Juzzbott
Juzzbott

Reputation: 1789

Please help my understanding of python class vs instance variables

I'm new to Python, and I'm having a little trouble comprehending how Python interprets class and instance variables. My background is C#, so I have a fairly good understanding of OOP (from a C# mindset), however I'm just a little confused with python. I'm guessing this is because I'm thinking in the wrong mind set.

Take the following class as example:

class User():
    """The default user implementation"""

    # define the variables
    id = None
    first_name = None
    last_name = None
    email = None
    password = None
    created = None
    deleted = False

    def __init__(self):
        """Creates a new instance of the User object."""
        self.is_admin = False

From the documentation that I have read, all of the id, first_name, last_name etc. are class attribute which are shared between instances. These would be static within a C# mindset. Then the is_admin is an instance attribute, which is limited to a specific instance of the object. These would be fields or properties within C#.

However, my confusion comes when I do something like this:


    new_user = User()
    new_user.id = "blah"
    new_user.last_name = "lasty"

    new_user1 = User()
    new_user1.id = "some_id"
    new_user1.last_name = "firsty"


This sets the values as:


    new_user.id = "blah"
    new_user.last_name = "lasty"

    new_user1.id = "some_id"
    new_user1.last_name = "firsty"

Given that the id and last_name are defined as class attributes, I would have assumed that the calls to the new_user1 objects would have overwritten the "blah" and "lasty" values, however each instance has retained the values that were defined to it. Hence, my confusion.

If this is normal, could someone please shed some light on to why this is? Also, in that case, how does one define a static variable?

Cheers, Justin

Upvotes: 3

Views: 187

Answers (4)

fiacre
fiacre

Reputation: 1180

Simplest idea that works and shows the difference between a class variable and an instance variable:

>>> import datetime
>>> class Example:
...     my_name = 'example'
...     def __init__(self):
...         self.now = datetime.datetime.now()
...
>>> e = Example()
>>> e.my_name
'example'
>>> e.now
datetime.datetime(2015, 6, 1, 2, 2, 58, 211399)
>>> d = Example()
>>> d.my_name
'example'
>>> d.now
datetime.datetime(2015, 6, 1, 2, 4, 21, 165731)
>>>

my_name is a class variable -- it is a property of the class and any instance of the class has access to that property. now is an instance variable -- only object of type Example have access to datetime and datetime is different for each object.

To drive the point home, let's try to access each variable from the class itself:

>>> Example.now
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Example' has no attribute 'now'
>>> Example.my_name
'example'
>>>

An object CAN modify a class variable for that instance:

>>> d.my_name = "George"
>>> d.may_name
'George'
>>> Example.my_name
'example'

But not for the class.

Static is a keyword that references static linkage in C and C++ and is used in many modern OO languages to denote a piece of code that is shared by all objects of a class but is not a class method or variable per se. Python as a staticmethod decorator for methods. but there are no static variables, only class variables.

I hope that clarifies things.

Upvotes: 1

justengel
justengel

Reputation: 6320

Python does not have constants or static variables. For class attributes it is the same. The class is loaded with the value on import. If the user changes that value for import.

class MyClass(object):
    id = 1

def main():

    myclass = MyClass()
    print("class1", myclass.id)
    print()

    myclass2 = MyClass()
    myclass2.id = 2
    print("class1", myclass.id)
    print("class2", myclass2.id)
    print()

    MyClass.id = 3
    myclass3 = MyClass()
    print("class1", myclass.id)
    print("class2", myclass2.id)
    print("class3", myclass3.id)
    print()

    myclass4 = MyClass()
    print("class1", myclass.id)
    print("class2", myclass2.id)
    print("class3", myclass3.id)
    print("class4", myclass4.id)
    print()

    myclass5 = MyClass()
    myclass5.id = 5 
    print("class1", myclass.id)
    print("class2", myclass2.id)
    print("class3", myclass3.id)
    print("class4", myclass4.id)
    print("class5", myclass5.id)
# end main

if __name__ == '__main__':
    main()

The results are interesting.

class1 1

class1 1
class2 2

class1 3
class2 2
class3 3

class1 3
class2 2
class3 3
class4 3

class1 3
class2 2
class3 3
class4 3
class5 5

If you have not set the instance id it will default to the class id.

MyClass.id = 1

This will change the value of an instance if the instance did not set a value.

Upvotes: 0

kindall
kindall

Reputation: 184365

Python looks up attributes on the instance, then the instance's class, then any base classes and their parents. So id and last_name are instance attributes that hide the attributes of the same name on the class. If you access an attribute you did not set on the instance (such as first_name) then you get the class's attribute since it was not found on the instance.

Upvotes: 3

dawg
dawg

Reputation: 104092

Class variables are not really 'static' in the sense that C and Java uses the term. They are easily overwritten. They are better thought of as single copy default values associated with the class object.

Consider (in Python 3):

class Foo(object):
    cv='cv'
    def __setattr__(self, name, value):
        if name in self.__class__.__dict__:
            print('Overloading class var "{}" with instance attribute "{}"'.format(self.__class__.__dict__[name], value))
            super(self.__class__, self).__setattr__(name, value)

    def __getattribute__(*args):
        self, name, *the_rest=args
        if name in Foo.__dict__:
            print('there is a class variable called "{}" with value of: "{}"'.format(name, Foo.__dict__[name]))      
        return object.__getattribute__(*args)  

Now instantiate a copy of the class Foo and test the class variable and instance variables:

>>> foo=Foo()
>>> foo.cv
there is a class variable called "cv" with value of: "cv"
'cv'
>>> foo.cv='IV'
Overloading class var "cv" with instance attribute "IV"
>>> foo.cv
there is a class variable called "cv" with value of: "cv"
'IV'
>>> Foo.cv='NEW CV'
>>> bar=Foo()
>>> bar.cv
there is a class variable called "cv" with value of: "NEW CV"
'NEW CV'

Upvotes: 0

Related Questions