Re: [Tutor] super constructor usage

2017-03-31 Thread Alan Gauld via Tutor
On 30/03/17 21:08, Alan Gauld via Tutor wrote:

> Of course, the __init__ methods are special in any way

Should have said *not special* in any way...

> But remember that not calling super potentially leaves
> some attributes of your superclass uninitialized. By not
> calling super you assume full responsibility for
> initializing both your sub class and the superclass.

And it's worth reminding ourselves that there could be several
superclasses not just the one we immediately inherit from.

Finally, this discussion has been in the context of
constructors but super() applies to all methods. There is
nothing unique about construction, it's just like any
other method call where you want to include the
superclass functionality.

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] super constructor usage

2017-03-31 Thread Mats Wichmann
On 03/30/2017 05:39 AM, Rafael Knuth wrote:
 I am trying to wrap my head around the super constructor.
> 
> Is it possible to embed a super constructor into an if / elif
> statement within the child class?
> 
> if message == "string A": return X
> elif: return Y
> 
> How should I modify my code below?
> (I couldn't solve that by myself)
> 
> class A:
> def __init__(self, message):
> self.message = message
> print(message)
> 
> class B(A):
> def __init__(self, message):
> print("This is the message from your parent class A:")
> super(B, self).__init__(message)
> 
> B("BlaBla")

For grins, try this (decorated with prints).  There's an additional
argument allowed to the class B initializer, but set as a default
argument so that if you omit it it defaults to True.

===
class A(object):
def __init__(self, msg):
print('A: Initializing instance of', self.__class__.__name__)
self.message = msg

class B(A):
def __init__(self, msg, doinit=True):
print('B: Initializing instance of', self.__class__.__name__)
if doinit:
super().__init__(msg)

print("Instantiating an A:")
a = A("some message")
print(a.message)

print("Instantiating a B:")
b = B("some message")
print(b.message)

print("Instantiating a B without calling superclass __init__:")
c = B("some message", False)
print(c.message)
===
When you run it:

Instantiating an A:
A: Initializing instance of A
some message
Instantiating a B:
B: Initializing instance of B
A: Initializing instance of B
some message
Instantiating a B without calling superclass __init__:
B: Initializing instance of B
Traceback (most recent call last):
  File "constr-tutor.py", line 22, in 
print(c.message)
AttributeError: 'B' object has no attribute 'message'
===

So note that: the instance attribute "message" is set in the class A
initializer, as in all your postings in this thread.  Just like Alan
pointed out, there are implications if you don't call up to the
superclass, and it shows up pretty clearly here: in the third case,
where we decide not to call the parent's  __init__, this initialization
doesn't happen and the attribute is missing, so accessing it blows up
with an AttributeError exception.


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] super constructor usage

2017-03-30 Thread Alan Gauld via Tutor
On 30/03/17 12:39, Rafael Knuth wrote:
 I am trying to wrap my head around the super constructor.
> 
> Is it possible to embed a super constructor into an if / elif
> statement within the child class?

Of course, the __init__ methods are special in any way
the normal coding mechanisms all work. If for some
reason you only want to call super some of the time
then by all means put it inside an if clause.

But remember that not calling super potentially leaves
some attributes of your superclass uninitialized. By not
calling super you assume full responsibility for
initializing both your sub class and the superclass.


-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] super constructor usage

2017-03-30 Thread Rafael Knuth
>> > I am trying to wrap my head around the super constructor.

Is it possible to embed a super constructor into an if / elif
statement within the child class?

if message == "string A": return X
elif: return Y

How should I modify my code below?
(I couldn't solve that by myself)

class A:
def __init__(self, message):
self.message = message
print(message)

class B(A):
def __init__(self, message):
print("This is the message from your parent class A:")
super(B, self).__init__(message)

B("BlaBla")
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] super constructor usage

2017-03-30 Thread Mats Wichmann
On 03/29/2017 08:33 AM, Rafael Knuth wrote:

> class A:
> def __init__(self, message):
> self.message = message
> print(message)
> 
> I then modified the child class B like this:
> 
> class B(A):
> def __init__(self, message):
> print("This is the message from your parent class A:")
> super(B, self).__init__(message)
> 
> B("BlaBla")
> 
> That works, however I am not sure about what exactly happens inside the code.
> What I am concerned about is whether the argument is being actually
> inherited from the parent class A or does B overwrite the argument.
> Can anyone advise what the correct solution would be (in case mine is wrong).
> Thank you.

Alan (as usual) already sorted this.

Just to try to fill in some of these questions - what's inherited,
overridden, etc., I'm pasting a bit of code I wrote for somewhere else
to demonstrate what's going on.  Hope it provides some enlightenment
(it's written for Python3, you'd have to change the super() call if
you're using Python2).

Note that your print(message) in A's initializer doesn't really tell you
much, it just tells you what argument was passed to the initializer.
That would be the same for any function/method, and tells you nothing
about inheritance.

class A(object):
print('Setting class variables in A')
aa_cls = 'class A data'

def __init__(self):
print('A: Initializing instance of', self.__class__.__name__)
self.aa = 'instance of class A'

def ameth(self):
return "A method from class A"

class B(A):
print('Setting class variables in B')
bb_cls = 'class B data'

def __init__(self):
print('B: Initializing instance of', self.__class__.__name__)
super().__init__()
self.bb = 'instance of class B'

print("Begin examination...")
print("Data from classes:")
print("A.aa_cls:", A.aa_cls)
print("B.aa_cls:", B.aa_cls)

print("Instantiating A as a:")
a = A()
print("Instantiating B as b:")
b = B()

print("Data from instance a:")
print("a.aa_cls:", a.aa_cls)
print("a.aa:", a.aa)
print("call ameth directly from a:", a.ameth())
print("Dict a:", a.__dict__)

print("Data from instance b:")
print("b.bb_cls:", b.bb_cls)
print("b.bb:", b.bb)
print("b.aa_cls:", b.aa_cls)
print("call ameth from b:", b.ameth())
print("b.aa:", b.aa)
print("Dict b:", b.__dict__)

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] super constructor usage

2017-03-30 Thread Mats Wichmann


On 03/29/2017 04:02 PM, Mats Wichmann wrote:
> On 03/29/2017 08:33 AM, Rafael Knuth wrote:
> 
>> class A:
>> def __init__(self, message):
>> self.message = message
>> print(message)
>>
>> I then modified the child class B like this:
>>
>> class B(A):
>> def __init__(self, message):
>> print("This is the message from your parent class A:")
>> super(B, self).__init__(message)
>>
>> B("BlaBla")
>>
>> That works, however I am not sure about what exactly happens inside the code.
>> What I am concerned about is whether the argument is being actually
>> inherited from the parent class A or does B overwrite the argument.
>> Can anyone advise what the correct solution would be (in case mine is wrong).
>> Thank you.
> 
> Alan (as usual) already sorted this.
> 
> Just to try to fill in some of these questions - what's inherited,
> overridden, etc., I'm pasting a bit of code I wrote for somewhere else
> to demonstrate what's going on. 

etc.

To make sure there's an even simpler answer than poring through all
those cases (which I think is useful), also try this minimal rewrite of
your example:

class A(object):
def __init__(self, message):
self.message = message + " (decorated by superclass)"

class B(A):
def __init__(self, message):
print("Class B initializer called with %s argument" % message)
super().__init__(message)

b = B("BlaBla")
print("instance message =", b.message)



___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] super constructor usage

2017-03-29 Thread Steven D'Aprano
On Wed, Mar 29, 2017 at 10:32:52PM +0100, Alan Gauld via Tutor wrote:
> On 29/03/17 15:33, Rafael Knuth wrote:
> > I am trying to wrap my head around the super constructor. 
> 
> This is one of these cases where it matters whether you
> are using Python v2 or v3. Use of super in v3 is much
> easier. It looks from your examples like you are using
> v2 but it would be good to confirm that.

It can't be Python 2, because A doesn't inherit from object. In that 
case, B's attempt to call super() would fail.

[...]
> > That works, however I am not sure about what exactly happens inside the 
> > code.
> 
> Yes, you have the mechanism right.
> As to what exactly happens inside the interpreter I'll leave
> that for those who care about such things :-)

The only real magic here is in super(). Everything else is just normal 
calling methods with arguments.

What super() does is return a special object, let's call it S. When you 
call S.__init__, S is smart enough to ignore it's own __init__ method, 
and instead search B's inheritance chain (called the MRO, or Method 
Resolution Order), returning the first __init__ it finds.

For B, the MRO is really short: first it looks at class A, then it looks 
at A's parent, namely `object`, which is the ultimate top of all 
inheritance chains in Python 3.

But in general, the MRO might be very long, it might contain branches, 
and the same class might be included multiple times. By using super(), 
Python will ensure that each superclass method is called at most *one* 
time, in the right order, no matter how many superclasses are involved.



-- 
Steve
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] super constructor usage

2017-03-29 Thread Steven D'Aprano
On Wed, Mar 29, 2017 at 04:33:20PM +0200, Rafael Knuth wrote:
> I am trying to wrap my head around the super constructor. Simple example:
> 
> class A:
> def __init__(self):
> print("world")
> 
> class B(A):
> def __init__(self):
> print("hello")
> super(B, self).__init__()

If you are using Python 2, this cannot work. You cannot use super() 
inside "classic classes" in Python 2, it will fail with TypeError. To 
make this work in Python 2, the class A needs to inherit from object:

class A(object): ...

So I think you are using Python 3. In Python 3 super() is much easier to 
use, as there is a special shortcut:

# like super(B, self).__init__()
super().__init__()


> B()
> 
> Then I changed the parent class A like this, as I wanted to test how
> the code would look like if I passed arguments:
> 
> class A:
> def __init__(self, message):
> self.message = message
> print(message)
> 
> I then modified the child class B like this:
> 
> class B(A):
> def __init__(self, message):
> print("This is the message from your parent class A:")
> super(B, self).__init__(message)
> 
> B("BlaBla")
> 
> That works, however I am not sure about what exactly happens inside the code.
> What I am concerned about is whether the argument is being actually
> inherited from the parent class A or does B overwrite the argument.

*Arguments* are not inherited. You are confusing two distinct, and 
different, parts of programming.

Inheritance is that if you call a method on a child class, say 
B.method(), but it doesn't exist, it will call the parent class instead, 
say A.method().

When you pass an argument to a method or function, you pass whatever you 
pass, and the method or function receives what you pass. In your 
example, you call B("BlaBla"), so B's __init__ method receives the same 
argument "BlaBla" as the argument "message". Inside B, it then passes on 
message unchanged:

B.__init__ receives message="BlaBla"
B.__init__ calls super().__init__(message)
A.__init__ receives message="BlaBla"


If you want to change the message, of course you can do so:

class C(A):
def __init__(self, message):
print("This is the message from your parent class A:")
super().__init__(message.upper() + "!!!")

C("BlaBla")

And now we have:

C.__init__ receives message="BlaBla"
C.__init__ calls super().__init__(modified message)
A.__init__ receives message="BLABLA!!!"


That is exactly the same process as if we had this instead:


def a_print(message):
print(message)

def b_print(message):
print("This is output from a_print:")
a_print(message)

def c_print(message):
print("This is output from a_print:")
a_print(message.upper() + "!!!")



> Can anyone advise what the correct solution would be (in case mine is wrong).
> Thank you.

You have the right idea. When you call your superclass method:

super().__init__()  # or any other method

you need to decide what the *purpose of your overridden method is*. If 
B.__init__() does exactly the same as A.__init__(), there is no point in 
writing B.__init__ at all! Just leave it out, and Python will 
automatically do the right thing.

So when you write B.__init__(), you have to decide what you want:

- ignore the superclass method, and just provide your own method:

class B(A):
def __init__(self, message):
self.message = message

(but if you do this for **everything**, then why are you inheriting from 
A? just write B as an independent class).


- call the superclass method, then do post-processing:

class B(A):
def __init__(self, message):
super().__init__(message)
self.message += "?"

- pre-process the argument, then call the superclass method:

class B(A):
def __init__(self, message):
if message == "hello?":
print("There's nobody here right now.")
message = "hello"
super().__init__(message)


- or any combination of the above.



-- 
Steve
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] super constructor usage

2017-03-29 Thread Alan Gauld via Tutor
On 29/03/17 15:33, Rafael Knuth wrote:
> I am trying to wrap my head around the super constructor. 

This is one of these cases where it matters whether you
are using Python v2 or v3. Use of super in v3 is much
easier. It looks from your examples like you are using
v2 but it would be good to confirm that.

> class A:
> def __init__(self):
> print("world")
> 
> class B(A):
> def __init__(self):
> print("hello")
> super(B, self).__init__()
> 
> B()
> 
> Then I changed the parent class A like this, as I wanted to test how
> the code would look like if I passed arguments:
> 
> class A:
> def __init__(self, message):
> self.message = message
> print(message)
> 
> I then modified the child class B like this:
> 
> class B(A):
> def __init__(self, message):
> print("This is the message from your parent class A:")
> super(B, self).__init__(message)
> 
> B("BlaBla")
> 
> That works, however I am not sure about what exactly happens inside the code.

Yes, you have the mechanism right.
As to what exactly happens inside the interpreter I'll leave
that for those who care about such things :-)

> What I am concerned about is whether the argument is being actually
> inherited from the parent class A or does B overwrite the argument.

The argument is just a parameter of the init() method like any
other argument. It is effectively a local variable. The self.message
attribute however is instantiated in A and inherited by B. (At least
conceptually. I'll let the interpreter gurus answer how it work in the
implementation)

Thus in B you could access self.message directly - and in a normal
method that might be the right thing to do. But in a
constructor/initializer you should leave the initializing
to super()


-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor