ramd
ramd

Reputation: 347

Understanding python class attributes

I am very new to programming and started learning python. Might look very stupid question, so please pardon my ignorance. Consider the following snippet of code :

class Test1:
    bar = 10    
    def display(self,foo):
        self.foo=foo
        print "foo : ",self.foo #80
    def display1(self):
        print "bar: ", self.bar #10
        print "again foo: ", self.foo #80

if __name__ == '__main__':
    test1 = Test1()
    test1.display(80)
    test1.display1()
    print test1.bar #10
    print test1.foo #80

I want to understand what is the difference between using foo and bar (wrt to where we have defined them) as in scope wise they are equally accessible at all places compared to each other and only difference is that one is inside function and other is inside Class but they both are still "instance" variable. So which is good practice?

Also, if I slightly modify display function as below :

    def display(self,foo):
        self.foo=foo
        foo = foo
        print "self.foo : ",self.foo 
        print "foo : ",foo 

Can someone please explain how python sees this, as in what difference/significance this self keyword is bringing in between two foo.

Upvotes: 5

Views: 282

Answers (3)

phant0m
phant0m

Reputation: 16905

bar is a class attribute. Since classes in Python are objects, they too can have attributes. bar just happens to live on that Test object, not an instance thereof.

Because of the way Python resolves attribute lookups, it looks like test1 has a bar attribute, but it doesn't.

foo on the other hand lives on the instance test1 after calling display(80). This means that different instances of Testcan have different values in their respective foo attributes.

Of course, you could use class variables as some kind of "shared default value", which you can then "override" with an instance attribute, but that might get confusing.

Second question

def display(self,foo):
    self.foo=foo
    foo = foo
    print "self.foo : ",self.foo 
    print "foo : ",foo 

Let's just get a detail out of the way: self is not a keyword, it's just convention to call the first argument "self", you could also call it "this" or "that" or "bar" if you liked, but I wouldn't recommend that.

Python will pass the object, on which the method was called as the first argument.

def display(self,foo): 

This foo is the name of the first parameter of the display instance-function.

    self.foo=foo

This sets the attribute with the name "foo" of the instance, on which you called display() to the value, which you passed as first argument. Using your example test1.display(80), self will be test1, foo is 80 and test1.foo will thus be set to 80.

    foo = foo

This does nothing at all. It references the first parameter foo.

The next two lines again reference the instance variable foo and the first parameter foo.

Upvotes: 3

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250951

bar is a class attribute and foo is an instance attribute. The main difference is that bar will be available to all class instances while foo will be available to an instance only if you call display on that instance

>>> ins1 = Test1()

ins1.bar works fine because it is a class attribute and is shared by all instances.

>>> ins1.bar
10

But you can't access foo directly here as it is not defined yet:

>>> ins1.foo
Traceback (most recent call last):
  File "<ipython-input-62-9495b4da308f>", line 1, in <module>
    ins1.foo
AttributeError: Test1 instance has no attribute 'foo'

>>> ins1.display(12)
foo :  12
>>> ins1.foo
12

If you want to initialize some instance attributes when the instance is created then place them inside the __init__ method.

class A(object):
    bar =  10
    def __init__(self, foo):
        self.foo = foo   #this gets initialized when the instance is created
    def func(self, x):
        self.spam = x    #this will be available only when you call func() on the instance
...         
>>> a = A(10)
>>> a.bar
10
>>> a.foo
10
>>> a.spam
Traceback (most recent call last):
  File "<ipython-input-85-3b4ed07da1b4>", line 1, in <module>
    a.spam
AttributeError: 'A' object has no attribute 'spam'

>>> a.func(2)
>>> a.spam
2

Upvotes: 4

sberry
sberry

Reputation: 132018

Personally, I don't like defining instance variables inside of methods other than __init__ or as class variables like bar in your example above.

Again, personally, I like to see every member of my class by looking at the top. Whether it is a class variable that you use as an instance variable (something I don't usually do) or an instance variable defined in __init__, it is easy to tell what is and isn't defined in a class by inspecting the first section of the class definition.

If you won't need to access a variable as a class member (ie. you are only defining it there to avoid writing self.variable = val in the __init__ method, then I would steer clear of it. If you might need to access it as a class variable, then doing what you are with bar is ok in my book.

This would be my preferred way of writing you class.

class Test1:

    def __init__(self):
        self.foo = None
        self.bar = 10

    def display(self,foo):
        self.foo=foo
        print "foo : ",self.foo #80

    def display1(self):
        print "bar: ", self.bar #10
        print "again foo: ", self.foo #80

Upvotes: 1

Related Questions