class variables is shared across all instances in python? [duplicate] - python

This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 4 years ago.
I started coding in python a week ago, it is my mistake i started coding using oops,classes and objects that soon. I assumed my C++ proficiency will help.... I got bit by the following code
class A:
var=0
list=[]
def __init__(self):
pass
Here to my surprise, var and list are kinda global variable, it is shared across all instances it seems.... What I thought was it was different across all the instances..... It took me half a day to figure out that.... It does not make even slightest sense, that a variable can be accessed by a class object only, but is shared across all instances....... Just Curious, is there a reason behind it?????

var should definitely not be shared as long as you access it by instance.var or self.var. With the list however, what your statement does is when the class gets evaluated, one list instance is created and bound to the class dict, hence all instances will have the same list. Whenever you set instance.list = somethingelse resp. self.list = somethingelse, it should get an instance level value.
Example time:
>>> class A():
... var = 0
... list = []
...
>>> a = A()
>>> b = A()
>>> a.var
0
>>> a.list
[]
>>> b.var
0
>>> b.list
[]
>>> a.var = 1
>>> b.var
0
>>> a.list.append('hello')
>>> b.list
['hello']
>>> b.list = ['newlist']
>>> a.list
['hello']
>>> b.list
['newlist']

These are basically like static variables in Java:
// Example equivalent Java
class A {
static int var = 0;
static String[] list;
}
This is the intended behavior: Class variables are for the class.
For normal instance variables, declare them in the constructor:
class A:
def __init__(self):
self.var = 0
self.list = []
You may want to look at Static class variables in Python.

The reason is that in Python class is an executable statement that is executed like any other. The body of the class definition is executed once, when the class is defined. If you have a line line var = [] in there, then it is only executed once, so only one list can ever be created as a result of that line.

Note that you do not see this behaviour for var, you only see it for list:
>>> class A:
... var = 0
... list = []
...
>>> a1 = A()
>>> a2 = A()
>>> a1.var = 3
>>> a2.var
0

Related

Function that acts on a variable as said variable's attribute

EDIT: Figured it out:
class i(int):
def __init__(self, __int: int):
self.__int = __int
def __times_two__(self):
return self.__int * 2
v = i(3)
print(v.__times_two__())
>>> 6
Forgive my lack of terminology, not too sure how to word this question (possibly why I've had so much trouble finding an answer). Here's an example of what I would like to do:
def times_two(x):
return x*2
class MyClass:
_1=1
_2=2
MyClass._2.times_two()
>>> 4
This isn't literally what I want, but I basically want to have a function that can act on a given variable and return the result of an action performed on that variable. This may just not be possible, but any help would be greatly appreciated.
you could use a dictionary instead of a class, and feed into your function whatever is inside it, for example:
>>> def times_two(x):
return x*2
>>> data={"_1":1, "_2":2}
>>> times_two(data["_1"])
2
>>> times_two(data["_2"])
4
>>>
about your peculiar solution, if you in turn want that the result to also have this extra method, you need to make sure is an instance of your personalize class
>>> class int2(int):
def times_two(self):
return type(self)(self*2) #with this the result is an int2
>>> i=int2(1)
>>> i
1
>>> i.times_two().times_two()
4
>>> type(_)
<class '__main__.int2'>
>>>

class variable oddities without self [duplicate]

This question already has answers here:
Is accessing class variables via an instance documented?
(2 answers)
Closed 6 years ago.
I'm trying to grasp on the idea on how the class variables work. And to my knowledge class variables are shared between the class instances (objects).
So within THAT idea, if I change the class variable, the value should change to all the class instances...
...BUT, this seems not to be always the case.
Below is an simplified example:
class A:
name = "Dog"
a1 = A()
a2 = A()
# These both change the value only for an instance
a1.name = "Cat"
a2.name += " is an animal"
print(a1.name, a2.name)
class B:
number = 0
b1 = B()
b2 = B()
# Again only changes value for an instance
b1.number = 2
print(b1.number, b2.number)
# This is the weird one.
class C:
lista = []
c1 = C()
c2 = C()
# Changes the value for BOTH/ALL instances.
c1.lista.append(5)
c2.lista.append(6)
print(c1.lista, c2.lista)
# But this one only changes the value for an instance.
c1.lista = [1, 2, 3]
print(c1.lista, c2.lista)
Class variables are shared between instances until the moment you assign to an instance variable by the same name. (As an aside, this behavior is useful for declaring defaults in inheritance situations.)
>>> class X:
... foo = "foo"
...
>>> a = X()
>>> b = X()
>>> c = X()
>>> c.foo = "bar"
>>> id(a.foo)
4349299488
>>> id(b.foo)
4349299488
>>> id(c.foo)
4349299824
>>>
Your list example mutates the shared instance first, then reassigns a new value to c1.lista, but c2.lista remains the shared instance.

Is it possible to set an instance variable for a function in python?

Lets say I have a function with a variable duration inside it.
Is there any way to set the Duration` value outside of the function in some other nonparent function without adjusting any parameter?
With Python 3.x you can declare it with the nonlocal keyword
def make_slow(action):
slowDuration = None
def slowAction(self, colony):
nonlocal slowDuration
slowDuration = 10 # It is changing the variable from the scope above
If you want to change a value from somewhere else and you mustn't return the value, try going global... Note this may pollute your current namespace.
For a more pythonic approach, you should use something like self.slowDuration. That's what objects are for.
slowDuration is a local variable of the slowAction function. The point of local variables is that they are only accessible inside the function.
You should change the slowAction function so it uses a slowDuration variable that is defined somewhere else, for example as a member variable of the class that make_slow apparently belongs to.
You can also make slowAction be an instance of a class that overrides the __call__ method.
>>> class Counter:
... def __init__(self):
... self.count = 0
... def __call__(self, delta):
... self.count += delta
... print(self.count)
... def set_count(self, c):
... self.count = c
...
>>> c = Counter()
>>> c(1)
1
>>> c(3)
4
>>> c(3)
7
>>> c(3)
10
>>> c.set_count(42)
>>> c(-2)
40
You could also use some trickery to make the shared variable available on the function object itself:
def makeCounter():
counter = None
def counter_func():
counter.count += 1
print(counter.count)
counter = counter_func
counter.count = 0
return counter
and use it like this:
>>> c = makeCounter()
>>> c()
1
>>> c()
2
>>> c()
3
>>> c()
4
>>> c.count = 42
>>> c()
43
>>> c()
44
>>> c()
45
But in general, "clever" code like that should be avoided unless you have a very good reason to use it, because it makes the code base harder to understand.

Class variable Vs. Instance variable in python for int value

I am new to python and i am not sure how this is working. Code is as below:
class test():
d=0
def __init__(self):
self.d=self.d+1;
D=test()
print D.d
D1=test()
print D1.d
D2=test()
print D2.d
Output is
1,1,1 # This should not be
Now using this :
class test():
d=[]
def __init__(self):
self.d.apend("1");
D=test()
print D.d
D1=test()
print D1.d
D2=test()
print D2.d
Result is (This should be)
['1']
['1', '1']
['1', '1', '1']
So i am not sure why integer value is not being treated as class variable while list is being treated.
In the first example,
self.d = self.d + 1
rebinds self.d, making it independent of test.d.
In the second example,
self.d.append("1")
modifies test.d.
To see that for yourself, print id(self.d) at the end of both constructors.
If you modified the second example to match the first:
self.d = self.d + ["1"]
you'd see that the behaviour would also change to match.
If you want to modify a class variable, do:
class test(object):
d=0
def __init__(self):
type(self).d=self.d+1;
D=test()
print D.d
D1=test()
print D1.d
D2=test()
print D2.d
You don't need the type on the right hand side of the assignment, because this way you never create an instance variable d. Note that new-style classes are necessary to this.
type is a function (actually a callable - it is also a class; but don't worry about that for now) which returns the class of its argument. So, type(self) returns the class of self. Classes are first class objects in Python.
Demo here: http://ideone.com/JdNpiV
Update: An alternative would be to use a classmethod.
To address a class variable use class_name.variable_name, giving :
class test(object):
d=0
def __init__(self):
test.d = test.d + 1;
NPE's answer tells you what is going wrong with your code. However, I'm not sure that it really tells you how to solve the issue properly.
Here's what I think you want, if each test instance should have a different d value in an instance variable:
class test(object): # new style class, since we inherit from "object"
_d = 0 # this is a class variable, which I've named _d to avoid confusion
def __init__(self):
self.d = test._d # assign current value of class variable to an instance variable
test._d += 1 # increment the class variable
Now, you can create multiple instances and each one will get a unique value for d:
>>> D0 = test()
>>> D1 = test()
>>> D2 = test()
>>> print D0.d
0
>>> print D1.d
1
>>> print D2.d
2

Prevent member objects referencing the same list

This should be simple...
class Object:
x = 0
y = []
a = Object()
b = Object()
a.x = 1
b.x = 2
print a.x, b.x
# output: 1 2
# works as expected
a.y.append(3)
b.y.append(4)
print a.y, b.y
# output: [3, 4] [3, 4]
# same list is referenced how to fix?
# desired output: [3] [4]
As far as I can tell, a.y and b.y reference the same list. How can I get them to be separate? Preferably, without adding an __init__ method.
You're creating the value of y only once, when you define the class. To assign a different list to each instance of y, you do need an init function. Just put self.y = [] in __init__ and it will work as intended.
What's happening here is that you actually have actually redefined x as an instance level attribute, and your class definition had them as both class level attributes.
If you do this, you can see your original x is still at 0.
>>> Object.x
0
As you don't create a new list, it's taking the class attribute. If you were to do this:
>>> a.y = []
>>> b.y = []
>>> a.y.append(1)
>>> b.y.append(2)
>>> print a.y, b.y
[1] [2]
That is what you are expecting. Really though you should be defining your class like this:
class Object(object):
def __init__(self):
self.y = []
self.x = 0
(and don't use Object as a classname!)
The easiest way to setup instance properties instead of class properties is to use __init__
When you reference an instance property (like a.y) the parser tries to return that first but if it isn't found the class property (Object.y) is returned.
In your case only defined a class property which is shared by all instances.
The only way to do that is creating the __init__ method
class Object:
def __init__(self):
self.x = 0
self.y = []
That way upon Object's construction, a new value will be assined to x and a new List will be created for y.
The way you were doing before creates two class/static variables to Object, but only y stays the same because it holds statically only a reference to the true List, reflecting to all instances of Object.
More on class/static variables on this other question:
Static class variables in Python
*Sorry if I used the wrong terms, I'm more of a Java person ;-)

Categories