Do instance variables declared outside of `__init__()` have any differences in python? - python

I'm wondering weather it is necessary to define class instance variable within class declarations.
I tried assigning a new instance variable after the object (class instance) was already created, and looks like there is no difference. Are there any caveats in this approach?
class Context():
def __init__(self, extension):
self.extension = extension
c = Context('extension+')
print(f"before: {c.__dict__}")
c.new_var = 'new_var_content'
print(c.extension + c.new_var)
print(f"after: {c.__dict__}")
printed:
before: {'extension': 'extension+'}
extension+new_var_content
after: {'extension': 'extension+', 'new_var': 'new_var_content'}

There is no difference between declaring self.foo within a def __init__(self, <arguments>): definition, and declaring it after an object has been instantiated.
Both assignments have instance-level scope.
Given -
class Context:
i_am_a_class_variable = 'class_string'
def __init__(self, bar):
self.bar = bar
See -
class attributes can be accessed without instantiating an object.
>>> Context.i_am_a_class_variable
'class_string'
instance attributes can be assigned during instantiation using the __init__(self) function.
>>> Context.bar
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-8be0704da5be> in <module>
----> 1 Context.bar
>>> instance = Context('bar')
>>> instance.bar
'bar'
instance attributes can be assigned after instantiation, directly
>>> instance = Context('bar')
>>> instance.foo = 'foo'
>>> instance.foo
'foo'

Speaking in terms of whether you can assign a value to the property or create a new property, there is no difference if you do it within init or anywhere else after the object is created as in both cases it gets added in dict of the object(unless you use slots)
However, if you want your class to be initialized with desired state (i.e, having some mandatory variables with default/preset values) , you should put it in init. Since init is called implicitly as soon as object is created, you object will be having desired state.

Related

Python, how come we can create class variables that were not defined in class creation?

Let's say we have this simple Python code
class MyClass(object):
class_var = 1
def __init__(self, i_var):
self.i_var = i_var
Correct me if I get any of this wrong:
Class_Var is a class variable that is the same for all instances of MyClass object.
I_Var is an instance variable that only exists in instances of the MyClass object
foo = MyClass(2)
bar = MyClass(3)
foo.class_var, foo.i_var
## 1, 2
bar.class_var, bar.i_var
## 1, 3
Class variables are also properties of the class itself.
MyClass.class_var ##
## 1
MyClass.I_var should error out, correct?
Does that mean that class variables can be considered like instance variables of the class object itself (since all classes are objects) ?
MyClass.new_attribute = 'foo'
print(hasattr(ObjectCreator, 'new_attribute'))
That should return true. And
print (MyClass.new_attribute)
should return foo.
How come we can create a new class variable that was not defined in the original definition for that class?
Is
MyClass.new_attribute = 'foo'
the exact same thing as creating that class attribute in the original definition?
class MyClass(object):
class_var = 1
new_attribute = 'foo'
So we can create new class attributes at runtime? How does that not interfere with the init constructor that creates the class object and has those class variables as instance variables of the class object?
A class object is just an instance of yet another type, usually type (though you can change this using the metaclass parameter to the class statement).
Like most other instances, you can add arbitrary instance attributes to a class object at any time.
Class attributes and instance attributes are wholly separate; the former are stored on the class object, the latter on instances of the class.
There's nothing particularly special about __init__; it's just another method that, among other things, can attached new attributes to an object. What is special is that __init__ is called automatically when you create a new instance of the class by calling the class. foo = MyClass(2) is equivalent to
foo = MyClass.__new__(MyClass, 2)
foo.__init__(2)
The class statement
class MyClass(object):
class_var = 1
def __init__(self, i_var):
self.i_var = i_var
is roughly equivalent to
def my_class_init(self, i_var):
self.i_var = i_var
MyClass = type('MyClass', (object,), {'class_var': 1, '__init__: my_class_init})
The 3-argument form of type lets you pass a dict that creates class attributes when you first create the class, but you can always assign attributes after the fact as well:
MyClass = type('MyClass', (object,), {})
MyClass.class_var = 1
MyClass.__init__ = my_class_init
Just to blow your mind a little bit more, the call to type can be though of as
MyClass = type.__new__(type, 'MyClass', (object,), {...})
MyClass.__init__('MyClass', (object,), {...})
though unless you define a custom metaclass (by subclassing type), you never have to think about type itself having __new__ and __init__ methods.
Does that mean that class variables can be considered like instance variables of the class object itself (since all classes are objects) ?
Yes.
How come we can create a new class variable that was not defined in the original definition for that class?
Because Python is a dynamic language. A class can be created at run time - in fact, it is created at run time when you run Python interactively.
So we can create new class attributes at runtime?
Yes, unless the metaclass (the class of the class) has forbidden it.
How does that not interfere with the init constructor that creates the class object and has those class variables as instance variables of the class object?
The only rule is that you cannot use something that has not yet be defined or something that has been deleted:
>>> class MyClass(object):
class_var = 1
def __init__(self, i_var):
self.i_var = i_var
self.j_var = self.class_var + 1
>>> a = MyClass(2)
>>> del MyClass.class_var
>>> b = MyClass(3)
Traceback (most recent call last):
File "<pyshell#39>", line 1, in <module>
b = MyClass(3)
File "<pyshell#36>", line 6, in __init__
self.j_var = self.class_var + 1
AttributeError: 'MyClass' object has no attribute 'class_var'
There is no magic here: anything can only exists between its definition point and its destruction point. Python allows you to add attributes to objects at any time, except that some classes (for example object) forbid it.
With the previous a object of class MyClass, you could do:
a.z_var = 12
from that point, z_var will be an attribute of a but others objects of same class will not have it.
Simply object forbids that:
>>> o = object()
>>> o.x=1
Traceback (most recent call last):
File "<pyshell#41>", line 1, in <module>
o.x=1
AttributeError: 'object' object has no attribute 'x'

Why method can't access class variable?

I am trying to understand variable scopes in Python, most of the things are clear to me except for the part that I don't understand why class variable is not accessible from its method.
In following example mydef1() can't access a, but if a is declared in global scope(outside class definition) it can.
class MyClass1:
a = 25
def mydef1(self):
print(a)
ins1 = MyClass1()
ins1.mydef1()
Output
Traceback (most recent call last):
File "E:\dev\Python\scope_test2.py", line 6, in <module>
ins1.mydef1()
File "E:\dev\Python\scope_test2.py", line 4, in mydef1
print(a)
NameError: name 'a' is not defined
It's important to understand that some of these comments are not equivalent. MyClass.a is a member of the class itself, self.a is a member of the instance of the class.
When you use self.a it will return a from the class, because there is no a on the instance. If there was also an a which was a member of the instance, it would return that instead. Generally the instance a is set using the __init__ constructor. Both of these can exist simultaneously.
class MyClass1:
a = 25
def __init__(self):
self.a = 100
def instance_a(self):
print(self.a)
def change_instance_a(self):
self.a = 5
def class_a(self):
print(MyClass1.a)
def change_class_a(self):
MyClass1.a = 10
# Create two instances
ins1 = MyClass1()
ins2 = MyClass1()
# Both instances have the same Class member a, and the same instance member a
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()
# Now lets change instance a on one of our instances
ins1.change_instance_a()
# Print again, see that class a values remain the same, but instance a has
# changed on one instance only
print()
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()
# Lets change the class member a on just one instance
ins1.change_class_a()
# Both instances now report that new value for the class member a
print()
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()
Short answer: That's Python's scoping rules. Nested functions in Python are lexically scoped, but that doesn't apply to things nested in classes.
class Foo:
a = 25
print(a)
class Bar:
print(a)
The first one prints, but the second is a NameError.
Longer answer:
There is a function closure for class-level variables, but it is all wrapped in __class__. (The main use for this is in the super() magic, which is why it no longer needs arguments in Python 3.)
class MyClass1:
a = 25
def mydef1(self):
print(__class__.a)
ins1 = MyClass1()
ins1.mydef1() # 25
Normally, you'd access such things through the self parameter to allow subclasses to override them, but __class__ would even work for a staticmethod, which has neither self, nor cls.
class MyClass1:
a = 25
#staticmethod
def mydef1():
print(__class__.a)
ins1 = MyClass1()
ins1.mydef1() # 25
The class object technically doesn't even exist until after the class declaration finishes executing, that's why you can't do
class Foo:
a = 25
class Bar:
# NameError: free variable '__class__' referenced before assignment
print(__class__.a)
Nor even,
class Foo:
a = 25
def bar():
print(__class__.a)
# NameError: free variable '__class__' referenced before assignment in enclosing scope
bar()
You can, however, access the locals() dict before then.
class Foo:
a = 21
locals()['a'] *= 2
Foo.a # 42
So this works.
class Foo:
a = 25
global foolocals
foolocals = locals()
def bar():
print(foolocals['a'])
bar() # 25
Consider the following class:
class ScopeTest(object):
classvariable = 0
number_of_objects_created = 0
def __init__(self, value=1):
self.instancevariable = value
ScopeTest.number_of_objects_created += 1
def number_of_brothers(self):
print(ScopeTest.number_of_objects_created)
def confusion(self, value=2):
self.classvariable = value
print (f"class: {ScopeTest.classvariable}, self:{self.classvariable}")
And let's see what happens when you play around with it:
>>> a = ScopeTest()
>>> a.instancevariable
1
>>> a.classvariable
0
>>> ScopeTest.classvariable
0
So far so good, but when you assign a new attribute to a:
>>> a.classvariable = 2
>>> a.classvariable
2
>>> ScopeTest.classvariable
0
The same holds if you add the attribute inside a class's method:
>>> a.confusion(4)
class: 0, self:4
These kind of class attributes are good to keep things common to all objects, as the number_of_objects_created:
>>> b = ScopeTest()
>>> b.number_of_brothers()
2
>>> a.number_of_brothers()
2
You could get a little more from this by adding yet another method to the class:
class ScopeTest(object):
...
def other_function(self, classvariable=3):
print(f"class: {ScopeTest.classvariable}\t"
f"instance: {self.classvariable}\t"
f"argument:{classvariable}")
And calling it (after using the first 'confusion' method to set self.classvariable):
>>> a.confusion()
class: 0, self:2
>>> a.other_function()
class: 0 instance: 2 argument:3
By calling print(a) in mydef1, python is looking for a local (or global, as you discovered) variable "a". That is, a variable not directly related to MyClass1 in any way, but such a variable has not been defined yet.
If you're trying to access the class variable "a" (i.e. a is a member of the class itself, not any instance of it), you must use MyClass1.a. Alternatively, because there is no instance variable named "a", you can also use self.a to the same effect. However, as soon as self.a is explicitly defined, self.a == MyClass1.a may not be true. For example:
>>>class MyClass1:
... a = 25
...
>>>my_obj = MyClass1()
>>>MyClass1.a
25
>>>my_obj.a
25
>>>MyClass1.a += 1
>>>MyClass1.a
26
>>>my_obj.a
26
>>>my_obj.a = 5 # explicitly define "a" for this instance; my_obj.a not longer synonymous with MyClass1.a
>>>MyClass1.a += 1
>>>MyClass1.a
27
>>>my_obj.a
5
You need self.a. There is no implicit class scope.

How to pass arguments to python function whose first parameter is self?

Take the following simplified example.
class A(object):
variable_A = 1
variable_B = 2
def functionA(self, param):
print(param+self.variable_A)
print(A.functionA(3))
In the above example, I get the following error
Traceback (most recent call last):
File "python", line 8, in <module>
TypeError: functionA() missing 1 required positional argument: 'param'
But, if I remove the self, in the function declaration, I am not able to access the variables variable_A and variable_B in the class, and I get the following error
Traceback (most recent call last):
File "python", line 8, in <module>
File "python", line 6, in functionA
NameError: name 'self' is not defined
So, how do I access the class variables and not get the param error here?
I am using Python 3 FYI.
You must first create an instance of the class A
class A(object):
variable_A = 1
variable_B = 2
def functionA(self, param):
return (param+self.variable_A)
a = A()
print(a.functionA(3))
You can use staticmethod decorator if you don't want to use an instance.
Static methods are a special case of methods. Sometimes, you'll write code that belongs to a class, but that doesn't use the object itself at all.
class A(object):
variable_A = 1
variable_B = 2
#staticmethod
def functionA(param):
return (param+A.variable_A)
print(A.functionA(3))
Another option is to use classmethod decorator.
Class methods are methods that are not bound to an object, but to a class!
class A(object):
variable_A = 1
variable_B = 2
#classmethod
def functionA(cls,param):
return (param+cls.variable_A)
print(A.functionA(3))
functionA in your snippet above is an instance method. You do not pass "self" directly to it. Instead, you need to create an instance in order to use it. The "self" argument of the function is, in fact, the instance it's called on. E.g.:
a = A()
a.functionA(3)
P.S.
Note that your functionA calls print but doesn't return anything, meaning it implicitly returns None. You should either have it return a value and print it from the caller, or, as I have done above, call it and let it print on its own.
Create an object of A first.
a = A()
a.functionA(3)
When a function object (what the def statement creates) is an attribute of a class AND is looked up (using the obj.attrname scheme) on the class or an instance of the class, it gets turned into a method object. This method object is itself a callable. If the lookup happens on an instance, this instance will be "magically" inserted as the first argument to the function. If not, you will have to provide it by yourself (just like you would for any other argument).
You can read more about this (and how the "magic" happens here: https://wiki.python.org/moin/FromFunctionToMethod
In your case, you lookup the function on the class, so it expects two arguments (self and param), but you only pass param, hence the error.
You defined variable_A and variable_B as class attributes (attributes that will be shared between all instances of the class). If that's really the intention, and you want a method you can call on the class itself and that will be able to access class attributes, you can make functionA a classmethod (it works the same as an "instance" method except it's the class that is 'magically' inserted as first argument):
class A(object):
variable_A = 1
variable_B = 2
#classmethod
def functionA(cls, param):
return param + cls.variable_A
Then you can call functionA either directly on the class itself:
print(A.functionA(42))
or on an instance if you already have one at hand:
a = A()
# ...
print(a.functionA(42))
Now if you really wanted variable_A and variable_B to be per-instance attributes (each instance of A has it's own distinct variables), you need to 1/ create those attributes on the instance itself in the initialier method and 2/ call functionA on some A instance, ie:
class A(object):
def __init__(self, variable_A=1, variable_B=2):
self.variable_A = variableA
self.variable_B = variableB
def functionA(self, param):
return param + self.variable_A
a1 = A() # using default values
print(a1.functionA(42))
a2 = A(5) # custom value for variable_A
print(a2.functionA(42))
class A(object):
variable_A = 1
variable_B = 2
def functionA(self, param):
print(param+self.variable_A)
A().functionA(3)
A() is calling the class to create an instance
4
[Program finished]
You can use return in function and then print at last.
Posting this answer as per OP template , accepted answers and other answers are recommended way to do it.

Adding a new attribute to Python object?

Being from OOPS background, It looks strange to see below code from link
def f():
f.beencalled = True
return 0
My question:
1)
From the above code,
Is f a reference variable pointing to an object f of class 'function'?
2)
We add a new attribute beencalled to an object f, so now 'function' class does not have this attribute beencalled defined and we say that object f is an object of class 'function'? Does it make sense?
1) Yes:
>>> def f():
print(type(f))
>>> f()
>>> <class 'function'>
2) The function class does not have a new attribute, but the object f does. Adding or removing attributes to/from an object does not affect which attributes other objects of that class will have:
>>> class A: pass
>>> a = A()
>>> a.var = 7
>>> b = A()
>>> b.var
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
b.newvar
AttributeError: 'A' object has no attribute 'var'
Classes are much more flexible in python than in Java or C++. Objects can have attributes not defined in their class, or even lack attributes that were defined in their class! Look at this:
>>> class A:
def __init__(self, a):
self.var = a
>>> obj = A(7)
>>> del obj.var #deletes the var attribute from obj, does not change the A class
>>> obj.var
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
obj.var
AttributeError: 'A' object has no attribute 'var'
>>> obj2 = A(6)
>>> obj2.var #obj2 is a new object, so the fact we deleted var from obj doesn't affect it
6
EDIT: after a bit of searching I found an explanation for why this behavior was chosen (source):
To implement user-defined objects, I settled on the simplest possible
design; a scheme where objects were represented by a new kind of
built-in object that stored a class reference pointing to a "class
object" shared by all instances of the same class, and a dictionary,
dubbed the "instance dictionary", that contained the instance
variables.
In this implementation, the instance dictionary would contain the
instance variables of each individual object whereas the class object
would contain stuff shared between all instances of the same class--in
particular, methods. In implementing class objects, I again chose the
simplest possible design; the set of methods of a class were stored in
a dictionary whose keys are the method names. This, I dubbed the class
dictionary. To support inheritance, class objects would additionally
store a reference to the class objects corresponding to the base
classes. At the time, I was fairly naïve about classes, but I knew
about multiple inheritance, which had recently been added to C++. I
decided that as long as I was going to support inheritance, I might as
well support a simple-minded version of multiple inheritance. Thus,
every class object could have one or more base classes.
In this implementation, the underlying mechanics of working with
objects are actually very simple. Whenever changes are made to
instance or class variables, those changes are simply reflected in the
underlying dictionary object. For example, setting an instance
variable on an instance updates its local instance dictionary.
Likewise, when looking up the value of a instance variable of an
object, one merely checks its instance dictionary for the existence of
that variable. If the variable is not found there, things become a
little more interesting. In that case, lookups are performed in the
class dictionary and then in the class dictionaries of each of the
base classes.
On a slightly different note, you can change this behavior for custom classes.
class FooBar(object):
__slots__ = ["foo","bar","baz"]
# if you don't define __slots__, you can add attr to the object as needed
# if you do, the object can only contain those attributes.
def __init__(self,foo=None,bar=None,baz=None):
self.foo = foo
self.bar = bar
self.baz = baz
def __str__(self):
return "I'm a FooBar with id {0} with foo: {1.foo}, bar: {1.bar}, baz: {1.baz}".format(id(self),self)
>>> a = FooBar("a","B","CCC")
>>> print(a)
I'm a FooBar with id 47260256 with foo: a, bar: B, baz: CCC
>>> a.spam = "eggs"
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
a.spam = "eggs"
AttributeError: 'FooBar' object has no attribute 'spam'
Alternately, without defining __slots__:
class BooFar(object):
def __str__(self):
return "I'm a BooFar with the following attributes:\n{}".format(self.__dict__)
>>> b = BooFar()
>>> print(b)
I'm a BooFar with the following attributes:
{}
>>> b.spam = "eggs"
>>> print(b)
I'm a BooFar with the following attributes:
{'spam': 'eggs'}
f() in just an instance of types.FunctionType, and instances can have their own attributes.
Adding an attribute to an instance won't affect its class unless you've overridden the __setattr__ method of that class and doing something evil there.
>>> import types
>>> def func(): pass
>>> isinstance(func, types.FunctionType)
True

What is the difference between declaring data attributes inside or outside __init__ [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python: Difference between class and instance attributes
I'm trying to get my head around OOP in Python and I'm a bit confused when it comes to declare variables within a class. Should I declare them inside of the __init__ procedure or outside it? What's the difference?
The following code works just fine:
# Declaring variables within __init__
class MyClass:
def __init__(self):
country = ""
city = ""
def information(self):
print "Hi! I'm from %s, (%s)"%(self.city,self.country)
me = MyClass()
me.country = "Spain"
me.city = "Barcelona"
me.information()
But declaring the variables outside of the __init__ procedure also works:
# Declaring variables outside of __init__
class MyClass:
country = ""
city = ""
def information(self):
print "Hi! I'm from %s, (%s)"%(self.city,self.country)
me = MyClass()
me.country = "Spain"
me.city = "Barcelona"
me.information()
In your first example you are defining instance attributes. In the second, class attributes.
Class attributes are shared between all instances of that class, where as instance attributes are "owned" by that particular instance.
Difference by example
To understand the differences let's use an example.
We'll define a class with instance attributes:
class MyClassOne:
def __init__(self):
self.country = "Spain"
self.city = "Barcelona"
self.things = []
And one with class attributes:
class MyClassTwo:
country = "Spain"
city = "Barcelona"
things = []
And a function that prints out information about one of these objects:
def information(obj):
print "I'm from {0}, ({1}). I own: {2}".format(
obj.city, obj.country, ','.join(obj.things))
Let's create 2 MyClassOne objects and change one to be Milan, and give Milan "something":
foo1 = MyClassOne()
bar1 = MyClassOne()
foo1.city = "Milan"
foo1.country = "Italy"
foo1.things.append("Something")
When we call information() on the foo1 and bar1 we get the values you'd expect:
>>> information(foo1)
I'm from Milan, (Italy). I own: Something
>>> information(bar1)
I'm from Barcelona, (Spain). I own:
However, if we were to do exactly the same thing, but using instances of MyClassTwo you'll see that the class attributes are shared between instances.
foo2 = MyClassTwo()
bar2 = MyClassTwo()
foo2.city = "Milan"
foo2.country = "Italy"
foo2.things.append("Something")
And then call information()...
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'm from Barcelona, (Spain). I own: Something
So as you can see - things is being shared between the instances. things is a reference to a list that each instance has access to. So if you append to things from any instance that same list will be seen by all other instances.
The reason you don't see this behaviour in the string variables is because you are actually assigning a new variable to an instance. In this case that reference is "owned" by the instance and not shared at the class level. To illustrate let's assign a new list to things for bar2:
bar2.things = []
This results in:
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'm from Barcelona, (Spain). I own:
You're two versions of the code are very different. In python, you have 2 distinct entities: classes and class instances. An instance is what is created when you do:
new_instance = my_class()
You can bind attributes to an instance within __init__ via self (self is the new instance).
class MyClass(object):
def __init__(self):
self.country = "" #every instance will have a `country` attribute initialized to ""
There's nothing terribly special about self and __init__. self is the customary name that is used to represent the instance that gets passed to every method (by default).
a.method() #-> Inside the class where `method` is defined, `a` gets passed in as `self`
The only thing special here is that __init__ gets called when the class is constructed:
a = MyClass() #implicitly calls `__init__`
You can also bind attributes to the class (putting it outside __init__):
class MyClass(object):
country = "" #This attribute is a class attribute.
At any point, you can bind a new attribute to an instance simply by:
my_instance = MyClass()
my_instance.attribute = something
Or a new attribute to a class via:
MyClass.attribute = something
Now it gets interesting. If an instance doesn't have a requested attribute, then python looks at the class for the attribute and returns it (if it is there). So, class attributes are a way for all instances of a class to share a piece of data.
Consider:
def MyClass(object):
cls_attr = []
def __init__(self):
self.inst_attr = []
a = MyClass()
a.inst_attr.append('a added this')
a.cls_attr.append('a added this to class')
b = MyClass()
print (b.inst_attr) # [] <- empty list, changes to `a` don't affect this.
print (b.cls_attr) # ['a added this to class'] <- Stuff added by `a`!
print (a.inst_attr) #['a added this']
When you define a variable in class scope (outside any method), it becomes a class attribute. When you define a value in method scope, it becomes a method local variable. If you assign a value to an attribute of self (or any other label referencing an object), it becomes (or modifies) an instance attribute.

Categories