Nicholas
Nicholas

Reputation: 2668

In python, when self can be omitted?

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()

The output are as follows, enter image description here

Upvotes: 2

Views: 4793

Answers (4)

Łukasz Rogalski
Łukasz Rogalski

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

bruno desthuilliers
bruno desthuilliers

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

poke
poke

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

deceze
deceze

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

Related Questions