an oop question

Julieta Shem jshem at yaxenu.org
Tue Nov 1 13:46:48 EDT 2022


Chris Angelico <rosuav at gmail.com> writes:

> On Mon, 31 Oct 2022 at 14:38, Julieta Shem <jshem at yaxenu.org> wrote:
>>
>> Chris Angelico <rosuav at gmail.com> writes:
>>
>> > The most straight-forward way to represent this concept in an
>> > object-oriented way is subclassing.
>> >
>> > class Stack:
>> >     ... # put whatever code is common here
>> >
>> > class Empty(Stack):
>> >     ... # put Empty-specific code here, possibly overriding Stack methods
>> >
>> > class Pair(Stack):
>> >    ... # ditto, overriding or augmenting as needed
>> >
>> > This way, everything is an instance of Stack, but they are still
>> > distinct types for when you need to distinguish.
>>
>> Can you provide a small example?  I can't see what you mean, but it
>> seems interesting.
>
> Sure. The easiest way would be to take your existing Empty and Pair
> classes, have them subclass Stack, and don't bother putting any code
> at all into Stack. Then construct an Empty and a few Pairs, and what
> you'll see is that all of them are also instances of Stack.
>
> After that, it's really a question of what you expect to be able to do
> with either an Empty or a Pair. Anything that should be possible with
> both types (that is, anything that should be possible with either
> variant of Stack) should get moved into the Stack type, while anything
> that is specific to one or the other stays in its own class. So here's
> a very very simple example:
>
> class Stack:
>     def prepend(self, other):
>         return Pair(other, self)
>
> class Empty(Stack):
>     def is_last(self):
>         return True
>     def get_current(self):
>         raise ValueError("Stack empty")
>     def get_next(self):
>         raise ValueError("Stack empty")
>
> class Pair(Stack):
>     def __init__(self, item1, item2):
>         self.item1 = item1
>         self.item2 = item2
>     def get_current(self):
>         return self.item1
>     def get_next(self):
>         return self.item2
>     def is_last(self):
>         return isinstance(self.item2, Empty)
>
> With this setup, you can build a stack by prepending items onto an
> Empty endpoint, and can iterate over it with the get_current and
> get_next methods. (Making this actually iterable, so that it works
> with a Python 'for' loop, would be a good exercise.)
>
> In this example, there isn't much code in the Stack class. But you
> could easily add more, and it would apply to both Empty and Pair.

Thank you so much for the example.  It's very interesting as it sort of
reverses my intuition.  It seems non-obvious.  I had in mind to build a
Stack out of Empty and Pair, but you seem to have done it the other way
around.

But I still don't see a way of using your classes up there in which I
have a single name to build empty and non-empty stacks.  Let me
clarify.  If I wish for an empty stack, I wish I could just say

>>> Stack()
Stack()

and if I wish for a nonempty stack, I'd write

>>> Stack(1, Stack(2, Stack(3, Stack())))
Stack(1, Stack(2, Stack(3, Stack())))

With your classes, I think I must write

>>> Empty() # That's an empty stack
...

>>> Pair(1, Pair(2, Empty())) # That's a non-empty stack
...

In other words, I need to memorize two different names for using stacks.
I'm trying to have a single name for both parts of the union (that is my
design of a stack).

Thanks so much!


More information about the Python-list mailing list