I have a ModelManager which keeps track of creating and destroying new objects. Here's an example:
class ModelManager:
MAX_OBJECTS = 10
OBJECTS = {} # hash to model object
NUM_OBJECTS = len(OBJECTS) # how to dynamically calculate this?
Every time an object is created it is added to OBJECTS and everytime it is deleted it gets popped from OBJECTS.
How would I properly do the NUM_OBJECTS here? Ideally it should be a classmethod/property to act as a calculation. For doing something like the above, what would be the best way?
I would like to call it as ModelManager.NUM_OBJECTS
Use a computed property
class ModelManager:
#property
def NUM_OBJECTS(self):
return len(self.OBJECTS)
Also, note that OBJECTS will be shared across your class instances because it is a dictionary initialized at class scope. The NUM_OBJECT property requires initializing the class. If you want NUM_OBJECTS to be a property of the class, use one of the solutions suggested here for class properties.
If you would rather be able to call len around your class (in addition to NUM_OBJECTS) - you can overkill with metaclasses:
class ModelManagerMeta(type):
def __len__(cls):
return len(cls.OBJECTS)
def NUM_OBJECTS(cls):
return len(cls.OBJECTS)
class ModelManager(metaclass=ModelManagerMeta):
MAX_OBJECTS = 10
OBJECTS = {} # hash to model object
...
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.
I have a question regarding return convention in Python. I have a class that has some data attributes and I have several functions that perform some analysis on the data and then store the results as results attributes (please see the simplified implementation below). Since, the analysis functions mainly update the results attribute, my question is what is the best practice in terms of return statements. Should I avoid updating class attributes inside the function (as in process1), and just return the data and use that to update the results attribute (as in process2)?
Thanks,
Kamran
class Analysis(object):
def __init__(self, data):
self.data = data
self.results = None
def process1(self):
self.results = [i**2 for i in self.data]
def process2(self):
return [i**2 for i in self.data]
a = Analysis([1, 2, 3])
a.process1()
a.results = a.process2()
It all depends on the use case.
First of all, You are not changing the class attributes there. You are changing the instance attributes.
Python: Difference between class and instance attributes
Secondly, If you are planning to share the results of your process among the various instances of the class,Then you can use class variables.
Third, If you are asking about instance variables, Then it depends on design choice.
Besides that, This is unnecessary I guess:-
a.results = a.process2()
You simply made allocation to be part of object itself. You can use a.process2 function for displaying results if you need that functionality.
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:
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
This is going to be difficult to explain, but what I'm trying to do is create a Base object to base other objects on. The Base class handles shared tasks so that subclasses don't need to keep implementing them. However, I also need a static/class method which creates instances of the classes. So for example, this is my Base class:
class Base(object):
def __init__(self, service, reference, vo=None):
self.service = service
self.reference = reference
self.id = reference.getId()
self.name = reference.getName()
# do a database lookup here to get more information
#staticmethod
def get_objects(service, references, as_dict=False):
"""
More efficient way to get multiple objects at once. Requires the service
instance and a list or tuple of references.
"""
vo_list = database.get_objects_from_references(references)
items = list()
for vo in vo_list:
items.append(Base(service, vo.getRef(), vo))
return items
The get_objects() method will take a list of ID numbers of entries stored in a database, then get all those objects and make objects out of them in one shot instead of hitting the database for each ID. The problem I'm having is that I have to use Base() in that method to instantiate the class. But this instantiates the Base class, not the subclass:
class Something(Base):
def __init__(self, service, reference, vo=None):
Base.__init__(self, service, reference, vo)
do_extra_stuff()
My problem is I don't know if I do this:
Something.get_objects(service, references)
Will that just run Base's init() method, or will it run the subclass's init() method (and the do_extra_stuff() method)?
You want a class method instead, that will get the class object as its first parameter so that you can build an instance of that specific class:
#classmethod
def get_objects(cls, service, references, as_dict=False):
"""
More efficient way to get multiple objects at once. Requires the service
instance and a list or tuple of references.
"""
vo_list = database.get_objects_from_references(references)
items = list()
for vo in vo_list:
items.append(cls(service, vo.getRef(), vo))
return items