Я сделал себя ленивым классом переменных и использовал его в другом классе. Как я могу получить доступ к атрибутам ленивого класса переменных? Я пробовал __getattr__
без везения. Вот пример:
class lazyobject(object):
def __init__(self,varname,something='This is the something I want to access'):
self.varname = varname
self.something = something
def __get__(self, obj, type=None):
if obj.__dict__.has_key(self.varname):
print "Already computed %s" % self.varname
return obj.__dict__[self.varname]
else:
print "computing %s" % self.varname
obj.__dict__[self.varname] = "something else"
return obj.__dict__[self.varname]
class lazyobject2(lazyobject):
def __getattr__(self):
return self.something
class dummy(object):
def __init__(self):
setattr(self.__class__, 'lazy', lazyobject('lazy'))
class dummy2(object):
def __init__(self):
setattr(self.__class__, 'lazy', lazyobject2('lazy'))
d1 = dummy()
d2 = dummy2()
try:
print "d1.lazy.something - no getattr: ",d1.lazy.something
except:
print "d2.lazy is already computed - can't get its .something because it now a string!"
print "d1.lazy - no getattr: ",d1.lazy
try:
print "d2.lazy.something - has getattr: ",d2.lazy.something
except:
print "d2.lazy is already computed - can't get its .something because it now a string!"
print "d2.lazy - no getattr: ",d2.lazy
Это печатает:
d1.lazy.something - no getattr: computing lazy
d2.lazy is already computed - can't get its .something because it now a string!
d1.lazy - no getattr: something else
d2.lazy.something - has getattr: computing lazy
d2.lazy is already computed - can't get its .something because it now a string!
d2.lazy - no getattr: something else
Что я хотел бы распечатать:
d1.lazy.something - no getattr: This is the something I want to access
computing lazy
d1.lazy - no getattr: something else
Вышеприведенный пример надуман, но я надеюсь, что все получится. Другой способ __get__
о моем вопросе: как я могу обойти метод __get__
при доступе к атрибуту класса?
Способ обойти __get__ при доступе к атрибуту класса - искать его через словарь классов, а не использовать точечный доступ.
Это легко продемонстрировать с использованием функциональных объектов. Например:
>>> class A(object):
def f(self):
pass
>>> A.f # dotted access calls f.__get__
<unbound method A.f>
>>> vars(A)['f'] # dict access bypasses f.__get__
<function f at 0x101723500>
>>> a = A()
>>> a.f # dotted access calls f.__get__
<bound method A.f of <__main__.A object at 0x10171e810>>
>>> vars(a.__class__)['f'] # dict access bypasses f.__get__
<function f at 0x101723500>
Другая часть информации, которую вы отсутствовали, заключается в том, что унаследованный __get__ работает до __getattr__, который работает только в том случае, если атрибут не найден. Эта логика управляется __getattribute__, которая унаследована от объекта. Итак, если вы хотите обойти __get__, вам нужно будет либо написать новый __get__ в подклассе, либо изменить логику поиска, задав __getattribute__ в подклассе.
Чтобы исправить класс lazyobject2, замените __getattr__ на:
class lazyobject2(lazyobject):
def __getattribute__(self, key):
# bypass __get__
return object.__getattribute__(self, '__dict__')[key]
Таким образом, ключевыми знаниями, которые используются для решения этой проблемы, являются:
Полные сведения о логике дескриптора можно найти в этом отчете или в этой презентации.