Changing variables in multiple Python instances - python

Is there anyway to set the variables of all instances of a class at the same time? I've got a simplified example below:
class Object():
def __init__(self):
self.speed=0
instance0=Object()
instance1=Object()
instance2=Object()
#Object.speed=5 doesn't work of course
I can see it would be possible by adding all new instances to a list and iterating with isinstance(), but that's not desirable.

One, simpler way, as the other answers put it, is to keep your attribute always as a class attribute. If it is set on the class body, and all write access to the attribute is via the class name, not an instance, that would work:
>>> class Object(object):
... speed = 0
...
>>> a = Object()
>>> b = Object()
>>> c = Object()
>>>
>>> Object.speed = 5
>>> print a.speed
5
>>>
However, if you ever set the attribute in a single instance doing it this way, the instance will have its own attribute and it will no longer change along with the other instance's:
>>> a.speed = 10
>>> Object.speed = 20
>>> print b.speed
20
>>> print a.speed
10
>>>
To overcome that, so that whenever the attribute is set in any instance, the class attribute itself is changed, the easier way is to have the object as a property - whose setter sets the class attribute instead:
class Object(object):
_speed = 0
#property
def speed(self):
return self.__class__._speed
#speed.setter
def speed(self, value):
self.__class__._speed = value
Which works:
>>>
>>> a = Object()
>>> b = Object()
>>> a.speed, b.speed
(0, 0)
>>> a.speed = 10
>>> a.speed, b.speed
(10, 10)
If you want to have independent attribute on the instances, but a special "set_all" method that would set the attribute in all instances, the way to go is to use the gc (Garbage Collector) module in standard librayr, to find and loop through all instances of the class, and set their instance attributes:
import gc
class Object(object):
def __init__(self):
self.speed = 0
def set_all_speed(self, value):
for instance in (obj for obj in gc.get_referrers(self.__class__):
if isinstance(obj, self.__class__)):
instance.speed = value
Which results in:
>>> a =Object()
>>> b = Object()
>>> a.speed = 5
>>> b.speed = 10
>>> a.speed, b.speed
(5, 10)
>>> a.set_all_speed(20)
>>> a.speed, b.speed
(20, 20)

What about using a class attribute?
class Object():
speed=0
instance0=Object()
instance1=Object()
instance2=Object()
Object.speed=5

You could use a class attribute:
class Object():
speed = 0
instance0=Object()
instance1=Object()
instance2=Object()
Object.speed=5
# instance0.speed == instance1.speed == instance2.speed == Object.speed == 5
However this would mean that all instances would always have the same speed.

"Is there any way to set the variables of all instances of a class at the same time?"
That's a class attribute!
Some examples on how to access a class attribute:
>>> class Object:
... speed = 5
... #classmethod
... def first(cls):
... return cls.speed
... def second(self):
... return self.speed
...
>>> Object.speed
5
>>> instance = Object()
>>> instance.speed
5
>>> instance.first()
5
>>> instance.second()
5

Related

How can I see attributes on a python namedlist object?

I have been using namedlist to create lightweight classes:
from namedlist import namedlist
# create a class
SomeClass = namedlist('SomeClass', 'foo bar', use_slots=False)
# create an object
my_list = SomeClass(1,2)
# set an attribute not specified in the class
my_list.baz = 3
# the attribute is there if I reference it
print(my_list.baz)
# output: 3
Sometimes I want to take an object and see if any extra attributes have been set:
# this doesn't show 'baz'
import inspect
inspect.getmembers(my_list)
# neither does this
my_list.__dict__
Is there a way I can see any attributes that have been added in this way?
Looking at the source of namedlist, we can see that the factory function namedlist(), generates the type (SomeClass in your example).
Now this is interesting.
On one hand, __getattribute__ and __setattribute__ were not overloaded, which lets you do things like my_list.baz = 3 and then access it as my_list.baz.
On the other, __dict__, was overridden with property(_asdict) (generated in _common_fields()). This causes whoever uses __dict__ to fail seeing baz - function such as dir() and the inspect module.
While I failed to find a function that will list the added attributes in this case, if you know what attribute you are looking for, you can still check if it exists using hasattr(my_list, 'baz'):
>>> from namedlist import namedlist
>>> SomeClass = namedlist('SomeClass', 'foo bar', use_slots=False)
>>> my_list = SomeClass(1,2)
>>> my_list.baz = 3
>>> hasattr(my_list, 'baz')
True
If switching types is going to be problematic (maybe there is legacy code already using namedlist), I found that the following makes viewing namedlist bearable:
def set_attr(self, attr_name, attr_val):
setattr(self, attr_name, attr_val)
self.opt_attrs.append(attr_name)
TypeA = namedlist('TypeA', 'field_a opt_attrs', use_slots=False)
TypeA.set_attr = set_attr
TypeB = namedlist('TypeB', 'field_b opt_attrs', use_slots=False)
TypeB.set_attr = set_attr
objA = TypeA(1, [])
objA.set_attr('field_x', 2)
objB = TypeB(7, [])
objA
# Out: TypeA(field_a=1, opt_attrs=['field_x'])
objA.field_x
# Out: 2
objB
# Out: TypeB(field_b=7, opt_attrs=[])
It is probably best to just to use python classes though. More up-front code, less after-the-fact confusion:
class TypeA:
def __init__(self, a):
self.a = a
def __repr__(self):
return "A(a={})".format(self.a)
class TypeB:
def __init__(self, b):
self.b = b
def __repr__(self):
return "B(b={})".format(self.b)
A = TypeA(1)
A.x = 2
B = TypeB(7)
class TypeA:
def __init__(self, a):
self.a = a
def __repr__(self):
return "A(a={})".format(self.a)
class TypeB:
def __init__(self, b):
self.b = b
def __repr__(self):
return "B(b={})".format(self.b)
objA = TypeA(1)
objA.x = 2
objB = TypeB(7)
objA
# Out: A(a=1)
objA.__dict__
# Out: {'a': 1, 'x': 2}
objB
# Out: B(b=7)

Is there a way to automatically convert an object method to an external function?

Just to illustrate let's take:
class newobj:
def __init__(self, val):
self.value = val
def mul2(self):
self.value = 2*self.value
Is there a way to automatically convert the method newobj.mul2 to a function mul2 that can be called like this:
o = newobj(5)
mul2(o)
Preferably the new function mul2 will know to assert that the input is an instance of newobj, but this is not mandatory.
Just assign the bound method to a variable:
o = newobj(5)
mul2 = o.mul2
You don't have to call a method in the same expression, you can just reference the attribute.
Demo:
>>> class newobj:
... def __init__(self, val):
... self.value = val
... def mul2(self):
... self.value = 2*self.value
...
>>> o = newobj(5)
>>> o.mul2
<bound method newobj.mul2 of <__main__.newobj instance at 0x1059d4440>>
>>> mul2 = o.mul2
>>> o.value
5
>>> mul2()
>>> o.value
10
>>> mul2()
>>> o.value
20
If you need a generic 'function' that operates on instances of newobj, just reference the method on the class and manually pass in the instance each time:
mul2 = newobj.mul2
mul2(o)
Demo:
>>> o = newobj(5)
>>> mul2 = newobj.mul2
>>> o.value
5
>>> mul2(o)
>>> o.value
10

Python - Exclusive attribute value for object?

Let say I have this class:
class A(object):
def __init__(self, one=None):
self.one = one
So every time you create A object, you can assign one attribute. But only the last object assigned not false value gets to keep it.
For example:
a = A('a')
b = A('b')
c = A()
print a
print b
print c
----
None
b
None
Now these objects are also actual records (rows) in database, so those could be accessed later.
What could be a good pattern (if there is) to set such attribute dynamically (unsetting attribute for older objects)?
You could store an instance in a class variable and unset the previous one in the __init__.
class A(object):
obj = None
def __init__(self, one=None):
self.one = one
if self.one:
if A.obj:
A.obj.one = None
A.obj = self
def __str__(self):
return str(self.one)
Result:
>>> a = A('a')
>>> b = A('b')
>>> c = A()
>>> print a
None
>>> print b
b
>>> print c
None

Are class fields inherited in python?

class A:
a = 3
def f(self):
print self.a
class C(A):
def __init__(self):
self.a = 4
>>> a=A()
>>> a.f()
3
>>> c = C()
>>> a.f()
3
>>> c.f()
4
//Update:
>>> C.a = 5
>>> A.a
3
>>> c.a
4
How to explain the result.
Seems like C and A has a different copy of a. In C++ a static member is expected to be shared with its derived class.
Martin Konecny's answer is mostly correct. There are class-level attributes (which are like static members) and instance-level attributes. All instances share their class attributes (but not their instance attributes), and they can be changed dynamically. Somewhat confusingly, you can get class attributes from an instance with the dot-notation, unless an instance attribute is defined with the same name. Perhaps these examples are illustrative:
>>> class A:
... a = 3
... def f(self):
... print self.a
...
>>> class C(A):
... def __init__(self):
... self.a = 4
...
>>> a = A()
>>>
>>> A.a # class-level attribute
3
>>> a.a # not redefined, still class-level attribute
3
>>> A.a = 5 # redefine the class-level attr
>>> A.a # verify value is changed
5
>>> a.a # verify instance reflects change
5
>>> a.a = 6 # create instance-level attr
>>> A.a # verify class-level attr is unchanged
5
>>> a.a # verify instance-level attr is as defined
6
>>> a.__class__.a # you can still get the class-level attr
5
>>>
>>> c1 = C()
>>> c2 = C()
>>> C.a # this changed when we did A.a = 5, since it's inherited
5
>>> c1.a # but the instance-level attr is still there
4
>>> c2.a # for both instances
4
>>> c1.a = 7 # but if we change one instance
>>> c1.a # (verify it is set)
7
>>> c2.a # the other instance is not changed
4
In python there is a class attribute and an instance attribute
class A:
a = 3 # here you are creating a class attribute
def f(self):
print self.a
class C(A):
def __init__(self):
self.a = 4 #here you are creating an instance attribute
Seems like C and A has a different copy of a.
Correct. If you're coming from Java, it's helpful to think of class A as a "static" field, and class C as an "instance" field.

python: class variables and instance variables

How python recognize class and instance level variables ? are they different ?
For example,
class abc:
i = 10
def __init__(self, i):
self.i = i
a = abc(30)
b = abc(40)
print a.i
print b.i
print abc.i
output
--------
30
40
10
Means, in above example when I access a.i (or b.i) and abc.i are they referring to completely different variables?
First, your sample is wrong for you can not init the instance if there is only a self in the __init__.
>>> class abc:
... i = 10
... j = 11
... def __init__(self, x):
... self.i = x
Then, when you access the attribute on the instance, it will check the instance variables first. Refer the paragraph here. As you guess:
>>> a = abc(30)
>>> a.i
30
>>> a.j
11
Besides, the class variables is an object shared by all the instances, and instance variables are owned by the instance:
>>> class abc:
... i = []
... def __init__(self, x):
... self.i = [x]
... abc.i.append(x)
...
>>> a = abc(30)
>>> b = abc(40)
>>> a.i
[30]
>>> b.i
[40]
>>> abc.i
[30, 40]
in above example when I access a.i (or b.i) and abc.i are they
referring to completely different variables?
Yes.
abc.i is a Class Object reference.
a.i and b.i are each Instance Object references.
They are all separate references.
This is all assuming your init is meant to be:
def __init__(self,i):
Other wise it doesn't work. In the third case, abc.i the class hasn't been initialized so i acts as a static variable for which you set the value at 10 in the class definition. In the first two instances, when you called init you created an instance of abc with a specific i value. When you ask for the i value of each of those instances you get the correct number.

Categories

Resources