简体   繁体   English

在__init__中使用python @property给出属性错误

[英]using python @property in __init__ , gives attribute error

Is my 'fix' the correct / good practice method? 我的“解决方案”是正确的/良好的实践方法吗?

I have a class Text(), that caches it's surface to render, unless font/size changes. 我有一个Text()类,它缓存要渲染的表面,除非字体/大小改变。 (I left the font property from the snippet) (我从代码段中删除了font属性)

If I attempt to use self.size in __init__ , it will fail: 如果我尝试在__init__使用self.size ,它将失败:

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'

I was able to assign self._text in __init__ , but I believe that's wrong, since: 我可以在__init__分配self._text ,但是我认为这是错误的,因为:

  1. Ignores the setter, which may have extra checks 忽略设置器,可能会有额外的检查
  2. code might break if _text changes, but could be ok if it uses the property instead. 如果_text更改,代码可能会中断,但如果改用该属性,则代码可能会正常。

Code with error: 错误代码:

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()

My fix: 我的解决方法:

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()
   try:
        if self._size != px:            
            self._size = px
            self.dirty=True
    except:
        self._size = 14
        self.dirty = True

The except is a bad idea because you catch all exceptions. 例外是一个坏主意,因为您会捕获所有异常。 You might accidentally catch some other exception and ignore it. 您可能不小心捕获了其他一些异常而忽略了它。 That would be a bad idea. 那将是一个坏主意。 Python throws exceptions for all sorts of reason and you'll end up masking a bug. Python出于各种原因引发异常,您最终将掩盖错误。 For the same reason, you should put as little code in the try block as possible. 出于同样的原因,您应该在try块中放入尽可能少的代码。

One approach would be: 一种方法是:

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

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

But a cleaner way to solve this problem: 但是,解决此问题的更干净的方法是:

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

The pattern that I use is: 我使用的模式是:

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

The problem that you were running in to is that your setter was referring to an attribute that you weren't creating until the setter was called -- self._size doesn't exist until you have assigned something to it. 您遇到的问题是,设置器引用的是直到调用该设置器后才创建的属性self._size在您为其分配了某些内容之前不存在。 Generally, I use properties that have nice names (like size ) and store the data in hidden attributes (eg, _size ). 通常,我使用名称很好的属性(例如size )并将数据存储在隐藏的属性(例如_size )中。 In the initializer, I only manipulate the attributes directly and never rely on properties. 在初始化程序中,我仅直接操作属性,而不依赖属性。

In your case, setting the property has a side-effect so you have a chance of bugs creeping in. Consider what would happen if you put self.dirty = False as the first line in the initializer. 在您的情况下,设置该属性会有副作用,因此您有可能会爬入错误。请考虑一下,如果将self.dirty = False为初始化的第一行会发生什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM