Descriptors
Python's most obscure language feature demystified.
In 5 minutes.
Chris Beaumont
@BeaumontChris
graduate student, astrophysics
Harvard 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 code
interact 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
Properties
Disguise 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.subject
e.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.subject
e.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
Descriptors
Reusable 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
Recipes
http://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 = 5
f2.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 = None
desc.cool_descriptor_method()

Python Descriptors Demystified

  • 1.
    Descriptors Python's most obscurelanguage feature demystified. In 5 minutes. Chris Beaumont @BeaumontChris graduate student, astrophysics Harvard University / University of Hawaii
  • 2.
    Learning about descriptors createsan appreciation for the elegance of Python's design. -Raymond Hettinger
  • 3.
    Learning about descriptors createsan appreciation for the elegance of Python's design. -Raymond Hettinger I DON'T GET IT
  • 4.
    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 code interact with email data?
  • 5.
    class Email(object): ... def get_subject(self): returnself.subject_widget.text() def set_subject(self, subject): self.subject_widget.setText(subject) ... e = Email() e.set_subject('Hi there') sub = e.get_subject()
  • 6.
    class Email(object): ... def get_subject(self): returnself.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
  • 7.
  • 8.
    class Email(object): ... @property def subject(self): returnself.subject_widget.text() @subject.setter def subject(self, subject): self.subject_widget.setText(subject) ...
  • 9.
    e = Email() sub= e.subject e.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) ...
  • 10.
    e = Email() sub= e.subject e.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.
  • 11.
    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)
  • 12.
    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
  • 13.
  • 14.
    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) ... ...
  • 15.
    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' ... ...
  • 16.
    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)
  • 17.
    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)
  • 18.
    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)
  • 19.
    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)
  • 20.
    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)
  • 21.
  • 23.
  • 24.
    Instance-specific data class Foo(object): x= Descriptor() f1 = Foo() f2 = Foo() f1.x = 5 f2.x = 4 class Descriptor(object): def __init__(self): self._data = {} def __get__(self, instance): return self._data[instance]
  • 25.
    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 = None desc.cool_descriptor_method()