Reputation: 2668
Codes below defined Duck class as composited from Bill class and Tail class. My questions is that as for the method about() inside Duck class definition, why can one write bill.description
and tail.length
? Is self
omitted here? If yes, when can I omit self
? Can I omit them inside __init__
method?
class Bill():
def __init__(self, description):
self.description = description
class Tail():
def __init__(self, length):
self.length = length
class Duck():
def __init__(self, bill, tail):
self.bill = bill
self.tail = tail
def about(self):
print('This duck has a', bill.description, 'bill and a', tail.length, 'tail')
tail = Tail('long')
bill = Bill('wide orange')
duck = Duck(bill, tail)
duck.about()
Upvotes: 2
Views: 4793
Reputation: 23203
In Python explicit is better than implicit and self
cannot ever be omitted when accessing member variables.
In your case you actually use names from global scope. To trigger an error simply assign the created objects to other names:
tail1 = Tail('long')
bill1 = Bill('another')
duck = Duck(tail1, bill1)
duck.about()
Upvotes: 4
Reputation: 77902
To make a long story short: you MUST use self.
whenever you want to access an attribute (data attribute, property or method) of the current instance.
For a somewhat more detailed answer: when using def
in a class
statement, what you create is not a "method" but a plain function. When looked up a class or instance attribute (Duck.about
or duck.about
) this function will be wrapped (together with the class and instance on which it's looked up) in a callable method
object that will take care of automagically inject the instance (or class) object as first parameter to the function call. You can read the whole details here : https://wiki.python.org/moin/FromFunctionToMethod
As other already mentionned, your code snippet only accidentally "works" because it ends up looking global names that happens to be defined, and breaks as soon as these names are not defined:
# tail = Tail('long')
# bill = Bill('wide orange')
duck = Duck(Bill('wide orange'), Tail('long'))
duck.about()
=> crashes with a NameError
You'd also get unexpected results if rebinding these global names ie:
tail = Tail('long')
bill = Bill('wide orange')
duck1 = Duck(bill, tail)
tail = Tail('short')
bill = Bill('norvegian blue')
duck2 = Duck(bill, tail)
duck1.about()
=> Duh, why does it prints "short norvegian blue" ???
Upvotes: 4
Reputation: 387507
You should avoid doing that. bill
in about()
will refer to bill
in the outer scope, in this case the global one. So when you change the value of that global variable, the output of about()
is also affected:
>>> tail = Tail('long')
>>> bill = Bill('wide orange')
>>> duck = Duck(bill, tail)
>>> duck.about()
This duck has a wide orange bill and a long tail
>>> bill = Bill('DIFFERENT')
>>> duck.about()
This duck has a DIFFERENT bill and a long tail
In order to reference the bill
and tail
objects that were passed to Duck()
and are stored within the instance, you always need to use self.bill
and self.tail
.
Upvotes: 1
Reputation: 521995
You can write that because you have defined a global variable bill
and tail
, which is being referenced here.
...
def about(self):
print('This duck has a', bill.description, 'bill and a', tail.length, 'tail')
# these ^^^^ ^^^^
# refer to these vvvv
tail = Tail('long')
bill = Bill('wide orange')
So it's not doing what you think. If you want to refer to the bill
and tail
properties of your current object, you cannot omit self
, it has to be self.bill
and self.tail
. Your code will break if you rename or remove those global variables.
Upvotes: 2