Reputation: 21019
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__
?
Upvotes: 71
Views: 113030
Reputation: 70552
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.
Upvotes: 1
Reputation: 3523
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
Upvotes: 0
Reputation: 3690
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()
Upvotes: 0
Reputation: 93754
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
.
Upvotes: 0
Reputation: 15916
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.
Upvotes: 2
Reputation: 150947
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 selfvar
s. 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]
Upvotes: 118
Reputation: 22482
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
.
Upvotes: 31