I'm trying to create a static variable to be accessed through different classes, assigning value to it, and getting this value when needed. I did use this way in order to achieve this, and that leads me to including a property as following:
class GetPartition(Partition):
_i = 100
def __init__(self):
super(Partition,self).__init__("get")
def get_i(self):
return type(self)._i
def set_i(self,val):
type(self)._i = val
i = property(get_i, set_i)
and this is class Partition
if needed:
class Partition(BaseCommand):
def __init__(self,type):
super(Partition,self).__init__("databaseTest")
self.type = type
So, when assigning a value to i
from another class, I'm assigning it directly like:
GetPartition.i = 5
and among that class when printing GetPartition.i
it gives me 5
, but when trying to get this value from another class:
partitionNum = GetPartition()
print(partitionNum.i) # 100
print(partitionNum.get_i()) # 100
print(GetPartition.i) # <property object at 0x12A938D0>
print(GetPartition._i) # 100
1 Answer 1
As I explained in my comment, the problem comes when you assign 5 to i
by way of:
GetPartition.i = 5
With this line of code, you are overwriting the property, and "bypassing" the property setter. What I mean by that is: the property setter is not called when you call its attribute name from the class; it is only called when you call its attribute name from a class instance.
Since it has been overwritten, the property no longer exists at that point and all references to the i
attribute, whether from class instances or from the class itself, are distinct. They will no longer retrieve the same object, but distinct objects.
You can confirm this problem by doing this:
gp = GetPartition()
print(GetPartition.i) # the property is returned
GetPartition.i = 5 # here the property is overwritten
print(GetPartition.i) # 5 ; the property is gone
print(gp.i) # 5 because gp instance doesn't have its own i
gp.i = 2 # now gp does have its own i
print(gp.i) # 2
print(GetPartition.i) # 5 ; i is not synced
As I said above, the property
getters and setters (and descriptors in general) only work with instances of GetPartition
, not the class itself. They can be forced to work with the class itself by creating a metaclass - which is the class of a class - for your class; this is considered "deep black magic" by many people, and I don't recommend going that route if you can avoid it.
I believe the below example is probably the simplest way to implement the behavior you want. This approach abandons the use of properties in favor of overriding the attribute getter and setter methods directly:
class Example():
i = 1 # this is a "static variable"
j = 3 # this is a regular class attribute
#designate which of the class attributes are "static"
statics = {'i'}
def __getattribute__(self, attr):
'''Overrides default attribute retrieval behavior.'''
if attr in Example.statics:
#use class version if attr is a static var
return getattr(Example, attr)
else:
#default behavior if attr is not static var
return super().__getattribute__(attr)
def __setattr__(self, attr, value):
'''Overrides default attribute setting behavior.'''
if attr in Example.statics:
#use class version if attr is a static var
setattr(Example, attr, value)
else:
#default behavior if attr is not static var
super().__setattr__(attr, value)
#testing
if __name__ == '__main__':
print("\n\nBEGIN TESTING\n\n")
e = Example()
#confirm instance and class versions of i are the same
test = "assert e.i is Example.i"
exec(test)
print(test)
e.i = 5
#confirm they remain the same after instance change
test = "assert e.i is Example.i"
exec(test)
print(test)
Example.i = 100
#confirm they remain the same after class change
test = "assert e.i is Example.i"
exec(test)
print(test)
e.j = 12
#confirm both versions of j are distinct
test = "assert e.j is not Example.j"
exec(test)
print(test)
print("\n\nTESTING COMPLETE\n\n")
If you are not familiar with __getattribute__
and __setattr__
, I should let you know that overriding them is often quite perilous and can cause big problems (especially __getattribute__
). You'll find many people simply say "don't do it; rethink your problem and find another solution". Doing the overrides correctly requires a deep understanding of a wide range python topics.
I do not claim to have this deep understanding (though I think I have a pretty good understanding), so I cannot be 100% certain that my overrides as given above will not lead to some other problem for you. I believe they are sound, but just be aware that these particular corners of python can be pretty tricky.
-
Thanks for this great illustration, in fact I tried this way but can't manage it, it gives me
super() takes at least 1 argument (0 given)
at the linesuper().__setattr__(attr, value)
when trying to set the valuee.i = 5
. and giving it 'self' doesn't work as it requires a certain type.Muhammed Refaat– Muhammed Refaat2015年04月05日 14:41:40 +00:00Commented Apr 5, 2015 at 14:41 -
Also worth to mention that I won't get the value from the same class but from another one with different layoutMuhammed Refaat– Muhammed Refaat2015年04月05日 14:42:13 +00:00Commented Apr 5, 2015 at 14:42
i
by way ofGetPartition.i = 5
, you are overwriting the property, and bypassing the property setter. The property no longer exists at that point. You can confirm this by doingGetPartition.i = 5
,gp = GetPartition()
,gp.i = 2
,print(gp.i, GetPartition.i)
. The property getter and setter only works with instances ofGetPartition
, not the class itself. If you want to update the "static variable" (keeping in mind that Python does not actually have static variables), you'll need to doGetPartition._i = 5
as in your example code.GetPartition._i = 5
but I faced similar problems when trying to get it, any ideas how to get it from another class?print(GetPartition._i)
instead ofprint(GetPartition.i)
. I realize this isn't optimal. There are other ways to accomplish what you want, but all of the ones I am aware of are pretty complicated.