Дизайн Python - инициализация, установка и получение атрибутов класса
У меня есть класс, в котором метод должен сначала проверить наличие атрибута и в противном случае вызвать функцию для его вычисления. Затем, гарантируя, что атрибут не None
, он выполняет некоторые операции с ним. Я вижу два немного разных варианта дизайна:
class myclass():
def __init__(self):
self.attr = None
def compute_attribute(self):
self.attr = 1
def print_attribute(self):
if self.attr is None:
self.compute_attribute()
print self.attr
А также
class myclass2():
def __init__(self):
pass
def compute_attribute(self):
self.attr = 1
return self.attr
def print_attribute(self):
try:
attr = self.attr
except AttributeError:
attr = self.compute_attribute()
if attr is not None:
print attr
В первом проекте мне нужно убедиться, что все атрибуты класса установлены в None
заранее, что может стать многословным, но и прояснить структуру объекта.
Второй вариант кажется наиболее широко используемым. Однако для моих целей (научные вычисления, связанные с теорией информации) использую try except
везде блоки могут быть немного излишними, учитывая, что этот класс на самом деле не взаимодействует с другими классами, он просто берет данные и вычисляет кучу вещей.
2 ответа
Во-первых, вы можете использовать hasattr
чтобы проверить, есть ли у объекта атрибут, он возвращает True
если атрибут существует.
hasattr(object, attribute) # will return True if the object has the attribute
Во-вторых, вы можете настроить доступ к атрибутам в Python, вы можете узнать больше об этом здесь: https://docs.python.org/2/reference/datamodel.html
По сути, вы отменяете __getattr__
Способ достижения этого, так что-то вроде:
класс myclass2 (): def init(self): pass
def compute_attr(self):
self.attr = 1
return self.attr
def print_attribute(self):
print self.attr
def __getattr__(self, name):
if hasattr(self, name) and getattr(self, name)!=None:
return getattr(self, name):
else:
compute_method="compute_"+name;
if hasattr(self, compute_method):
return getattr(self, compute_method)()
Убедитесь, что вы используете только getattr
чтобы получить доступ к атрибуту в __getattr__
или вы закончите бесконечной рекурсией
Основываясь на ответе Джоншарпа, я предлагаю третий вариант дизайна. Идея здесь заключается в том, что никакая специальная условная логика вообще не требуется ни клиентами MyClass
или по коду в MyClass
сам. Вместо этого декоратор применяется к функции, которая выполняет (гипотетически дорогое) вычисление свойства, и затем этот результат сохраняется.
Это означает, что дорогостоящие вычисления выполняются лениво (только если клиент пытается получить доступ к свойству) и выполняются только один раз.
def lazyprop(fn):
attr_name = '_lazy_' + fn.__name__
@property
def _lazyprop(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, fn(self))
return getattr(self, attr_name)
return _lazyprop
class MyClass(object):
@lazyprop
def attr(self):
print('Generating attr')
return 1
def __repr__(self):
return str(self.attr)
if __name__ == '__main__':
o = MyClass()
print(o.__dict__, end='\n\n')
print(o, end='\n\n')
print(o.__dict__, end='\n\n')
print(o)
Выход
{}
Generating attr
1
{'_lazy_attr': 1}
1
редактировать
Применение ответа Cyclone к контексту OP:
class lazy_property(object):
'''
meant to be used for lazy evaluation of an object attribute.
property should represent non-mutable data, as it replaces itself.
'''
def __init__(self, fget):
self.fget = fget
self.func_name = fget.__name__
def __get__(self, obj, cls):
if obj is None:
return None
value = self.fget(obj)
setattr(obj, self.func_name, value)
return value
class MyClass(object):
@lazy_property
def attr(self):
print('Generating attr')
return 1
def __repr__(self):
return str(self.attr)
if __name__ == '__main__':
o = MyClass()
print(o.__dict__, end='\n\n')
print(o, end='\n\n')
print(o.__dict__, end='\n\n')
print(o)
Вывод идентичен приведенному выше.