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.
Related
Consider this example:
def A():
b = 1
def B():
# I can access 'b' from here.
print(b)
# But can i modify 'b' here?
B()
A()
For the code in the B function, the variable b is in a non-global, enclosing (outer) scope. How can I modify b from within B? I get an UnboundLocalError if I try it directly, and using global does not fix the problem since b is not global.
Python implements lexical, not dynamic scope - like almost all modern languages. The techniques here will not allow access to the caller's variables - unless the caller also happens to be an enclosing function - because the caller is not in scope. For more on this problem, see How can I access variables from the caller, even if it isn't an enclosing scope (i.e., implement dynamic scoping)?.
On Python 3, use the nonlocal keyword:
The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.
def foo():
a = 1
def bar():
nonlocal a
a = 2
bar()
print(a) # Output: 2
On Python 2, use a mutable object (like a list, or dict) and mutate the value instead of reassigning a variable:
def foo():
a = []
def bar():
a.append(1)
bar()
bar()
print a
foo()
Outputs:
[1, 1]
You can use an empty class to hold a temporary scope. It's like the mutable but a bit prettier.
def outer_fn():
class FnScope:
b = 5
c = 6
def inner_fn():
FnScope.b += 1
FnScope.c += FnScope.b
inner_fn()
inner_fn()
inner_fn()
This yields the following interactive output:
>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined
I'm a little new to Python, but I've read a bit about this. I believe the best you're going to get is similar to the Java work-around, which is to wrap your outer variable in a list.
def A():
b = [1]
def B():
b[0] = 2
B()
print(b[0])
# The output is '2'
Edit: I guess this was probably true before Python 3. Looks like nonlocal is your answer.
No you cannot, at least in this way.
Because the "set operation" will create a new name in the current scope, which covers the outer one.
I don't know if there is an attribute of a function that gives the __dict__ of the outer space of the function when this outer space isn't the global space == the module, which is the case when the function is a nested function, in Python 3.
But in Python 2, as far as I know, there isn't such an attribute.
So the only possibilities to do what you want is:
1) using a mutable object, as said by others
2)
def A() :
b = 1
print 'b before B() ==', b
def B() :
b = 10
print 'b ==', b
return b
b = B()
print 'b after B() ==', b
A()
result
b before B() == 1
b == 10
b after B() == 10
.
Nota
The solution of Cédric Julien has a drawback:
def A() :
global b # N1
b = 1
print ' b in function B before executing C() :', b
def B() :
global b # N2
print ' b in function B before assigning b = 2 :', b
b = 2
print ' b in function B after assigning b = 2 :', b
B()
print ' b in function A , after execution of B()', b
b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b
result
global b , before execution of A() : 450
b in function B before executing B() : 1
b in function B before assigning b = 2 : 1
b in function B after assigning b = 2 : 2
b in function A , after execution of B() 2
global b , after execution of A() : 2
The global b after execution of A() has been modified and it may be not whished so
That's the case only if there is an object with identifier b in the global namespace
The short answer that will just work automagically
I created a python library for solving this specific problem. It is released under the unlisence so use it however you wish. You can install it with pip install seapie or check out the home page here https://github.com/hirsimaki-markus/SEAPIE
user#pc:home$ pip install seapie
from seapie import Seapie as seapie
def A():
b = 1
def B():
seapie(1, "b=2")
print(b)
B()
A()
outputs
2
the arguments have following meaning:
The first argument is execution scope. 0 would mean local B(), 1 means parent A() and 2 would mean grandparent <module> aka global
The second argument is a string or code object you want to execute in the given scope
You can also call it without arguments for interactive shell inside your program
The long answer
This is more complicated. Seapie works by editing the frames in call stack using CPython api. CPython is the de facto standard so most people don't have to worry about it.
The magic words you are probably most likely interesed in if you are reading this are the following:
frame = sys._getframe(1) # 1 stands for previous frame
parent_locals = frame.f_locals # true dictionary of parent locals
parent_globals = frame.f_globals # true dictionary of parent globals
exec(codeblock, parent_globals, parent_locals)
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only
The latter will force updates to pass into local scope. local scopes are however optimized differently than global scope so intoducing new objects has some problems when you try to call them directly if they are not initialized in any way. I will copy few ways to circumvent these problems from the github page
Assingn, import and define your objects beforehand
Assingn placeholder to your objects beforehand
Reassign object to itself in main program to update symbol table: x = locals()["x"]
Use exec() in main program instead of directly calling to avoid optimization. Instead of calling x do: exec("x")
If you are feeling that using exec() is not something you want to go with you can
emulate the behaviour by updating the the true local dictionary (not the one returned by locals()). I will copy an example from https://faster-cpython.readthedocs.io/mutable.html
import sys
import ctypes
def hack():
# Get the frame object of the caller
frame = sys._getframe(1)
frame.f_locals['x'] = "hack!"
# Force an update of locals array from locals dict
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
ctypes.c_int(0))
def func():
x = 1
hack()
print(x)
func()
Output:
hack!
I don't think you should want to do this. Functions that can alter things in their enclosing context are dangerous, as that context may be written without the knowledge of the function.
You could make it explicit, either by making B a public method and C a private method in a class (the best way probably); or by using a mutable type such as a list and passing it explicitly to C:
def A():
x = [0]
def B(var):
var[0] = 1
B(x)
print x
A()
For anyone looking at this much later on a safer but heavier workaround is. Without a need to pass variables as parameters.
def outer():
a = [1]
def inner(a=a):
a[0] += 1
inner()
return a[0]
You can, but you'll have to use the global statment (not a really good solution as always when using global variables, but it works):
def A():
global b
b = 1
def B():
global b
print( b )
b = 2
B()
A()
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.
is there any difference between these two method?
option1 :
class a(object):
def __init__(self):
self.x = 123
self.y = 345
option2 :
class a(object):
x = 123
y = 345
is there any difference between these two options?
Thanks in advance.
An example of the first method (instance level variables):
instance0 = a()
instance1 = b()
instance0.x = 5
print instance1.x # prints 123
print a.x # undefined variable - x is not defined
An example of the second method (class level variables):
instance0 = a()
instance1 = b()
instance0.x = 5
print instance1.x # prints 5
print a.x # prints 5
The second method, the variables are assigned at the class level meaning changing this value propagates to all instances of that class. You can also access the variables without an instance of the class.
Yes, in the first case each object of class a has its own copy of x and y, in the second case all objects of class a share them.
By the way, if your starting out with Python, use a capital for the first character of your class names, e.g. MyClass. People are used to that and it will help you understand your own programs once they get bigger.
This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
class abc :
x = 10
list = []
def __init__(self):
self.a = 30
self.b = 40
a = abc()
b = abc()
a.x = a.x + 1
print a.x
print b.x
a.list.append(1)
print b.list
Output :
10
11
[1]
So we see that x is not shared across objects a and b but list is shared. Can someone explain this behaviour?
So its appears answer to this lies in the fact that list are mutable objs and numbers are not:
class abc :
x = 10
m_list = []
def __init__(self):
self.a = 30
self.b = 40
a = abc()
b = abc()
print id(a.x)
a.x = a.x + 1
print id(a.x)
print a.x
print b.x
print id(a.m_list)
a.m_list.append(1)
print id(a.m_list)
print b.m_list
print id(b.m_list)
output :
5342052
5342040
11
10
38600656
38600656
[1]
38600656
but this is so strange ...numbers are immutable ?
They are both shared across all instances of the class, however the difference is that lists are mutable, but integers are immutable.
This:
a.x = a.x + 1
creates a new immutable int object* then points a.x (but not b.x) to the new object.
By contrast, this:
a.list.append(1)
modifies the mutable list object a.list (and b.list) was already pointing to in-place.
You can modify immutable class attributes more consistently using e.g.
abc.x = abc.x + 1
abc.list.append(1)
And don't call your own variable list!
* small integers are "interned" in CPython, but let's not worry about that for now
a list is a mutable datastructure since you are creating it at the class level it works simillar to static variables in other languages.... however since integers are not mutable when you change a.x you are actually creating a new a.x value and not affecting b.x
class abc :
x = 10
def __init__(self):
self.a = 30
self.my_list = [] #now its tied to the instance rather than the class
self.b = 40
you can actually see that a.x is not the same object after incrementing it
>>> id(a.x)
5321432
>>> a.x += 1
>>> id(a.x)
5321420 #notice the address of the object is different
It has nothing to do with lists, but with the fact that, in your example, a.x is updated and that creates an instance-level variable which is not anymore shared amongst instances.
But the same thing happens to the list, if you update it. To prove my assertion, I will make use of the id() primitive which returns object's unique IDs:
class abc :
x = 10
my_list = []
def __init__(self):
self.a = 30
self.b = 40
a = abc()
b = abc()
print "id(a.x) = %d" % id(a.x) # identifier of a.x
print "id(b.x) = %d" % id(b.x) # identifier of b.x = a.x
a.x = a.x + 1
print "id(a.x) = %d" % id(a.x) # changed
print "id(b.x) = %d" % id(b.x) # unchanged
print a.x
print b.x
a.my_list.append(1)
print b.my_list
print "id(a.mylist) = %d" % id(a.my_list)
print "id(b.mylist) = %d" % id(b.my_list)
a.my_list = [42]
print "id(a.mylist) = %d" % id(a.my_list) # changed
print "id(b.mylist) = %d" % id(b.my_list)
Which gives:
id(a.x) = 154513476
id(b.x) = 154513476
id(a.x) = 154513464 # changed
id(b.x) = 154513476
11
10
[1]
id(a.mylist) = 3072656236
id(b.mylist) = 3072656236
3072781100
3072656236
id(a.mylist) = 3072781100 # changed
id(b.mylist) = 3072656236
As an aside, I have renamed the list variable. Please don't use reserved words as variable names.
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