This question already has answers here:
Weird list behavior in class
(4 answers)
Closed 9 years ago.
I'm doing some practicing with OOP in python and I've run into an issue that my non-computer scientist mind cannot comprehend. I'm sure it's just due to my inexperience with OO but I can't seem to find an answer for it anywhere.
So I've got three classes. A class called tester, which should contain a unique object called group, which should contain a list of objects called atom. My issue is that whenever I create multiple groups they all seem to have the same list object. So whenever I append an atom to the list it gets appended to all the group's lists. My code is:
count = 0
testers = []
class atom:
def __init__(self):
pass
class group:
myList = list()
def __init__(self):
pass
def createAtom(self):
self.myList.append(atom())
class tester:
def __init__(self):
self.myGroup = group()
for k in range(4):
testers.append(tester())
print testers[k].myGroup
for t in testers:
t.myGroup.createAtom()
print t.myGroup.myList
I would expect this to create a new list for each group and that this would add a single atom to each group. This instead creates an output as follows.
<__main__.group instance at 0x02C2E058>
<__main__.group instance at 0x02C2E0A8>
<__main__.group instance at 0x02C2E0F8>
<__main__.group instance at 0x02C2E148>
[<__main__.atom instance at 0x02C2E170>]
[<__main__.atom instance at 0x02C2E170>, <__main__.atom instance at 0x02C2E198>]
[<__main__.atom instance at 0x02C2E170>, <__main__.atom instance at 0x02C2E198>, <__main__.atom instance at 0x02C2E1C0>]
[<__main__.atom instance at 0x02C2E170>, <__main__.atom instance at 0x02C2E198>, <__main__.atom instance at 0x02C2E1C0>, <__main__.atom instance at 0x02C2E1E8>]
A single list gets all four atoms. I apologize for my likely poor code. If it's of any help, I'm using python portable 2.7.5.1. Any insight into this would be greatly appreciated.
Thanks
Your list is a class attribute, shared amongst all instances:
class group:
myList = [] # class attribute
def __init__(self):
pass
Instead, make it an instance attribute, separate for each instance of the class:
class group:
def __init__(self):
self.myList = [] # instance attribute
Note that I have replaced list() with [], per thefourtheye's comment. It is bad practice to shadow built-ins (e.g. having your own list or other object named list), but this avoids side effects if the rule gets broken.
You've made group.myList a class attribute, shared by all instances.
class group:
#myList = list() # <--- this defines a 'class' attribute
# which is shared by all instances of 'group'
def __init__(self):
self.myList = list() # <--- do this instead to create an instance attribute
def createAtom(self):
self.myList.append(atom())
Move the mylist = list() in class group into the __init__ of class group.
Doing so would make group create a new list every time a new group instance is created. Otherwise, all you've done is create a class-level variable (not instance-level), which will be shared among all instances of the same class.
Think of class variables as sort of a "hive mind" (think of The Borg from Star Trek) structure for all instances of that class. Any changes made to a class variable will be visible to all objects of that type.
On the other hand, if you were to create an instance variable (a variable initialized in __init__), then each instance would have its own value for that variable. Thus any changes that one instance makes to its variable will be invisible to other instances of the same type
Related
This question already has an answer here:
Python: How to share data between instances of different classes?
(1 answer)
Closed 4 years ago.
I have been searching for the next answer but for sure I have been searching the wrong keywords.
I used to develop with C++, passing pointers as references between objects. The case is, now I'm trying to build a program in Python where one instance of a class 'General' initializes different instances of a class 'Specific' with the same shared variable.
class General():
def __init__(self):
self._shared_variable = 0
self._specific1 = Specific(self._shared_variable)
self._specific2 = Specific(self._shared_variable)
class Specific():
def __init__(self, shared):
self._shared = shared
def modify_shared_variable(self):
self._shared_variable +=1
So what I'm trying to do is shared this 'shared_variable' within de General scope, so when a 'Specific' instance modifies his internal variable, this change is seeing or mirrored by the other instance. But this is not the case in python. So, every specific instance has its own variable. How can I achieve this?
You can't have references to variables in Python. A variable is just a name, in some namespace (usually the __dict__ of a module, class, or instance object, or the special local namespace inside a function-call frame), for a value.
You can have references to values, but of course numbers are immutable values, so you can't change the number 1 into the number 2.
So, what you can do is create some kind of mutable value that holds the number, and share references to that.
One obvious possibility is to just give each Specific instance a reference to the General instance that created it:
class General():
def __init__(self):
self._shared_variable = 0
self._specific1 = Specific(self)
self._specific2 = Specific(self)
class Specific():
def __init__(self, shared_general):
self._shared_general = shared_general
def modify_shared_variable(self):
self._shared_general._shared_variable +=1
Another possibility is to store a single-element list:
class General():
def __init__(self):
self._shared_variable = [0]
self._specific1 = Specific(self._shared_variable)
self._specific2 = Specific(self._shared_variable)
class Specific():
def __init__(self, shared):
self._shared = shared
def modify_shared_variable(self):
self._shared[0] += 1
(This is really the same thing you're doing in C++, but without the syntactic sugar of arrays and pointers being nearly the same thing…)
Or you can create a simple MutableInteger class that holds an int, proxies non-mutating methods to it, adds a set method to replace it, and handles += and other mutating methods by calling set and returning self, instead of returning a new value.
What's the lifetime of a class attribute, in Python? If no instances of the class are currently live, might the class and its class attributes be garbage-collected, and then created anew when the class is next used?
For example, consider something like:
class C(object):
l = []
def append(self, x):
l.append(x)
Suppose I create an instance of C, append 5 to C.l, and then that instance of C is no longer referenced and can be garbage-collected. Later, I create another instance of C and read the value of C.l. Am I guaranteed C.l will hold [5]? Or is it possible that the class itself and its class attributes might get garbage-collected, and then C.l = [] executed a second time later?
Or, to put it another way: Is the lifetime of a class attribute "forever"? Does a class attribute have the same lifetime as a global variable?
You asked several questions.
What's the lifetime of a class attribute, in Python?
A class attribute lives as long as there is a reference to it. Since the class holds a reference, it will live as at least long as the class lives, assuming that the class continues to hold the reference. Additionally, since each object holds a reference, it will live at least as long as all of the objects, assuming that each object continues to hold the reference.
Am I guaranteed C.l will hold [5]?
In the hypothetical that you describe, yes.
Or is it possible that the class itself and its class attributes might get garbage-collected, and then C.l = [] executed a second time later?
Not given your hypothetical that you are able to construct an instance of C. If you are able to construct a second instance of C, then C must exist, and so too must C.l
Is the lifetime of a class attribute "forever"?
No. The lifetime of a class attribute follows the lifetime rules of any object. It exists as long a reference to it exists. In the case of C.l, a reference exists in the class, and a reference exists in each instance. If you destroy all of those, then C.l will also be destroyed.
Does a class attribute have the same lifetime as a global variable?
Sort of: a class attribute exists until the last reference goes away. A global variable also exists until the last reference goes away. Neither of these are guaranteed to last the entire duration of the program.
Also, a class defined at module scope is a global variable. So the class (and, by implication, the attribute) have the same lifetime as a global variable in that case.
If no instances of the class are currently live, might the class and its class attributes be garbage-collected, and then created anew when the class is next used?
No. There is no "next use" of a class that's been garbage-collected, because the only way it can be garbage-collected is if there's no way left to use it.
Suppose I create an instance of C, append 5 to C.l, and then that instance of C is no longer referenced and can be garbage-collected. Later, I create another instance of C and read the value of C.l. Am I guaranteed C.l will hold [5]?
Yes. The class has a live reference because you created another instance, and the list has a live reference as an attribute on the class.
Does a class attribute have the same lifetime as a global variable?
If the class holds a reference to something, that something will live at least as long as the class.
I am new to python and trying to learn.
Problem
I am stuck at a place where I initialize and object of a class in the loop. During this initialization I expect that I get a brand new object with all its attribute reset. But that doesn't happen when for the array list the class has.
Example below:
Class MyClass:
id=""
aList=[]
Class SecondClass: # ignore its content
pid=""
anObj=MyClass()
sc1=SecondClass()
anobj.append(sc1)
print str(len(anObj.aList) # gives the output 1
anObj=MyClass() # reinitalizing the object, so I expect on the lines of java that I get an empty class
print str(len(anObj.aList) # gives the output 1, why I just reinitialized the object "anObj"
What I want is after I do anObj=MyClass() all the attributes should be reset. Like java. It seems like anObj (array ) is treated as static variable of class (using Java language)
Problem at deeper depth
I don't want to do anObj.aList=[] explicitly because my issue is some thing like in the below code
aCollection=[]
for x in (0,3):
anObj=MyClass()
sc=getSecondClassObjectWithDifferentValues()
anobj.append(sc)
aCollection.append(anOb)
I am putting anObj in aCollection, eventually I would like to access them in the state I put it.
Thanks in advance for the help
You are confusing static properties with instance property. You should be doing this instead:
Class MyClass:
def __init__(self):
self.id = ""
self.aList = []
The fundamental difference is that, in your implementation, the property aList will be the same for all instances of MyClass. This is why we call them static, because they do not change from instance to instance. In contrast, an instance variable defined as above will be unique for each new instance you create.
This is a super common misunderstanding with python ... effectively, MyClass.aList is a "static" member. The key to understanding this is to understand how python looks up attributes on an object.
First, python look at the instance for the attribute. If it isn't there, then python moves up a level (to the class) and looks for the attribute there. If it isn't on the class, it'll look at the base classes (in the "Method Resolution Order").
So, this (hopefully) explains the problem. you create an instance of MyClass and get a reference to it's list:
c = MyClass()
lst = c.aList
Now, note, c.aList is MyClass.aList because c doesn't have an aList attribute of it's own.
print(lst is MyClass.aList) # True
So, how do we resolve this? The typical fix for this is to bind the aList attribute to the instance at initialization time:
class MyClass(object):
def __init__(self):
self.aList = []
Now, MyClass doesn't have an aList member, but all of it's instances will (and their members will all be distinct).
This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 9 years ago.
Look at the code below:
class Node:
feature = list()
label = list()
def __init__(self, f, l):
self.feature.append(f)
self.label.append(l)
I create two instances of this class:
n1 = Node(1,2)
print n1.feature
n2 = Node(3,4)
print n2.feature
My desired output is:
1
2
But the real output is:
1
1 2
What is the problem? How can I fix it?
variables defined in class scope are class variables, and are share among all class instances (they are stored on the class object itself, not on the instances).
Just initialize the variables inside the init function.
class Node:
def __init__(self, f, l):
self.feature = [f]
self.label = [l]
The issue is that you're trying to "declare" the member data for the class instances in the class block. That's not how Python works.
Everything defined in the class block (including feature, label, and __init__) becomes an attribute of the object that represents the class itself, not the instances of the class. Trying to access an attribute that doesn't exist in an instance will fall back to the class, which is how method lookup works.
There is no way to create a attribute on an instance until it exists and you have a reference to it. The purpose of the __init__method is to give you a place to do that. So initial values for an instance's member data should be set up in __init__; trying to declare these initial values in the class block actually does something else.
While researching about python class attribute and instance attribute, I came to know that it's not possible to create object attribute outside object methods (or may be class method). Like code below will generate an "NameError" in python.
class test(object):
def __init__(self):
self.lst = []
self.str = 'xyz'
Why python doesn't allow this? I'm not questioning language creator's decision, but any reason behind this. Like, is it technically incorrect or any other disadvantage of this behavior.
You are defining a class, so there is no instance to point to outside methods. Drop the `self:
class test(object):
def __init__(self):
self.lst = []
str = 'xyz'
self points to the instance, not the class. You either need to create an instance and assign directly to attributes (test().str = 'xyz') or you need to be inside a method (when self can actually refer to an instance).
self is not a special name in python, you could use \
class test(object):
def __init__(foo):
foo.lst = []
If you want. Every method of a class gets the instance explicitly passed to it as the first parameter, you can call it whatever you want. Trying to access a parameter outside the scope of the method obviously won't work.