Re: A typing question

2022-10-30 Thread Peter J. Holzer
On 2022-10-29 20:14:12 -0400, Thomas Passin wrote:
> I don't understand
> 
> class Foos:
>  Foos: List[Foo]=[]
> 
> If "Foos" is supposed to be a class attribute, then it cannot have the same
> name as the class.

Why not? They are in different namespaces.

#v+
#!/usr/bin/python3

class Foos:
Foos = [1, 2, 3]

f = Foos()
print(f.Foos)
#v-

This works and I see no reason why it shouldn't work.

hp

-- 
   _  | Peter J. Holzer| Story must make more sense than reality.
|_|_) ||
| |   | [email protected] |-- Charles Stross, "Creative writing
__/   | http://www.hjp.at/ |   challenge!"


signature.asc
Description: PGP signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Peter J. Holzer
On 2022-10-29 23:59:44 +0100, Paulo da Silva wrote:
> Às 22:34 de 29/10/22, dn escreveu:
> > Solution (below) will not work if the mention of Foos in GLOBALS is a
> > forward-reference.
> > Either move GLOBALS to suit, or surround "Foos" with quotes.
 ^^
> This is the problem for me.

Quotes are a bit ugly, but why are they a problem?

[...]

> The funny thing is that if I replace foos by Foos it works because it gets
> known by the initial initialization :-) !
> 
> 
> from typing import List, Optional
> 
> class GLOBALS:
> Foos: Optional[Foos]=None
[...]
> class Foos:

That seems like a bug to me. What is the «Foos» in «Optional[Foos]»
referring to?

If it's the class attribute «Foos» then that's not a type and even if
its type is inferred that's not the same as «Optional[it's type]», or is
it?

If it's referring to the global symbol «Foos» (i.e. the class defined
later) that hasn't been defined yet, so it shouldn't work (or
alternatively, if forward references are allowed it should always work).

hp

-- 
   _  | Peter J. Holzer| Story must make more sense than reality.
|_|_) ||
| |   | [email protected] |-- Charles Stross, "Creative writing
__/   | http://www.hjp.at/ |   challenge!"


signature.asc
Description: PGP signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Thomas Passin

On 10/30/2022 6:26 AM, Peter J. Holzer wrote:

On 2022-10-29 23:59:44 +0100, Paulo da Silva wrote:

Às 22:34 de 29/10/22, dn escreveu:

Solution (below) will not work if the mention of Foos in GLOBALS is a
forward-reference.
Either move GLOBALS to suit, or surround "Foos" with quotes.

  ^^

This is the problem for me.


Quotes are a bit ugly, but why are they a problem?

[...]


The funny thing is that if I replace foos by Foos it works because it gets
known by the initial initialization :-) !


from typing import List, Optional

class GLOBALS:
 Foos: Optional[Foos]=None

[...]

class Foos:


That seems like a bug to me. What is the «Foos» in «Optional[Foos]»
referring to?

If it's the class attribute «Foos» then that's not a type and even if
its type is inferred that's not the same as «Optional[it's type]», or is
it?

If it's referring to the global symbol «Foos» (i.e. the class defined
later) that hasn't been defined yet, so it shouldn't work (or
alternatively, if forward references are allowed it should always work).


Quoting a forward-referenced type is the way to use one.  Unquoted types 
need to have been declared already.



--
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Peter J. Holzer
On 2022-10-30 09:23:27 -0400, Thomas Passin wrote:
> On 10/30/2022 6:26 AM, Peter J. Holzer wrote:
> > On 2022-10-29 23:59:44 +0100, Paulo da Silva wrote:
> > > The funny thing is that if I replace foos by Foos it works because it gets
> > > known by the initial initialization :-) !
> > > 
> > > 
> > > from typing import List, Optional
> > > 
> > > class GLOBALS:
> > >  Foos: Optional[Foos]=None
> > [...]
> > > class Foos:
> > 
> > That seems like a bug to me. What is the «Foos» in «Optional[Foos]»
> > referring to?
> > 
> > If it's the class attribute «Foos» then that's not a type and even if
> > its type is inferred that's not the same as «Optional[it's type]», or is
> > it?
> > 
> > If it's referring to the global symbol «Foos» (i.e. the class defined
> > later) that hasn't been defined yet, so it shouldn't work (or
> > alternatively, if forward references are allowed it should always work).
> 
> Quoting a forward-referenced type is the way to use one.  Unquoted types
> need to have been declared already.

Yes. I was referring to the code as written. Why does that work? I don't
think it should.

hp

-- 
   _  | Peter J. Holzer| Story must make more sense than reality.
|_|_) ||
| |   | [email protected] |-- Charles Stross, "Creative writing
__/   | http://www.hjp.at/ |   challenge!"


signature.asc
Description: PGP signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Peter Otten

On 30/10/2022 14:37, Peter J. Holzer wrote:

On 2022-10-30 09:23:27 -0400, Thomas Passin wrote:

On 10/30/2022 6:26 AM, Peter J. Holzer wrote:

On 2022-10-29 23:59:44 +0100, Paulo da Silva wrote:

The funny thing is that if I replace foos by Foos it works because it gets
known by the initial initialization :-) !


from typing import List, Optional

class GLOBALS:
  Foos: Optional[Foos]=None

[...]

class Foos:


That seems like a bug to me. What is the «Foos» in «Optional[Foos]»
referring to?

If it's the class attribute «Foos» then that's not a type and even if
its type is inferred that's not the same as «Optional[it's type]», or is
it?

If it's referring to the global symbol «Foos» (i.e. the class defined
later) that hasn't been defined yet, so it shouldn't work (or
alternatively, if forward references are allowed it should always work).


Quoting a forward-referenced type is the way to use one.  Unquoted types
need to have been declared already.


Yes. I was referring to the code as written. Why does that work? I don't
think it should.


For me it makes sense. I think mypy should refrain from trying to figure
out order of execution. If the above is disallowed, how about

if random.randrange(2):
class A: pass

class B(A): pass

?

One interesting consequence of that policy -- take the whole scope
instead of the most recent appearance of a name is that

class A: pass
class A: pass

won't compile.

While I didn't expect that I think I like it ;)

--
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Paulo da Silva

Às 02:32 de 30/10/22, dn escreveu:

On 30/10/2022 11.59, Paulo da Silva wrote:
Solution (below) will not work if the mention of Foos in GLOBALS is a 
forward-reference. Either move GLOBALS to suit, or surround "Foos" 
with quotes.
This is the problem for me. So far, without typing, I used to have 
some config and globals classes, mostly to just group definitions an 
make the program more readable. A matter of taste and style.


Agreed, a good practice.

Thank you.



Now, "typing" is breaking this, mostly because of this forward 
reference issue.


As a first step, use the quotation-marks to indicate that such will be 
defined later in the code:-



class GLOBALS:
    Foos: Optional[Foos]=None 


class GLOBALS:
     Foos: Optional["Foos"]=None


Later, as gather (typing) expertise, can become more sophisticated, 
as-and-when...



The funny thing is that if I replace foos by Foos it works because it 
gets known by the initial initialization :-) !


Is the objective to write (good) code, or merely to satisfy the 
type-checker?


Something that is misleading is not going to be appreciated by others 
(including the +6-months you), eg


a = a + 1   # decrement total

Typing is not compulsory, and has been designed so that we can implement 
it a bit at a time, eg only one function amongst many contained by a 
module - if that's the only code that requires maintenance/update.


Best not to create "technical debt" though!

The main idea is to eventually catch some, otherwise "hidden", errors 
and produce better and cleaner code. Documentation is also a must.


Regards


--
https://mail.python.org/mailman/listinfo/python-list


Re: A typing question

2022-10-30 Thread Paulo da Silva

Às 01:14 de 30/10/22, Thomas Passin escreveu:

On 10/29/2022 1:45 PM, Paulo da Silva wrote:

Hi!

Consider this simple script ...

___
from typing import List, Optional

class GLOBALS:
 foos=None

class Foo:

 def __init__(self):
 pass

class Foos:
 Foos: List[Foo]=[]
 # SOME GLOBALS ARE USED HERE in a real script

 def __init__(self):
 pass

GLOBALS.foos: Optional[Foos]=Foos()
___

Running mypy on it:
pt9.py:18: error: Type cannot be declared in assignment to non-self 
attribute
pt9.py:18: error: Incompatible types in assignment (expression has 
type "Foos", variable has type "None")

Line  18 is last line and pt9.py is the scrip.

Replacing last line by
GLOBALS.foos=Foos()
and running mypy still gives the second error.
pt9.py:18: error: Incompatible types in assignment (expression has 
type "Foos", variable has type "None")


What is the common practice in these cases?

Thank you.



I don't understand

class Foos:
  Foos: List[Foo]=[]

If "Foos" is supposed to be a class attribute, then it cannot have the 
same name as the class.

Yes it can.
You can refer it anywhere by Foos.Foos as a list of Foo elements.


--
https://mail.python.org/mailman/listinfo/python-list


an oop question

2022-10-30 Thread Julieta Shem
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 "", line 1, in 
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://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Paulo da Silva

Às 10:26 de 30/10/22, Peter J. Holzer escreveu:

On 2022-10-29 23:59:44 +0100, Paulo da Silva wrote:

Às 22:34 de 29/10/22, dn escreveu:

Solution (below) will not work if the mention of Foos in GLOBALS is a
forward-reference.
Either move GLOBALS to suit, or surround "Foos" with quotes.

  ^^

This is the problem for me.


Quotes are a bit ugly, but why are they a problem?

[...]


The funny thing is that if I replace foos by Foos it works because it gets
known by the initial initialization :-) !


from typing import List, Optional

class GLOBALS:
 Foos: Optional[Foos]=None

[...]

class Foos:


That seems like a bug to me. What is the «Foos» in «Optional[Foos]»
referring to?

If it's the class attribute «Foos» then that's not a type and even if
its type is inferred that's not the same as «Optional[it's type]», or is
it?

If it's referring to the global symbol «Foos» (i.e. the class defined
later) that hasn't been defined yet, so it shouldn't work (or
alternatively, if forward references are allowed it should always work).


The problem is exactly this.
Is there anything to do without loosing my script structure and usual 
practice? The forward reference only is needed to the "typing thing".
Even if I declare class "Foos: pass" before, then another error arises - 
something like "already declared" below.



--
https://mail.python.org/mailman/listinfo/python-list


Re: an oop question

2022-10-30 Thread Julieta Shem
[email protected] (Stefan Ram) writes:

> Julieta Shem  writes:
>>My desire seems to imply that I need a union-like data structure.
>
>   You only need to worry about such things in languages with
>   static typing. For example, to have a function that can
>   sometimes return an int value and at other times a string
>   value in C, one would need a union.
>
>   In Python with its dynamic typing, one does not need unions.
>
> def an_int_sometimes_and_sometimes_a_string( x ):
> if x:
> return 2
> else:
> return "two"

Nice.  This means that I can solve my stack-union problem by writing a
procedure --- say stack(...) --- that sometimes gives me Empty() and
sometimes gives me Stack().

>>> stack()
Empty()

>>> stack(1,2,3,4)
Stack(4, Stack(3, Stack(2, Stack(1, Empty()

The user interface of this non-empty case is that we're stacking up the
arguments, hence the number 1 ends up at the bottom of the stack.

def stack(*args):
  if len(args) == 0:
return Empty()
  else:
return Stack(args[-1], stack(*args[:-1]))

I realize now that I'm using the same solution of the /open()/
procedure.  Depending on the arguments, open() produces a different type
of object.

>   . If you should, however, be talking about the new "type hints":
>   These are static and have "Union", for example, "Union[int, str]"
>   or "int | str".

I ended up locating such features of the language in the documentation,
but I actually am not interested in declaring the type to the compiler
(or to the reader).  

I was looking for a solution like yours --- thank you! ---, although I
was hoping for handling that situation in the construction of the Stack
object, which was probably why I did not find a way out.  Right now I'm
looking into __new__() to see if it can somehow produce one type or
another type of object depending on how the user has invoked the class
object.

Terminology.  By ``invoking the class object'' I mean expressions such
as Class1() or Class2().  ``Class1'' represents the object that
represents the class 1.  Since the syntax is that of procedure
invokation, I say ``invoking the class object''.
-- 
https://mail.python.org/mailman/listinfo/python-list


Weired behaviour - The slice of empty string not return an error

2022-10-30 Thread Yassine Nasri
Hello,

len('')  # => 0''[0]# => error''[::]   # => [::]   # <=> ''[0:len(''):1]

the syntax of slice:

slice(start, end, step)

The start in ''[::] equivalent at index 0 of the ''

Since the index 0 of '' returns an error.

The ''[::] should not return an error logically.


Best regards
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Paulo da Silva

Às 22:34 de 29/10/22, dn escreveu:
Out of interest, tested snippet in PyCharm, cf native-mypy. It flags the 
original:


     GLOBALS.foos: Optional[Foos]=Foos()

but not the fall-back:

     GLOBALS.foos=Foos()


Must admit, the first query coming to mind was: why is the typing taking 
place at initialisation-time, rather than within the (class) definition? 
At definition time "foos" has already been typed as None by implication!



Solution (below) will not work if the mention of Foos in GLOBALS is a 
forward-reference. Either move GLOBALS to suit, or surround "Foos" with 
quotes.

Somehow I missed this sentence the 1st. time I read this post :-(
This is good enough to me! Thank you.
I didn't know about this "quoting" thing.

Regards
Paulo


--
https://mail.python.org/mailman/listinfo/python-list


Re: Weired behaviour - The slice of empty string not return an error

2022-10-30 Thread Yassine Nasri
PS: The ''[::] should  return an error logically.

Le dim. 30 oct. 2022 à 15:57, Yassine Nasri  a
écrit :

> Hello,
>
> len('')  # => 0''[0]# => error''[::]   # => [::]   # <=> 
> ''[0:len(''):1]
>
> the syntax of slice:
>
> slice(start, end, step)
>
> The start in ''[::] equivalent at index 0 of the ''
>
> Since the index 0 of '' returns an error.
>
> The ''[::] should not return an error logically.
>
>
> Best regards
>
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: an oop question

2022-10-30 Thread Julieta Shem
Julieta Shem  writes:

[...]

>>   . If you should, however, be talking about the new "type hints":
>>   These are static and have "Union", for example, "Union[int, str]"
>>   or "int | str".
>
> I ended up locating such features of the language in the documentation,
> but I actually am not interested in declaring the type to the compiler
> (or to the reader).  
>
> I was looking for a solution like yours --- thank you! ---, although I
> was hoping for handling that situation in the construction of the Stack
> object, which was probably why I did not find a way out.  Right now I'm
> looking into __new__() to see if it can somehow produce one type or
> another type of object depending on how the user has invoked the class
> object.
>
> Terminology.  By ``invoking the class object'' I mean expressions such
> as Class1() or Class2().  ``Class1'' represents the object that
> represents the class 1.  Since the syntax is that of procedure
> invokation, I say ``invoking the class object''.

An experiment.  What's my definition of Stack?  It's either Empty or
Pair, so it's a union.  So let us create two inner classes (that is,
inner to Stack) and make them behave just like Empty and Pair.  Using
__new__(), we can produce a Stack object that is sometimes Empty() and
sometimes Pair(...).

class Stack:
  class StackEmpty(Empty):
pass
  class StackPair(Pair):
pass
  def __new__(clss, *args):
if len(args) == 0:
  return Stack.StackEmpty()
else:
  return Stack.StackPair(*args)

This does it.  However, we are exposing the different types to the user.

>>> Stack()
Empty()

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

Since we have our own copies of Empty and Pair inside Stack, we change
their representation.

class Stack:
  class StackEmpty(Empty):
def __str__(self):
  return "Stack()"
def __repr__(self):
  return str(self)
  class StackPair(Pair):
def __str__(self):
  return "Stack({!r}, {})".format(self.first, str(self.rest))
def __repr__(self):
  return str(self)
  [...]

>>> Stack()
Stack()

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

That's it.  That's what I was looking for.  However, I don't really like
the OOP approach here because of what's going to happen next.

Now we are free to move on with implementing push and pop, say.  But
since Stack is really a union, we need to define push and pop on each of
these inner types that make up Stack.  Let's do it and see what we get.

  class StackEmpty(Empty):
[...]
def pop(self):
  raise ValueError("cannot pop from an empty stack")
def push(self, x):
  return Stack(x, self)

  class StackPair(Pair):
def pop(self):
  return self.first
def push(self, x):
  return Stack(x, self)

The job is done.

>>> Stack().push("it")
Stack('it', Stack())

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

We may observe that we had to write the same exact procedure /push/ in
both types.  Sounds funny to say that it seems that we are too modular
here.  I'm pretty sure you'll find a way to grab the procedure of one
class and apply it on the other, so I really think there's a way out of
not wasting keyboard-typing work and keeping things with a ``single
point of control''.

(*) Complete code

class Stack:
  class StackEmpty(Empty):
def __str__(self):
  return "Stack()"
def __repr__(self):
  return str(self)
def pop(self):
  raise ValueError("cannot pop from an empty stack")
def push(self, x):
  return Stack(x, self)
  class StackPair(Pair):
def __str__(self):
  return "Stack({!r}, {})".format(self.first, str(self.rest))
def __repr__(self):
  return str(self)
def pop(self):
  return self.first
def push(self, x):
  return Stack(x, self)
  def __new__(clss, *args):
if len(args) == 0:
  return Stack.StackEmpty()
else:
  return Stack.StackPair(*args)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Paulo da Silva

Às 17:06 de 30/10/22, Stefan Ram escreveu:

Paulo da Silva  writes:

Is there anything to do without loosing my script structure and usual
practice?


   to lose (losing): to stop having something
   to loose (loosing): to let or make loose (see next line)
   loose (adj.): not firmly attached/tied/fastened/controlled
   to loosen: similar to "to loose"


It was a keyboard bounce ;-)
How about answering the question?
Thank you.



--
https://mail.python.org/mailman/listinfo/python-list


Re: an oop question

2022-10-30 Thread Giorgio Pastore

Il 30/10/22 15:54, Julieta Shem ha scritto:

[email protected] (Stefan Ram) writes:


Julieta Shem  writes:

My desire seems to imply that I need a union-like data structure.


   You only need to worry about such things in languages with
   static typing. For example, to have a function that can
   sometimes return an int value and at other times a string
   value in C, one would need a union.

   In Python with its dynamic typing, one does not need unions.

def an_int_sometimes_and_sometimes_a_string( x ):
 if x:
 return 2
 else:
 return "two"


Nice.  This means that I can solve my stack-union problem by writing a
procedure --- say stack(...) --- that sometimes gives me Empty() and
sometimes gives me Stack().

I think that Stefan Ram's suggestion provides a solution to the problem 
of the stack, but not in a real OOP flavor.


You may find useful to learn about the possibilities of using Python 
tools to implement a stack data structure and its manipulation methods. 
A good introduction is at

https://realpython.com/how-to-implement-python-stack/

Giorgio

--
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread dn

On 30/10/2022 17.48, Paulo da Silva wrote:

Às 02:32 de 30/10/22, dn escreveu:

On 30/10/2022 11.59, Paulo da Silva wrote:
Solution (below) will not work if the mention of Foos in GLOBALS is 
a forward-reference. Either move GLOBALS to suit, or surround "Foos" 
with quotes.
This is the problem for me. So far, without typing, I used to have 
some config and globals classes, mostly to just group definitions an 
make the program more readable. A matter of taste and style.


Agreed, a good practice.

Thank you.



Now, "typing" is breaking this, mostly because of this forward 
reference issue.


As a first step, use the quotation-marks to indicate that such will be 
defined later in the code:-



class GLOBALS:
    Foos: Optional[Foos]=None 


class GLOBALS:
 Foos: Optional["Foos"]=None


Later, as gather (typing) expertise, can become more sophisticated, 
as-and-when...



The funny thing is that if I replace foos by Foos it works because it 
gets known by the initial initialization :-) !


Is the objective to write (good) code, or merely to satisfy the 
type-checker?


Something that is misleading is not going to be appreciated by others 
(including the +6-months you), eg


a = a + 1   # decrement total

Typing is not compulsory, and has been designed so that we can 
implement it a bit at a time, eg only one function amongst many 
contained by a module - if that's the only code that requires 
maintenance/update.


Best not to create "technical debt" though!

The main idea is to eventually catch some, otherwise "hidden", errors 
and produce better and cleaner code. Documentation is also a must.



Good idea!

All-along typing has been regarded as a tool for dev.aids, eg IDEs like 
PyCharm and tools such as mypy - rather than a (compulsory) component of 
the Python language itself. This means the folk who decide they don't 
like the idea can happily (and safely) ignore it - and continue to write 
code in perfect-Python. Also, that 'old program[me]s' will continue to 
operate, just as well as they ever did, without any reference to the 
typing library/ies at all. So, there is no imposed-cost, or 'pressure' 
to (wait to) upgrade (such as there was with the jump from Python 2 to 
Python 3).



IMHO I'm finding that the auto-checking performed by the IDE is very 
helpful and stops my imagination from outstripping my coding-abilities 
(in the way that my eyes for chocolate cake are bigger than my 
stomach!). I'll go so far as to say that the discipline imposed/flagged 
by typing has been more beneficial, than my flirtation with an 'AI 
assistant' suggesting what I should code next! The assistant rarely 
seems to correctly anticipate my thinking*, whereas typing pulls me up 
when the code falls-short and prevents me from falling flat on my face!


* which may say something unfortunate about my coding/design, or may 
simply show that SODD (Stack Overflow Driven Development - or the GitHub 
equivalent) leaves much to be desired. Hence the gold-plated advice: 
never copy-paste code without understanding it first!



As time goes by, 'The Python Gods' have been introducing more and more 
capability under the typing banner. Which makes the policy as-outlined, 
the sensible (and parallel) course. S/w architecture and coding-quality 
books talk about "The Boy Scout Rule" (leave the camp-site in better 
condition than you found it). Accordingly, when performing 
user-specified updates, while we are 'there', we have the option/ability 
to add typing to a module/class/function - just as we might perform 
other "refactoring" tasks (one 'green-thumb' I know, referred to it as 
'gardening').



Evidently, the OP introduced typing into his/her code-base with the 
likes of List*. Thereafter, added the Optional, er, option. Now, we're 
talking about forward-references. Plus alluding to more recent 
developments (many of which are version-dependent!).


Accordingly, we see another attribute of a gradual-introduction policy - 
the practitioner learning and becoming confident that (s)he has mastered 
basic techniques, before striding onwards to 'the next level'!



* and for the above reason, I wondered, belatedly, if earlier advice to 
'graduate' to "list", might be premature.



PS and on a personal note, this policy is the one I'm following. So, 
given that we-two are on the same track, we must be "correct" - and also 
the best Python programmers in the world!

(pardon me, I seem over-taken by a coughing fit...)

YMMV!
--
Regards =dn
--
https://mail.python.org/mailman/listinfo/python-list


Re: an oop question

2022-10-30 Thread Chris Angelico
On Mon, 31 Oct 2022 at 09:05, Julieta Shem  wrote:
>
> Julieta Shem  writes:
>
> [...]
>
> >>   . If you should, however, be talking about the new "type hints":
> >>   These are static and have "Union", for example, "Union[int, str]"
> >>   or "int | str".
> >
> > I ended up locating such features of the language in the documentation,
> > but I actually am not interested in declaring the type to the compiler
> > (or to the reader).
> >
> > I was looking for a solution like yours --- thank you! ---, although I
> > was hoping for handling that situation in the construction of the Stack
> > object, which was probably why I did not find a way out.  Right now I'm
> > looking into __new__() to see if it can somehow produce one type or
> > another type of object depending on how the user has invoked the class
> > object.
> >
> > Terminology.  By ``invoking the class object'' I mean expressions such
> > as Class1() or Class2().  ``Class1'' represents the object that
> > represents the class 1.  Since the syntax is that of procedure
> > invokation, I say ``invoking the class object''.
>
> An experiment.  What's my definition of Stack?  It's either Empty or
> Pair, so it's a union.  So let us create two inner classes (that is,
> inner to Stack) and make them behave just like Empty and Pair.  Using
> __new__(), we can produce a Stack object that is sometimes Empty() and
> sometimes Pair(...).
>

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.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread dn

On 31/10/2022 06.06, Stefan Ram wrote:

Paulo da Silva  writes:

Is there anything to do without loosing my script structure and usual
practice?


   to lose (losing): to stop having something
   to loose (loosing): to let or make loose (see next line)
   loose (adj.): not firmly attached/tied/fastened/controlled
   to loosen: similar to "to loose"



Hay, your write*!

Well done. There's many a native-speaker who doesn't know the 
distinction, or doesn't care about accuracy.


It's a pity that there's no decent typing module for the English language!



* yes, each word sounds about-right, but is totally wrong...
--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread Chris Angelico
On Mon, 31 Oct 2022 at 09:39, dn  wrote:
>
> On 31/10/2022 06.06, Stefan Ram wrote:
> > Paulo da Silva  writes:
> >> Is there anything to do without loosing my script structure and usual
> >> practice?
> >
> >to lose (losing): to stop having something
> >to loose (loosing): to let or make loose (see next line)
> >loose (adj.): not firmly attached/tied/fastened/controlled
> >to loosen: similar to "to loose"
>
>
> Hay, your write*!
>
> Well done. There's many a native-speaker who doesn't know the
> distinction, or doesn't care about accuracy.
>

I'm curious to what extent sloppy English correlates with sloppy code.
Do people care about learning proper Python but not about proper
English, or do they think there's no such thing as proper English just
because there's no English Steering Council?

A lot of people seem to treat English the way web browsers treat HTML
- as long as you can make some sense out of it, it's good enough. Some
nerds treat English the way the W3C treats HTML - there actually is a
standard and everything has defined rules. I know which camp I prefer
to communicate with.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: an oop question

2022-10-30 Thread Weatherby,Gerard
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  on 
behalf of Julieta Shem 
Date: Sunday, October 30, 2022 at 5:51 PM
To: [email protected] 
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 "", line 1, in 
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://mail.python.org/mailman/listinfo/python-list


Re: Fwd: A typing question

2022-10-30 Thread dn

On 31/10/2022 11.44, Chris Angelico wrote:

On Mon, 31 Oct 2022 at 09:39, dn  wrote:


On 31/10/2022 06.06, Stefan Ram wrote:

Paulo da Silva  writes:

Is there anything to do without loosing my script structure and usual
practice?


to lose (losing): to stop having something
to loose (loosing): to let or make loose (see next line)
loose (adj.): not firmly attached/tied/fastened/controlled
to loosen: similar to "to loose"



Hay, your write*!

Well done. There's many a native-speaker who doesn't know the
distinction, or doesn't care about accuracy.



I'm curious to what extent sloppy English correlates with sloppy code.
Do people care about learning proper Python but not about proper
English, or do they think there's no such thing as proper English just
because there's no English Steering Council?

A lot of people seem to treat English the way web browsers treat HTML
- as long as you can make some sense out of it, it's good enough. Some
nerds treat English the way the W3C treats HTML - there actually is a
standard and everything has defined rules. I know which camp I prefer
to communicate with.



There are two distinctions to make: what is "correct", and how correctly 
we practice!


Various languages do have a 'standards body', eg French and the group 
known as "l'Académie (française)". However, no matter how much they try 
to push-back against the encroachment of words from other languages (eg 
"le weekend"), even they admit that it is a losing battle (see also: 
King Canute).


English is, and has long-been, an "acquisitive language". It takes words 
from other languages, often as attempted-homophones, and makes them 
"English": verandah, foreign, algebra, ... (and seeing I mentioned 
French: "maitre d'" because it makes the restaurant sound 'posh'; 
champagne because you can then charge more for the bubbles, and 
omelet/omelette because I'm feeling hungry...). Indeed yous-Australians 
are also manipulators of the language, eg "tradies" for 
tradesmen/trades-people (see also: "They're a Weird Mob" - in case you 
haven't read that hilarious piece of Aussie Kulcha).


There have been various attempts at standardising English. Indeed for 
our Internet-based training courses and courseware, I went looking for 
an 'international English'. There is no such thing. The closest is more 
to do with accent than text, ie speaking the Queen's/King's English, is 
really a euphemism for a (plummy) Oxford accent (or BBC accent - also 
fading into the past). A good starting-point: 
https://www.cambridge.org/highereducation/books/the-cambridge-encyclopedia-of-the-english-language/2B10AC8766B73D09955C899572C1E7EC#overview 
- a tome compiled by a man living in Wales (go figure!).


The earliest attempts would include Samuel Johnson's Dictionary of the 
English Language (which preceded 'your' Lewis Carroll's playing with 
language by about half a century). Around-about one-century after that 
came the OED (Oxford English Dictionary), which is the closest thing to 
an idea of 'standard spelling'. (and at times differs significantly from 
alternate tomes describing American-English).


As for grammar, I must admit deferring (and devolving?) such to style 
manuals, such as The Times of London, Strunk and White, etc. (but 
perhaps only later and lesser: Kernighan and Plauger's similarly named 
"Elements of Programming Style"). In this case, my multi-cultural 
education (or lack of application to my school-work?) has caused me many 
a stumble. Certainly, I've never taught the subject. Perhaps someone is 
better-acquainted with works used by The British Council, et al, in 
their English as a Second Language (and related) courses?



The other side of this topic, that of accuracy and precision, involves 
aspects which were driven into us at school - that is, those of us with 
Optional[grey] hair! These days, teaching good-grammar, spelling, and 
so-on, are deemed unnecessarily restrictive, perhaps even cruel, but 
certainly far too boring (see also: "joined-up" hand-writing). "Drill" 
(aka "deliberate practice") is rarely employed as a teaching/learning 
technique in schools. Cue "amo, amas, amat, amamus, amatis, amant" 
(Latin - although quite why, as six-year olds, the teachers ("Masters") 
thought we should be speaking of love escapes me) and "Je suis, tu es, 
il est, ..." (French - which always reminds me of Rodin's sculpture and 
Descartes exclamation)*


I've even (as an adult) had a (school) Department Head reply to such 
criticism with "but you understood me, didn't you?". So, with teachers 
like that, the kids are 'on a hiding to nothing'. The prevailing-theory 
seems to be that 'you'll pick it up as you go-along'. However, empirical 
evidence suggests otherwise. I've even met voracious readers who admit 
that their spelling is at the lousy-end of any metric, busting that myth 
well-and-truly!



Traditionally 'computer people' have been assumed to need a good 
grounding in math[s]. Although, I haven't 

Re: an oop question

2022-10-30 Thread dn

On 31/10/2022 09.22, Stefan Ram wrote:

Giorgio Pastore  writes:

You may find useful to learn about the possibilities of using Python
tools to implement a stack data structure and its manipulation methods.
A good introduction is at
https://realpython.com/how-to-implement-python-stack/


   I can't see how lists are not stacks.


Agreed - in terms of functionality(!)


|>>> s=[]
|>>> s.append(1)
|>>> s.append(2)
|>>> s.pop()
|2
|>>> s.pop()
|1
|>>> s.pop()
|Traceback (most recent call last):
|  File "", line 1, in 
|IndexError: pop from empty list

   So, for practical purposes, there is no need to implement a
   stack class as far as I can understand it.

   Maybe the OP wanted to implement a stack in a special way,
   so that this stack is immutable and its implementation is
   based on something like dotted pairs.



Code is for humans to read too. Accordingly, instead of "s", preference 
for "stack" - per OP terminology.


The OP has coded pop() and push() methods, which are familiar/taught as 
'the' stack operations in every 'intro to algorithms' (or intermediate 
'data-structures') class.


Sadly, whereas there is list.pop(), the equivalent "push" method is 
append(). Thus, a (small) dissonance in one's mind.


Writing a Stack class is unnecessary - at least given list, it is. 
However, having called the structure "stack" to be descriptive and 
helpful, it seems logical to continue by referring to the methods as 
"push" and "pop".
(which is why many of us have such 'utilities'/snippets sitting-ready in 
a personal library)


The OP has already coded such, so no cost to keep.


(returning to the OP)
Like @Gerard, am confused. There are two types: Pair and Empty. 
Thereafter the need is to keep them on a stack. The stack is a 
collection, an aggregate of 'whatever'. Why inherit from/to Stack?

(is this a 'Lisp thing'?)

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list


Re: Weired behaviour - The slice of empty string not return an error

2022-10-30 Thread MRAB

On 2022-10-30 14:57, Yassine Nasri wrote:

Hello,

len('')  # => 0''[0]# => error''[::]   # => [::]   # <=> ''[0:len(''):1]

the syntax of slice:

slice(start, end, step)

The start in ''[::] equivalent at index 0 of the ''

Since the index 0 of '' returns an error.

The ''[::] should not return an error logically.


Python uses half-open ranges. It excludes the end.

''[0:0:1] starts at index 0 but ends at and excludes index 0.
--
https://mail.python.org/mailman/listinfo/python-list


Re: an oop question

2022-10-30 Thread Julieta Shem
Chris Angelico  writes:

> On Mon, 31 Oct 2022 at 09:05, Julieta Shem  wrote:
>>
>> Julieta Shem  writes:
>>
>> [...]
>>
>> >>   . If you should, however, be talking about the new "type hints":
>> >>   These are static and have "Union", for example, "Union[int, str]"
>> >>   or "int | str".
>> >
>> > I ended up locating such features of the language in the documentation,
>> > but I actually am not interested in declaring the type to the compiler
>> > (or to the reader).
>> >
>> > I was looking for a solution like yours --- thank you! ---, although I
>> > was hoping for handling that situation in the construction of the Stack
>> > object, which was probably why I did not find a way out.  Right now I'm
>> > looking into __new__() to see if it can somehow produce one type or
>> > another type of object depending on how the user has invoked the class
>> > object.
>> >
>> > Terminology.  By ``invoking the class object'' I mean expressions such
>> > as Class1() or Class2().  ``Class1'' represents the object that
>> > represents the class 1.  Since the syntax is that of procedure
>> > invokation, I say ``invoking the class object''.
>>
>> An experiment.  What's my definition of Stack?  It's either Empty or
>> Pair, so it's a union.  So let us create two inner classes (that is,
>> inner to Stack) and make them behave just like Empty and Pair.  Using
>> __new__(), we can produce a Stack object that is sometimes Empty() and
>> sometimes Pair(...).
>>
>
> 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.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Weired behaviour - The slice of empty string not return an error

2022-10-30 Thread dn

On 31/10/2022 03.59, Yassine Nasri wrote:

PS: The ''[::] should  return an error logically.

Le dim. 30 oct. 2022 à 15:57, Yassine Nasri  a
écrit :


Hello,

len('')  # => 0''[0]# => error''[::]   # => [::]   # <=> ''[0:len(''):1]

the syntax of slice:

slice(start, end, step)

The start in ''[::] equivalent at index 0 of the ''

Since the index 0 of '' returns an error.

The ''[::] should not return an error logically.



Perhaps it will help to say:

- "indexing" one "element" from a "sequence" will yield a single result 
of the type of that element


- "slicing" a "sequence" will yield a result of the same type as the 
original sequence


The corollary to the first is expected: that if there is no element at 
the stated index/position, something is wrong.


However, what is not necessarily apparent, is that because the result is 
of the same type as the original sequence, and an empty-sequence is 
perfectly legal; so is it possible to slice something and receive back 
'nothing'.



The second 'saying' is to use the correct jargon:

- in a[ i ] i is an "index"

- in a[ i:j ] i and j are "parameters"

A parameter does not (also) need to be a legal index!


This is proven (as you have discovered (but is difficult to read, above):

>>> sequence = []
>>> len( sequence )
0
>>> sequence[ 0 ]
Traceback (most recent call last):
  File "", line 1, in 
IndexError: list index out of range
>>> sequence[ 0:1 ]
[]


The question (and assertion) are discussed in the manual:
«Sequences

These represent finite ordered sets indexed by non-negative 
numbers. The built-in function len() returns the number of items of a 
sequence. When the length of a sequence is n, the index set contains the 
numbers 0, 1, …, n-1. Item i of sequence a is selected by a[i].


Sequences also support slicing: a[i:j] selects all items with index 
k such that i <= k < j. When used as an expression, a slice is a 
sequence of the same type. This implies that the index set is renumbered 
so that it starts at 0.


Some sequences also support “extended slicing” with a third “step” 
parameter: a[i:j:k] selects all items of a with index x where x = i + 
n*k, n >= 0 and i <= x < j.

»

Thus, an index must point to an existing element, whereas the parameters 
of a slice need not. If the slice-specification calls for elements 
'beyond' the end of the sequence, then the result will only contain 
items up-to the last element, ie the length of the resultant-sequence 
will be shorter than j - i, where j > len( sequence ).



# Proof (which concurs with the OP's observations)

# playing with a tuple's indexes/indices

>>> sequence = 1,2,3
>>> sequence[ 1 ]
2

# notice the result's type!

>>> sequence[ 9 ]
Traceback (most recent call last):
  File "", line 1, in 
IndexError: tuple index out of range

# the above index is plainly 'beyond' len( sequence )
# do you know about negative indexes/indices?

>>> sequence[ -1 ]
3
>>> sequence[ -9 ]
Traceback (most recent call last):
  File "", line 1, in 
IndexError: tuple index out of range

# again this index is 'beyond' the first element of the sequence


# let's play with some slicing;

>>> sequence[ 0:3 ]
(1, 2, 3)
>>> [ 1, 2, 3 ][ 0:3 ]
[1, 2, 3]

# same applies to lists (and strings, etc)
# notice the types!

>>> sequence[ 1:1 ]
()
>>> sequence[ 1:2 ]
(2,)
>>> sequence[ -1:2 ]
()
>>> sequence[ -1:-2 ]
()
>>> sequence[ -1:-2:-1 ]
(3,)
>>> sequence[ -1:-3:-1 ]
(3, 2)

# OK, what happens when parameters point to non-existent indexes?

>>> sequence[ 1:9 ]
(2, 3)
>>> sequence[ 1:-9 ]
()
>>> sequence[ -1:-9:-1 ]
(3, 2, 1)
>>> sequence[ -9:-1:-1 ]
()

# yes, they are parameters and not strictly indexes/indices!


https://docs.python.org/3/reference/datamodel.html?highlight=slicing#the-standard-type-hierarchy
for "sequences" and "slice objects"

Note that a Python set is "collection" but not a "sequence". Which means 
that neither indexing nor slicing (per above) will work on a set. We can 
ask if a particular value appears in a set ("contains") but cannot ask 
for the n-th element. To be able to index or slice it, a sequence must 
offer a __getitem__() method - tuple, list, and string do; but set does 
not. (check for yourself: help( set ) )


https://docs.python.org/3/library/collections.abc.html?highlight=collection

--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list


Re: an oop question

2022-10-30 Thread Chris Angelico
On Mon, 31 Oct 2022 at 14:38, Julieta Shem  wrote:
>
> Chris Angelico  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.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list