1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| from functools import wraps
class StaticVarsMeta(type): statics = {} def __new__(mcls, name, bases, namespace): cls = super().__new__(mcls, name, bases, namespace) cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c, mcls)) cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__) cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__) cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__) try: mcls.statics[cls] = getattr(cls, '__statics__') except AttributeError: mcls.statics[cls] = namespace['__statics__'] = set() if any(not isinstance(static, str) for static in mcls.statics[cls]): typ = dict(zip((not isinstance(static, str) for static in mcls.statics[cls]), map(type, mcls.statics[cls])))[True].__name__ raise TypeError('__statics__ items must be strings, not {0}'.format(typ)) if len(cls.__sro__) > 1: for attr, value in namespace.items(): if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']: for 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): @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): @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): @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): for 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): for c in cls.__sro__: if attr in StaticVarsMeta.statics[c]: setattr(c, attr, value) break
def __delstatic__(cls, attr): for 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): if attr == '__sro__': raise AttributeError('readonly attribute') super().__delattr__(attr)
def is_static(cls, attr): if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__): return True return False
class MyBaseClass(metaclass=StaticVarsMeta): __statics__ = {'a', 'b', 'c'} i = 0 a = 1
class MyParentClass(MyBaseClass): __statics__ = {'d', 'e', 'f'} j = 2 d, e, f = 3, 4, 5 a, b, c = 6, 7, 8
class MyChildClass(MyParentClass): __statics__ = {'a', 'b', 'c'} j = 2 d, e, f = 9, 10, 11 a, b, c = 12, 13, 14
|