2016-03-31 4 views
3

У меня есть класс, как:Override атрибут со свойством в классе питона

class Parent(object): 
    def __init__(self, a1 = None, a2 = None): 
     someInitialization() 
     self.a1 = a1 
     self.a2 = a2 

    def coolMethod(self): 
     return dosomething(self.a2) 

Давайте предположим, что

  • Я не хочу, чтобы изменить атрибуты родительского класса
  • Я не хочу предоставлять a1 и a2 при инициализации, потому что они дороги для вычисления (и они не нужны для каждого метода родителя)
  • Я не хочу помнить, какие из методов Paren т требует a1 или a2, они должны просто работать

EDIT мои фактические ограничения на самом деле не так строги, но я задаю этот вопрос также для лучшего понимания атрибутов классов, методов и т.д ..

Я использую самое простое решение без наследования инициализации:

class Child(Parent): 
    _propertiesCache = {'a1':None,'a2':None} 
    def _initProperty(self, propertyName): 
     value = self._propertiesCache[propertyName] 
     if value is None: 
      self._propertiesCache[propertyName]=expensiveFunction(self.b, propertyName) 
     return self._propertiesCache[propertyName] 

    @property 
    def a1(self): 
     return self._initProperty('a1') 

    @property 
    def a2(self): 
     return self._initProperty('a2') 

    def __init__(self,b): 
     self.b = b 
     someInitialization() 

есть ли способ, чтобы правильно называть инициализации родительского класса? Если я использую super(Child,self).__init__(), я получаю AttributeError: can't set attribute. Я попытался переопределить __new__ но я всегда что-то отсутствует

class Child(Parent): 
    _propertiesCache = {'a1':None,'a2':None} 
    @classmethod 
    def _initProperty(cls, propertyName): 
     value = cls._propertiesCache[propertyName] 
     if value is None: 
      cls._propertiesCache[propertyName] = someTimeConsumingFunctionThatIDontWantToCallAtInitialization(propertyName) 
     return cls._propertiesCache[propertyName] 

    @property 
    def a1(self): 
     return self._initProperty('a1') 

    @property 
    def a2(self): 
     return self._initProperty('a2') 

    def __new__(cls): 
     super(Child,cls).__new__(cls) 
     return cls 
    def __init__(self,b): 
     self.b = b 
     super(Child,self).__init__(a1=self.a1, a2=self.a2) 

дает мне:

>>c = Child();c.a1 
<property at 0x3aa4228> 
>>c.a1.fget(c) 
"ValueOfa1" 
+0

Рассматривали ли вы делая 'Parent' абстрактный базовый класс, и' a1' требуемое свойство? См. Https://docs.python.org/2/library/abc.html – jonrsharpe

+0

Можете ли вы сделать метод 'a' getter, который вычисляет' self.a', если он не существует, а затем возвращает 'self.a' ? – acdr

+0

@jonrsharpe спасибо за предложение, это излишний для моего фактического варианта использования, но это то, на что я должен смотреть, в общем! – lib

ответ

3

вместо вызова метода _initProperty называют его __getattr__ так, что он будет вызываться каждый раз, когда атрибут не найденный в обычных местах, он должен быть сохранен (словарь атрибутов, словарь классов и т. д.), то при первом попытке доступа к атрибуту он получает инициализацию.

Обязательно не устанавливать их в инициализации Родителя, возможно только установить их, если они не являются None:

class Parent: 
    def __init__(self,a1=None,a2=None): 
     if a1 is not None: 
      self.a1 = a1 
     if a2 is not None: 
      self.a2 = a2 

Как оставаться последовательным с ошибками, которые вы хотите поднять AttributeError если атрибут Безразлично «т есть вместо того, чтобы позволить KeyError пройти, и, возможно, добавить ссылку на значение в регулярном атрибута Словаре, так что ему не нужно, чтобы запустить __getattr__ каждый раз:

_propertiesCache = {'a1':None,'a2':None} 
def __getattr__(self, propertyName): 
    if propertyName not in self._propertiesCache: 
     raise AttributeError(propertyName) 
    value = self._propertiesCache[propertyName] 
    if value is None: 
     value = self._propertiesCache[propertyName]=expensiveFunction(self.b, propertyName) 
    setattr(self,propertyName,value) 
    return value 

в любом случае вы имп lement этого вам необходимо убедиться, что:

Атрибут не не установлен, пока они впервые используются (в какой момент __getattr__ привыкает)

+1

Это лучшее решение, но OP должен будет изменить родительский класс для удаления self.a и self.b инициализации из родительского класса. Вы должны, вероятно, упомянуть об этом. В противном случае '__getattr__' никогда не будет вызван для' self.a' и 'self.b' –

+0

@MuhammadTahir в первом примере Child супер '__init__' никогда не вызывается, но я не заметил, что он используется во второй реализации. Вы поднимаете хороший момент, о котором следует упомянуть независимо. –

+1

Также ваше определение __getattr__ может быть как 'if attr == 'b': self.b = calculate_b(); return self.b; else: raise AttributeError' Не нужно использовать кеш свойств. Как только вы выполните 'self.b = calculate_b()' '__getattr__', больше никогда не будет вызываться для' self.b'. Он будет доступен другими методами python. –

Смежные вопросы