How to fully emulate the behavior of static class variables of other languages in Python -


it established python not have "static class variables" in same sense in other languages. instead, has class attributes, similar, different.

for example, class attribute (a variable defined inside of class definition) can "overridden" instance attribute of same name:

class a():     = 1  assert a.i == 1 = a() assert a.i == 1 a.i = 2 # has a.i been updated 2 well? assert a.i == 2 # error: no, hasn't assert a.i == 2 # a.i , a.i 2 different variables del a.i assert a.i == 1 # if a.i doesn't exist, falls in a.i 

in actuality, there no "overriding" going on - class dictionary comes later in attribute lookup order instance dictionary.

this behavior can partially overcome using property decorator:

class a():     _i = 1     @property     def i(self):         return type(self)._i     @i.setter     def i(self,value):         type(self)._i = value 

now, property remain "in sync" between multiple instances:

a1 = a() a2 = a() a1.i = 2 assert a1.i == a2.i # attribute remained in sync 

however, "gotcha" here cannot access or set property via class itself:

assert a.i == a.i # error type(a.i) # class 'property' a.i = 10 # if try set via class... assert a.i == 10 # appears have worked! a.i = 5 # but... assert a.i == a.i # error! didn't work.  type(a.i) # int - setting property via class overwrote 

this difficulty can overcome using full-fledged descriptor (of property 1 type):

class myproperty():     def __init__(self,name):         self.name = name     def __get__(self,inst,cls):         return getattr(cls,self.name)     def __set__(self,inst,value):         setattr(type(inst),self.name,value)     def __delete__(self,inst):         delattr(type(inst),self.name)  class a():     = myproperty('i') 

now, getting , setting attribute via class , via instance work, , deleting via instance works:

a = a() a.i = 5 # can set via class assert a.i == a.i a.i = 10 assert a.i == a.i # still in sync! del a.i assert a.i # error, expected a.i = 2 assert a.i == 2  

however, there still problem when try delete via class:

del a.i # myproperty descriptor has been deleted a.i = 2 a.i = 4 assert a.i == a.i # error! 

how can full emulation of "static variables" achieved in python?

i present (python 3) solution below informational purposes only. not endorsing "good solution". have doubts whether emulating static variable behavior of other languages in python ever necessary. however, regardless whether useful, creating code below helped me further understand how python works, might others well.

the metaclass have created below attempts emulate "static variable" behavior of other languages. it's pretty complicated, works replacing normal getter, setter, , deleter versions check see if attribute being requested "static variable". catalog of "static variables" stored in staticvarmeta.statics attribute. if requested attribute not "static variable", class fall on default attribute get/set/delete behavior. if "static variable", attempted resolve attribute request using substitute resolution order (which have dubbed __sro__, or "static resolution order").

i sure there simpler ways accomplish this, , forward seeing other answers.

from functools import wraps  class staticvarsmeta(type):     '''a metaclass creating classes emulate "static variable" behavior     of other languages. not advise using anything!!!      behavior intended similar classes use __slots__. however, "normal"     attributes , __statics___ can coexist (unlike __slots__).       example usage:           class mybaseclass(metaclass = staticvarsmeta):             __statics__ = {'a','b','c'}             = 1 # regular attribute          class myparentclass(mybaseclass):             __statics__ = {'d','e','f'}             j = 2 # regular attribute             d, e, f = 3, 4, 5 # static vars             a, b, c = 6, 7, 8 # static vars (inherited mybaseclass, defined here)          class mychildclass(myparentclass):             __statics__ = {'a','b','c'}             j = 2 # regular attribute (redefines j myparentclass)             d, e, f = 9, 10, 11 # static vars (inherited myparentclass, redefined here)             a, b, c = 12, 14, 14 # static vars (overriding previous definition in myparentclass here)'''     statics = {}     def __new__(mcls, name, bases, namespace):         # class object         cls = super().__new__(mcls, name, bases, namespace)         # establish "statics resolution order"         cls.__sro__ = tuple(c c in cls.__mro__ if isinstance(c,mcls))          # replace class getter, setter, , deleter instance attributes         cls.__getattribute__ = staticvarsmeta.__inst_getattribute__(cls, cls.__getattribute__)         cls.__setattr__ = staticvarsmeta.__inst_setattr__(cls, cls.__setattr__)         cls.__delattr__ = staticvarsmeta.__inst_delattr__(cls, cls.__delattr__)         # store list of static variables class object         # list permanent , cannot changed, similar __slots__         try:             mcls.statics[cls] = getattr(cls,'__statics__')         except attributeerror:             mcls.statics[cls] = namespace['__statics__'] = set() # no static vars provided         # check , make sure statics var names strings         if any(not isinstance(static,str) static in mcls.statics[cls]):             typ = dict(zip((not isinstance(static,str) static in mcls.statics[cls]), map(type,mcls.statics[cls])))[true].__name__             raise typeerror('__statics__ items must strings, not {0}'.format(typ))         # move existing, not overridden statics static var parent class(es)         if len(cls.__sro__) > 1:             attr,value in namespace.items():                 if attr not in staticvarsmeta.statics[cls] , attr != ['__statics__']:                     c in cls.__sro__[1:]:                         if attr in staticvarsmeta.statics[c]:                             setattr(c,attr,value)                             delattr(cls,attr)         return cls     def __inst_getattribute__(self, orig_getattribute):         '''replaces class __getattribute__'''         @wraps(orig_getattribute)         def wrapper(self, attr):             if staticvarsmeta.is_static(type(self),attr):                 return staticvarsmeta.__getstatic__(type(self),attr)             else:                 return orig_getattribute(self, attr)         return wrapper     def __inst_setattr__(self, orig_setattribute):         '''replaces class __setattr__'''         @wraps(orig_setattribute)         def wrapper(self, attr, value):             if staticvarsmeta.is_static(type(self),attr):                 staticvarsmeta.__setstatic__(type(self),attr, value)             else:                 orig_setattribute(self, attr, value)         return wrapper     def __inst_delattr__(self, orig_delattribute):         '''replaces class __delattr__'''         @wraps(orig_delattribute)         def wrapper(self, attr):             if staticvarsmeta.is_static(type(self),attr):                 staticvarsmeta.__delstatic__(type(self),attr)             else:                 orig_delattribute(self, attr)         return wrapper     def __getstatic__(cls,attr):         '''static variable getter'''         c in cls.__sro__:             if attr in staticvarsmeta.statics[c]:                 try:                     return getattr(c,attr)                 except attributeerror:                     pass         raise attributeerror(cls.__name__ + " object has no attribute '{0}'".format(attr))     def __setstatic__(cls,attr,value):         '''static variable setter'''         c in cls.__sro__:             if attr in staticvarsmeta.statics[c]:                 setattr(c,attr,value)                 break     def __delstatic__(cls,attr):         '''static variable deleter'''         c in cls.__sro__:             if attr in staticvarsmeta.statics[c]:                 try:                     delattr(c,attr)                     break                 except attributeerror:                     pass         raise attributeerror(cls.__name__ + " object has no attribute '{0}'".format(attr))     def __delattr__(cls,attr):         '''prevent __sro__ attribute deletion'''         if attr == '__sro__':             raise attributeerror('readonly attribute')         super().__delattr__(attr)     def is_static(cls,attr):         '''returns true if attribute static variable of class in __sro__'''         if any(attr in staticvarsmeta.statics[c] c in cls.__sro__):             return true         return false 

Comments

Popular posts from this blog

c++ - Difference between pre and post decrement in recursive function argument -

php - Nothing but 'run(); ' when browsing to my local project, how do I fix this? -

php - How can I echo out this array? -