Python - Sharing variables between different instances of different classes [duplicate] - python

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.

Related

Self vs return in Python Classes for Class Attribute

Suppose I have a class attribute in the form of list, and two functions inside that class that append data onto the list for further operations on the list.
Would this be a proper way to write the same:
class Sample:
def __init__(self):
self.Mylist = []
self.customfunc1()
self.customfunc2()
self.customfunc3()
def customfunc1(self):
somevar = 1
self.Mylist.append(somevar)
def customfunc2(self):
somevar = 2
self.Mylist.append(somevar)
def customFunc3(self):
## Operations on self.Mylist
Or Do I need to return a value after every sub function. I'm new to using 'self' reference so any help appreciated.
It all depends on the intend. When you look, for example, at the standard library (list.append), the convention is that when a method mutates its argument, it does not return anything (i.e. it implicitly returns None). The idea is to make it really obvious that the data was changed.
If, other other hand, the data does not change but you create a new instance, then, well, you don't really have any other option that to return the new instance.
For you example, it is a bit hard to tell as it is a bit artificial but since MyList is an internal implementation detail of the class, I would definitely not return (or the value).
PS: In your example, there are only "instance variables", not "class attribute[s]" - for that the fields would have to be declared one scope above.

Setting class variable value using self. versus classname

In python class (say class C) lets say we have class variable V = 0. Lets say this is a counter that keeps track of how many objects of the class have been created.
In the init methid, say we want to fetch the class variable value. We can do this either by writing C.V or self.V - assuming both do exactly the same thing.
To set the value of this class variable, there are same 2 options so what is the difference of using:
C.V += 1 versus self.V += 1
Is it that using self will update the variable at that object level and other objects wont get the change? Or do both approach behave the same.
Setting C.V refers to the class's definition. This will set the value for all objects of the same class. Using self refers to the current instance, or object, of that class. This will not affect other objects of the same class.
EDIT -- Credits to #Jdw136
And adding on to what #CalderWhite said, when you use a normal variable in a class (C.V), that variable is shared among all instances of that class. On the other hand when you use the self. Method (self.V), each instance of that object gets its own variable all to itself. Keeping this in mind, you can use this knowledge to avoid creating the same variable in the stack over and over.

Python new list not created upon object creation [duplicate]

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

Class member variables are not treated locally [duplicate]

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.

A private list variable is inadvertently shared between instance objects

I created many instances of a PlotHandler class. An instance must keep it's variables private. But the way I managed them led to a hard to detect problem, a private list variable is shared between instances! And that too without any obvious source for the leak.
My debugging told me that the private member function that modifies the list sees the same list, even if they are different objects.
Is this a "gotcha" problem? What is the best way to troubleshoot this?
Here are the relevant parts (I hope they are!) of the implementation. Please see the ALL-CAPS comments:
The file implementing PlotHandler:
class PlotHandler(wx.Frame):
__crop_section = None
__projection = None
__crop_xcord = None
_band_data = [] #THIS GETS SHARED
def _on_plot_click(self, xcord): #CALLED BY ANOTHER OBJECT
band = self._analyze_band( xcord )
self._band_data.append(band)
...
The parent class that it is managing PlotHandlers:
class MainFrame(wx.Frame):
__close_callback__ = None
_plot_handlers = []
def __init__(self, parent, title):
...
def InitUI(self):
...
img_handler = ImageHandler(panel)
self.img_src.register_callback( img_handler.update_image )
#you need to call PlotHandler(parent, cropped)
img_handler.register_sample_callback( self._create_new_plot_handler )
...
def _create_new_plot_handler(self, cropped_sample ):
self._plot_handlers.append( PlotHandler(self, cropped_sample) ) #CREATE THEM
See this question, this one, and tons of other stuff you can find by googling "Python class variables shared", "Python FAQ class variables", etc.
The short answer is: variables defined directly in the class body are class variables, not instance variables, and are thus shared among instances of the class. If you want instance variables you must assign them from within a method, where you have access to self.
Class attributes are shared between instances. If you want to define an instance attribute (so each object have its own reference to the variable) you have to define it in __init__
class PlotHandler(wx.Frame):
__crop_section = None
__projection = None
__crop_xcord = None
def __init__(self, **kwargs):
self._band_data = [] #THIS IS NOT SHARED

Categories