an oop question

Weatherby,Gerard gweatherby at uchc.edu
Sun Oct 30 20:03:27 EDT 2022


I don’t understand your implementation enough to comment specifically. What’s the definition of Pair? (i.e. what methods and public attributes does it have?) (I manage to escape Lisp as an undergrad)

To answer your question generally, Union’s are not OO. If you want Pair and Stack to have the same interface create an abstract base class and derive both of them from it.

https://docs.python.org/3/library/abc.html


From: Python-list <python-list-bounces+gweatherby=uchc.edu at python.org> on behalf of Julieta Shem <jshem at yaxenu.org>
Date: Sunday, October 30, 2022 at 5:51 PM
To: python-list at python.org <python-list at python.org>
Subject: an oop question
*** Attention: This is an external email. Use caution responding, opening attachments or clicking on links. ***

I have a question about a particular case I'm working on.  I'm studying
OOP.  To ask the question, I'm going to have to introduce you my context
here, so you'll need to bear with me.  If you'd like to see the question
right away, go to the section ``My difficulty in encapsulating a
union''.

(*) Introduction

I wrote the classes

  class Empty:
    ...
  class Pair:
    ...

With them, I can (sort of) make a union of them and build a Lisp-like
list by defining that my Lisp-like list is either Empty or a Pair.  For
instance, here's a Lisp-like sequence that represents the string "abc".

>>> Pair.fromIterable("abc")
Pair('a', Pair('b', Pair('c', Empty())))

So far so good.  (``Full'' code at the end of this message, if you'd
like to more carefully look into my reasoning there.)

(*) How to build a stack?

These Lisp-like sequences are clearly a stack.  You pop an element from
it by grabbing the first element.  You push an element on to it by just
pairing the new element with the current stack.  For instance, let's pop
the 1-string "a" off of the stack.

>>> ls = Pair.fromIterable("abc")
>>> ls.first
'a'
>>> ls = ls.rest
>>> ls
Pair('b', Pair('c', Empty()))

Done.  Let's push it back on.

>>> ls = Pair("a", ls)
>>> ls
Pair('a', Pair('b', Pair('c', Empty())))

So far so good, but when it comes to building a better user interface
for it I have no idea how to do it.  I think one of the purposes of OOP
is to organize code hierarchically so that we can reuse what we wrote.
So I tried to make a Stack by inheriting Pair.

class Stack(Pair):
    pass

>>> Stack(1, Empty())
Stack(1, Empty())

Then I wrote pop and push.

>>> Stack(1, Empty()).pop()
1

>>> Stack(1, Empty()).push(2)
Stack(2, Stack(1, Empty()))

So far so good.  Now let me show you what I can't do.

(*) The difficulty of encapsulating a union

The Lisp-like sequences we're building here are union-like data
structures.  A /sequence/ is either Empty() or Pair(..., /sequence/).  I
have not found a way to represent this either-or datastructure with a
class.  For example, there is no way right now to build an empty Stack
by invoking the Stack constructor.

>>> Stack()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Pair.__init__() missing 2 required positional arguments: 'first' and 'rest'

As I designed, an empty Stack is represented by Empty(), which is a
whole other object.  Users will have to know this.  I wish I did not
have to burden my users with such knowledge.  For instance, I wish the
answer was "Stack()" to the question of -- ``how can I build an empty
stack?''

My desire seems to imply that I need a union-like data structure.  If
Stack is invoked with no arguments, it should produce Empty(), otherwise
it produces Pair() as it does today.

How can I achieve that?

(*) Code

class Pair:
  def __init__(self, first, rest):
    if not isinstance(rest, Pair) and not isinstance(rest, Empty):
      raise ValueError("rest must be Empty or Pair")
    self.first = first
    self.rest = rest
  def fromIterable(it):
    if len(it) == 0:
      return Empty()
    else:
      return Pair(it[0], Pair.fromIterable(it[1:]))
  def __str__(self):
    return "{}({!r}, {})".format(self.__class__.__name__, self.first, str(self.rest))
  def __repr__(self):
    return str(self)
  def __len__(self):
    return 1 + self.rest.__len__()

class Empty:
  def __len__(self):
    return 0
  def __str__(self):
    return  "Empty()"
  def __repr__(self):
    return self.__str__()
  def __new__(clss):
    if not hasattr(clss, "saved"):
      clss.saved = super().__new__(clss)
    return clss.saved

class Stack(Pair):
  def pop(self):
    return self.first
  def push(self, x):
    return Stack(x, self)
--
https://urldefense.com/v3/__https://mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!iK-T12707FwUmb1Qqxlxawj0i9z1tR9aCPWPoHzh5veCzlaFBY9pKfFZwGqg3sDjUqLPC2IZgGw4QI3sRg$<https://urldefense.com/v3/__https:/mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!iK-T12707FwUmb1Qqxlxawj0i9z1tR9aCPWPoHzh5veCzlaFBY9pKfFZwGqg3sDjUqLPC2IZgGw4QI3sRg$>


More information about the Python-list mailing list