I have a class Text(), that caches it's surface to render, unless font/size changes. (I left the font property from the snippet)
self.size
in __init__
, it will fail: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:
_text
changes, but could be ok if it uses the
property instead.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()
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. For the same reason, you should put as little code in the try block as possible.
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. Generally, I use properties that have nice names (like size
) and store the data in hidden attributes (e.g., _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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With