python descriptors demystified
TRANSCRIPT
DescriptorsPython's most obscure language feature demystified.
In 5 minutes.
Chris Beaumont @BeaumontChris
graduate student, astrophysicsHarvard University / University of Hawaii
Learning about descriptors creates an appreciation for the elegance of Python's design.
-Raymond Hettinger
Learning about descriptors creates an appreciation for the elegance of Python's design.
-Raymond Hettinger
I DON'T
GET IT
class Email(object):
def __init__(self, sender, subject, message): self.sender_widget = QLineEdit(sender) self.subject_widget = QLineEdit(subject) self.message_widget = QLineEdit(message)
How does other codeinteract with email data?
class Email(object): ...
def get_subject(self): return self.subject_widget.text()
def set_subject(self, subject): self.subject_widget.setText(subject) ...
e = Email()e.set_subject('Hi there')sub = e.get_subject()
class Email(object): ...
def get_subject(self): return self.subject_widget.text()
def set_subject(self, subject): self.subject_widget.setText(subject) ...
e = Email()e.set_subject('Hi there')sub = e.get_subject()
Gross
PropertiesDisguise methods
as attributes
http://www.flickr.com/photos/mkapple/741018101/
class Email(object): ...
@property def subject(self): return self.subject_widget.text()
@subject.setter def subject(self, subject): self.subject_widget.setText(subject) ...
e = Email()sub = e.subjecte.subject = 'hi there'
class Email(object): ...
@property def subject(self): return self.subject_widget.text()
@subject.setter def subject(self, subject): self.subject_widget.setText(subject) ...
e = Email()sub = e.subjecte.subject = 'hi there'
class Email(object): ...
@property def subject(self): return self.subject_widget.text()
@subject.setter def subject(self, subject): self.subject_widget.setText(subject) ...
Nice.
class Email(object):
def __init__(self, sender, subject, message): self._sender_widget = QLineEdit(sender) self._subject_widget = QLineEdit(subject) self._message_widget = QLineEdit(message)
@property def sender(self): return self._sender_widget.text()
@sender.setter def sender(self, sender): self._sender_widget.setText(sender)
@property def subject(self): return self._subject_widget.text()
@subject.setter def subject(self, subject): self._subject_widget.setText(subject)
@property def message(self): return self._message_widget.text()
@message.setter def message(self, message): self._message_widget.setText(message)
class Email(object):
def __init__(self, sender, subject, message): self._sender_widget = QLineEdit(sender) self._subject_widget = QLineEdit(subject) self._message_widget = QLineEdit(message)
@property def sender(self): return self._sender_widget.text()
@sender.setter def sender(self, sender): self._sender_widget.setText(sender)
@property def subject(self): return self._subject_widget.text()
@subject.setter def subject(self, subject): self._subject_widget.setText(subject)
@property def message(self): return self._message_widget.text()
@message.setter def message(self, message): self._message_widget.setText(message)
Gross
DescriptorsReusable Properties
http://www.freeimageslive.co.uk/files/images005/locust_leaf.JPG
class TextWrapper(object):
def __init__(self, widget_name): self.widget_name = widget_name
def __get__(self, object): widget = getattr(object, self.widget_name) return widget.text()
def __set__(self, object, value): widget = getattr(object, self.widget_name) widget.setText(value)
class Email(object): sender = TextWrapper('sender_widget') subject = TextWrapper('subject_widget') message = TextWrapper('message_widget')
def __init__(self, sender, subject, message): self.sender_widget = QLineEdit(sender) self.subject_widget = QLineEdit(subject) self.message_widget = QLineEdit(message)
...
...
class TextWrapper(object):
def __init__(self, widget_name): self.widget_name = widget_name
def __get__(self, object): widget = getattr(object, self.widget_name) return widget.text()
def __set__(self, object, value): widget = getattr(object, self.widget_name) widget.setText(value)
class Email(object): sender = TextWrapper('sender_widget') subject = TextWrapper('subject_widget') message = TextWrapper('message_widget')
def __init__(self, sender, subject, message): self.sender_widget = QLineEdit(sender) self.subject_widget = QLineEdit(subject) self.message_widget = QLineEdit(message)
e = Email()
sender = e.sender
e.sender = 'foo'
...
...
e = Email()
sender = e.sender
e.sender = 'foo'...
class TextWrapper(object):
def __init__(self, widget_name): self.widget_name = widget_name
def __get__(self, object): widget = getattr(object, self.widget_name) return widget.text()
def __set__(self, object, value): widget = getattr(object, self.widget_name) widget.setText(value)
class Email(object): sender = TextWrapper('sender_widget') subject = TextWrapper('subject_widget') message = TextWrapper('message_widget')
def __init__(self, sender, subject, message): self.sender_widget = QLineEdit(sender) self.subject_widget = QLineEdit(subject) self.message_widget = QLineEdit(message)
e = Email()
sender = e.sender
e.sender = 'foo'
class TextWrapper(object):
def __init__(self, widget_name): self.widget_name = widget_name
def __get__(self, object): widget = getattr(object, self.widget_name) return widget.text()
def __set__(self, object, value): widget = getattr(object, self.widget_name) widget.setText(value)
class Email(object): sender = TextWrapper('sender_widget') subject = TextWrapper('subject_widget') message = TextWrapper('message_widget')
def __init__(self, sender, subject, message): self.sender_widget = QLineEdit(sender) self.subject_widget = QLineEdit(subject) self.message_widget = QLineEdit(message)
e = Email()
sender = e.sender
e.sender = 'foo'
Nice.
class TextWrapper(object):
def __init__(self, widget_name): self.widget_name = widget_name
def __get__(self, object): widget = getattr(object, self.widget_name) return widget.text()
def __set__(self, object, value): widget = getattr(object, self.widget_name) widget.setText(value)
class Email(object): sender = TextWrapper('sender_widget') subject = TextWrapper('subject_widget') message = TextWrapper('message_widget')
def __init__(self, sender, subject, message): self.sender_widget = QLineEdit(sender) self.subject_widget = QLineEdit(subject) self.message_widget = QLineEdit(message)
e = Email()
sender = e.sender
e.sender = 'foo'
Nice.
Nice.
class TextWrapper(object):
def __init__(self, widget_name): self.widget_name = widget_name
def __get__(self, object): widget = getattr(object, self.widget_name) return widget.text()
def __set__(self, object, value): widget = getattr(object, self.widget_name) widget.setText(value)
class Email(object): sender = TextWrapper('sender_widget') subject = TextWrapper('subject_widget') message = TextWrapper('message_widget')
def __init__(self, sender, subject, message): self.sender_widget = QLineEdit(sender) self.subject_widget = QLineEdit(subject) self.message_widget = QLineEdit(message)
e = Email()
sender = e.sender
e.sender = 'foo'
Nice.
Nice.
Meh?class TextWrapper(object):
def __init__(self, widget_name): self.widget_name = widget_name
def __get__(self, object): widget = getattr(object, self.widget_name) return widget.text()
def __set__(self, object, value): widget = getattr(object, self.widget_name) widget.setText(value)
class Email(object): sender = TextWrapper('sender_widget') subject = TextWrapper('subject_widget') message = TextWrapper('message_widget')
def __init__(self, sender, subject, message): self.sender_widget = QLineEdit(sender) self.subject_widget = QLineEdit(subject) self.message_widget = QLineEdit(message)
Go forth and refactor
Descriptor
Recipeshttp://epicthings.net/wp-content/uploads/2011/09/Cookie-Monster-Cookie.jpg
Instance-specific data
class Foo(object): x = Descriptor()
f1 = Foo()f2 = Foo()
f1.x = 5f2.x = 4
class Descriptor(object):
def __init__(self): self._data = {}
def __get__(self, instance): return self._data[instance]
Accessing Descriptor Methods
class Descriptor(object)
def __get__(self, instance): if instance == None: return self
def cool_descriptor_method(self): pass
desc = Foo.x # instance = Nonedesc.cool_descriptor_method()