использование python @property в __init__ выдает ошибку атрибута - PullRequest
2 голосов
/ 27 октября 2011

Является ли мое 'fix' правильным / хорошим методом?

У меня есть класс Text (), который кэширует свою поверхность для рендеринга, если только шрифт / размер не изменятся.(Я оставил свойство шрифта из фрагмента)

Если я попытаюсь использовать self.size в __init__, произойдет сбой:

Traceback (most recent call last):
  File "temp.py", line 21, in <module>
    t = Text("arial", 6)
  File "temp.py", line 3, in __init__
    self.size = size
  File "temp.py", line 16, in size
    if self._size != px:            
AttributeError: 'Text' object has no attribute '_size'

Мне удалось назначить self._textв __init__, но я считаю, что это неправильно, так как:

  1. Игнорирует установщик, который может иметь дополнительные проверки
  2. код может сломаться, если _text изменится, но может быть в порядкеесли вместо этого используется свойство.

Код с ошибкой:

class Text(object):
    def __init__(self, font, size=12):
        self.size = size
        self.font = font
        self.dirty = False

    def draw(self):
        print "Draw: ", self.size, self.font, self.dirty

    @property
    def size(self): 
        return self._size

    @size.setter
    def size(self, px):
        if self._size != px:            
            self._size = px
            self.dirty=True

if __name__  == "__main__":
    t = Text("arial", 6)
    t.draw()
    t.size = 8
    t.draw()

Мое исправление:

class Text(object):
    def __init__(self, font, size=12):
        self.size = size
        self.font = font
        self.dirty = False

    def draw(self):
        print "Draw: ", self.size, self.font, self.dirty

    @property
    def size(self): 
        return self._size
    @size.setter
    def size(self, px):
        try:
            if self._size != px:            
                self._size = px
                self.dirty=True
        except:
            self._size = 14
            self.dirty = True

if __name__  == "__main__":
    t = Text("arial", 6)
    t.draw()
    t.size = 8
    t.draw()

Ответы [ 2 ]

3 голосов
/ 27 октября 2011
   try:
        if self._size != px:            
            self._size = px
            self.dirty=True
    except:
        self._size = 14
        self.dirty = True

Исключение - плохая идея, потому что вы ловите все исключения. Вы можете случайно поймать какое-то другое исключение и проигнорировать его. Это было бы плохой идеей. Python генерирует исключения по разным причинам, и вы в конечном итоге замаскируете ошибку. По той же причине вы должны поместить как можно меньше кода в блок try.

Один из подходов будет:

try:
   size_changed = self._size != size
except AttributeError:
   size_changed = True

if size_changed:
    self._size = size
    self.dirty = True

Но более чистый способ решить эту проблему:

def __init__(self, font, size=12):
    # store something in the hidden attribute
    # so it will work as expected
    self._size = None

    self.size = size
    self.font = font
    self.dirty = False
2 голосов
/ 27 октября 2011

Шаблон, который я использую:

class Text(object):
    def __init__(self, font, size=12):
        self.font = font
        self._size = size
        self.dirty = False
    @property
    def size(self):
        return self._size
    @size.setter
    def size(self, px):
        if self._size != px:
            self._size = px
            self.dirty = True

Проблема, с которой вы работали, заключалась в том, что ваш установщик имел в виду атрибут, который вы не создавали, пока не был вызван установщик - self._size не существует, пока вы не назначите ему что-то.Обычно я использую свойства с хорошими именами (например, size) и храню данные в скрытых атрибутах (например, _size).В инициализаторе я только манипулирую атрибутами напрямую и никогда не полагаюсь на свойства.

В вашем случае установка свойства имеет побочный эффект, так что у вас есть вероятность появления ошибок. Рассмотрите, что произойдет, если выпоставьте self.dirty = False в качестве первой строки инициализатора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...