Python Class Members

Question:

I am just learning Python and I come from a C background so please let me know if I have any confusion / mix up between both.

Assume I have the following class:

class Node(object):
    def __init__(self, element):
        self.element = element
        self.left = self.right = None

    @classmethod
    def tree(cls, element, left, right):
        node = cls(element)
        node.left = left
        node.right = right
        return node

This is a class named Node, that overloads the constructor, to be able to handle different arguments if needed.

What is the difference between defining self.element in __init__ only (as shown above) as opposed to doing the following:

class Node(object):
    element, left, right = None
    def __init__(self, element):
        self.element = element
        self.left = self.right = None

Isn’t self.element in __init__ the same as the class’s element variable defined? Wouldn’t that just overwrite element from None to the element value passed into __init__?

Asked By: darksky

||

Answers:

self.element inside the constructor (i.e. – __init__ method) is an instance variable (if a node object modifies its value it only changes for this object) where the one in the second version is a class variable (so if one node object modifies its value it will change for all node objects).

The analogy in C++ would be non-static versus static member variables in your class.

Answered By: Borgleader

The important part is the self argument to __init__. In fact, in any instance method, this will be the first argument. This is done by design; in Python, the only time you actually have access to the instance is during method calls, and it is shown explicitly with the self argument.

When you’re inside of a class definition, you don’t have any instances yet, so what you’re really modifying is the class itself. Thus, if you define attributes at class-level, then they really become class attributes, and not instance.

Comparing it to a C++, you could probably say that "classes" in those languages are basically blueprints for the objects that they represent. "These objects shall have foo and bar attributes, and, in addition, the following methods." In Python, however, classes are objects themselves, and their main strength is that they can create copies (instances) of themselves, which also happen to use the class’s methods. So, it’s more like "They shall have foo and bar as class attributes, and, in addition, the following method which thou shall use to create instances."

So, instead of a blueprint, it’s more of a step-by-step how-to.

Answered By: voithos

self.element in the __init__ is an instance variable, you can get/set it in any other member function by typing self.element. element declared in the class is the class variable, you can get/set it by typing Node.element.

Answered By: waitingkuo

One is a class attribute, while the other is an instance attribute. They are different, but they are closely related to one another in ways that make them look the same at times.

It has to do with the way python looks up attributes. There’s a hierarchy. In simple cases it might look like this:

instance -> Subclass -> Superclass -> object (built-in type)

When you look for an attribute on instance like this…

`instance.val`

…what actually happens is that first, Python looks for val in the instance itself. Then, if it doesn’t find val, it looks in its class, Subclass. Then, if it doesn’t find val there, it looks in the parent of Subclass, Superclass. This means that when you do this…

>>> class Foo():
    foovar = 10  
    def __init__(self, val):
        self.selfvar = val

…all instances of Foo share foovar, but have their own distinct selfvars. Here’s a simple, concrete example of how that works:

>>> f = Foo(5)
>>> f.foovar
10
>>> Foo.foovar
10

If we don’t touch foovar, it’s the same for both f and Foo. But if we change f.foovar

>>> f.foovar = 5
>>> f.foovar
5
>>> Foo.foovar
10

…we add an instance attribute that effectively masks the value of Foo.foovar. Now if we change Foo.foovar directly, it doesn’t affect our foo instance:

>>> Foo.foovar = 7
>>> f.foovar
5

But it does affect a new foo instance:

>>> Foo(5).foovar
7

Also keep in mind that mutable objects add another layer of indirection (as mgilson reminded me). Here, f.foovar refers to the same object as Foo.foovar, so when you alter the object, the changes are propagated up the hierarchy:

>>> Foo.foovar = [1]
>>> f = Foo(5)
>>> f.foovar[0] = 99
>>> Foo.foovar
[99]
Answered By: senderle

In python it is possible to have class variables and instance variables of the same name. They are located separately in memory, and are accessed quite differently.

In your code:

class Node(object):
    element, left, right = None
    def __init__(self, element):
        self.element = element
        self.left = self.right = None

The first set of variables (outside the __init__ function) are called class variables. These can be subsequently accessed using Node.element, etc. These are equivalent to static member variables in C++, and they are shared by all instances of the class.

The second set of variables (inside the __init__ function) are called instance variables. These are accessed via the self object, e.g. self.element, or by the instance name e.g. myNode.element outside of the class.

It is important to note that you have to use either the self.variable or Node.variable form to access either of these from within a member function. Just accessing variable will try to access a local variable called variable.

Answered By: Lee Netherton

When you try to access the variable with a class "notation" it only looks into

cls.__dict__

but when you try to access the variable with instance "notation" it looks first in

self.__dict__ 

and if it finds the desired member then it returns, or if it can’t find then it also looks in

cls.__dict__

here cls is the class

class Test:
    temp_1=10
    temp_2=20

    def __init__(self):
        self.test_1=10
        self.test_2=20

    @classmethod
    def c_test(cls):
        pass

    def t_method(self):
        pass


print Test.__dict__
print Test().__dict__

Output:

{'c_test': <classmethod object at 0x7fede8f35a60>, '__module__': '__main__', 't_method': <function t_method at 0x7fede8f336e0>, 'temp_1': 10, '__doc__': None, '__init__': <function __init__ at 0x7fede8f335f0>, 'temp_2': 20}

{'test_2': 20, 'test_1': 10}

For detail class special attribute

Answered By: Kallz

I wanted to add an answer with a different "perspective" that none of the above answers mentions (not saying that they are not good !!) –> the "size" of the object.

Preface: In Python, for better or worse, it is possible to "create" (add) a "new" (additional) INSTANCE class member to a particular object instance in RUN TIME, meaning, if for example a specific class has two instance members, then a 3rd one can be added after the creation of the object instance and it will be added ONLY to this specific instance of the class (i.e. – the "blueprint" of the class will NOT change). See #1) in the sample code below.

Now, if the name of this "new" INSTANCE member happens to be with the same name of a "global" class member –> then for this specific object instance, an additional instance member will be added, with the same name as of the class member that is already has (and it shares with all other instances of this class). See #2) in the sample code below.

–> So, when you access for "setting purposes" the "global" (class) member via a SPECIFIC instance object and NOT via the class name, meaning: my_class_obj2.class_data_member = some_value and NOT MyClass.class_data_member according to the sample code below, then what happens is that for this specific instance, an additional instance member is created – thus its SIZE was altered as well (if you run the sample code you will see that the two different instances has two different sizes. I’m not so good with all that has to do with Python’s size "allocations", so I was expecting to see that the size of my_class_obj2 to be greater than my_class_obj –> but it is not, I guess that further information about sizes of objects in Python can be seen in this Q&A, from which I took the usage example in the asizeof function of pympler package).

See the below code for a more complete example:

import sys
from pympler import asizeof

class MyClass:

    class_data_member = 15

    def __init__(self, num):
        self.num = num
        self.string = ""

    def define_some_class_member(self):
        pass

    def print_me(self):
        self.x = 17
        print("MyClass::print_me - num is:" + str(self.num) + ", string is:" + self.string)
        print("MyClass::print_me - self.x is:" + str(self.x))

    def foo(self):
        print("Hi there form " + __class__.__name__ + "::foo")


def classes_and_object_example():
    func_name = "classes_and_object_example - "
    print(func_name + "creating MyClass object")
    my_class_obj = MyClass(12)
    my_class_obj.print_me()

    print(func_name + "creating another MyClass object")
    my_class_obj2 = MyClass(17)
    my_class_obj2.print_me()

    # 1)
    my_class_obj.some_new_instance_member = 90
    print(func_name + "the new additional instance member is:" + str(my_class_obj.some_new_instance_member))
    # Pay attention that the "new instance member" was added to my_class_obj and NOT to my_class_obj2 so the below statement is illegal
    # print("the new additional instance member is:" + str(my_class_obj2.some_new_instance_member))

    print(func_name + "the "global" data member seen by my_class_obj.class_data_member"
      + " is:" + str(my_class_obj.class_data_member) + " and the "global" data member seen by my_class_obj2.class_data_member"
      + " is (also):" + str(my_class_obj2.class_data_member))

    # 2)
    my_class_obj2.class_data_member = 99
    print(func_name + "the "global" data member seen by my_class_obj2.class_data_member"
      + " after intentionally modifying it is:" + str(my_class_obj2.class_data_member) + ", while on the other hand it is STILL seen by my_class_obj.class_data_member"
      + " as:" + str(MyClass.class_data_member))

    MyClass.class_data_member = 67
    print(func_name + "after setting the "global (static)" data member that is shared among all MyClass instances"
      + " using the assignemt:MyClass.class_data_member = 67, its value is:" + str(MyClass.class_data_member) + ", while on the other hand my_class_obj2 STILL has its "own" INSTANCE member with the same name and value of:" 
      + str(my_class_obj2.class_data_member))

    size_of_my_class_orig_object = asizeof.asizeof(my_class_obj)
    print(func_name + "the size of a MyClass object instance is:" + str(size_of_my_class_orig_object))

    size_of_my_class_modified_object = asizeof.asizeof(my_class_obj2)
    print(func_name + "the size of a MyClass object instance after "adding" to it an additional instance member is:" + str(size_of_my_class_modified_object))

# run the sample code for illustration
classes_and_object_example()
Answered By: Guy Avraham
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.