python - Determine if class attribute is a read-only data descriptor -
a read-only data descriptor descriptor defines both __get__
, __set__
, __set__
raises attributeerror
when called.
an example simple read-only property:
class test(): _i = 1 @property def i(self): return self._i assert hasattr(test.i, '__get__') assert hasattr(test.i, '__set__') t = test() t.i # 1 t.i = 2 # error
if have instance of class, can determine if instance attribute read-only data descriptor way (although don't @ all):
def is_ro_data_descriptor_from_instance(instance, attr): temp = getattr(instance, attr) try: setattr(instance, attr, none) except attributeerror: return true else: setattr(instance, attr, temp) return false
if know class doesn't require arguments instantiated, can determine if class attribute read-only data descriptor similar above:
def is_ro_data_descriptor_from_klass(klass, attr): try: setattr(klass(), attr, none) except attributeerror: return true else: return false
however, if don't know signature of class ahead of time, , try instantiate temporary object in way, error:
class myclass(): = 1 def __init__(self, a, b, c): '''a, b, , c required!''' pass def is_ro_data_descriptor_from_klass(myclass, 'i') # error
what can done determine if class attribute read-only data descriptor?
edit: adding more information.
below code trying working:
class staticvarsmeta(type): '''a metaclass emulate "static variable" behavior of other languages. example: class test(metaclass = staticvarsmeta): _i = 1 @property def i(self): return self._i t = test() assert t.i == test.i''' statics = {} def __new__(meta, name, bases, dct): klass = super().__new__(meta, name, bases, dct) meta.statics[klass] = {} key, value in dct.items(): if "_" + key in dct: meta.statics[klass][key] = set() if hasattr(value, '__get__'): meta.statics[klass][key].add('__get__') if hasattr(value, '__set__'): try: value.__set__(none, none) except attributeerror: continue else: meta.statics[klass][key].add('__set__') return klass def __getattribute__(klass, attr): if attr not in staticvarsmeta.statics[klass]: return super().__getattribute__(attr) elif '__get__' not in staticvarsmeta.statics[klass][attr]: return super().__getattribute__(attr) else: return getattr(klass, '_' + attr) def __setattr__(klass, attr, value): if attr not in staticvarsmeta.statics[klass]: super().__setattr__(attr, value) elif '__set__' not in staticvarsmeta.statics[klass][attr]: super().__setattr__(attr, value) else: setattr(klass, '_' + attr, value) class test(metaclass = staticvarsmeta): _i = 1 def get_i(self): return self._i = property(get_i)
note following:
type(test.i) # int type(test.__dict__['i']) # property test().i = 2 # error, expected test.i = 2 # no error - should produce error
it seems super-awkward, here's how implement based on comment:
class staticvarsmeta(type): statics = {} def __new__(meta, name, bases, dct): cls = super().__new__(meta, name, bases, dct) meta.statics[cls] = {} key, val in dct.items(): if hasattr(val, '__get__') , hasattr(val, '__set__'): meta.statics[cls][key] = {'__get__'} try: val.__set__(none, none) except attributeerror err: if "can't set attribute" in err.args: continue meta.statics[cls][key].add('__set__') return cls
in use:
>>> class readonly(metaclass=staticvarsmeta): @property def foo(self): return none >>> class readwrite(metaclass=staticvarsmeta): @property def bar(self): return none @bar.setter def bar(self, val): pass >>> staticvarsmeta.statics {<class '__main__.readonly'>: {'foo': {'__get__'}}, <class '__main__.readwrite'>: {'bar': {'__get__', '__set__'}}}
this more of "starter 10", there must better way it...
Comments
Post a Comment