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


[Tutor] super constructor usage

2017-03-29 Thread Rafael Knuth
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__()

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.
Can anyone advise what the correct solution would be (in case mine is wrong).
Thank you.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor