Skip to content

Instantly share code, notes, and snippets.

@is55555
Last active February 20, 2017 11:18
Show Gist options
  • Save is55555/de79c7bc981ebf78330435f7af491788 to your computer and use it in GitHub Desktop.
Save is55555/de79c7bc981ebf78330435f7af491788 to your computer and use it in GitHub Desktop.
basic example outlining how to use descriptors
# ***** "raw" descriptor not using "property" or property decorators.
class Descriptor(object): # simply encapsulates the management of the attribute "_name"
def __init__(self):
self._name = ''
def __get__(self, instance, owner):
print "Getting: %s" % self._name
return self._name
def __set__(self, instance, name):
print "Setting: %s" % name
self._name = name.title() # title just makes the first letter in every word of the string upper-case
def __delete__(self, instance):
print "Deleting: %s" %self._name
del self._name
class Person(object):
name = Descriptor()
# ***** an equivalent descriptor, but this time using "property"
class PersonUsingDescriptorViaProperty(object):
def __init__(self):
self._name = ''
def fget(self):
print "Getting: %s" % self._name
return self._name
def fset(self, value):
print "Setting: %s" % value
self._name = value.title()
def fdel(self):
print "Deleting: %s" %self._name
del self._name
name = property(fget, fset, fdel, "Property docstring")
# ***** another equivalent descriptor, using property decorators
class PersonDescriptorViaPropertyDecorators(object):
def __init__(self):
self._name = ''
@property
def name(self):
print "Getting: %s" % self._name
return self._name
@name.setter
def name(self, value):
print "Setting: %s" % value
self._name = value.title()
@name.deleter
def name(self):
print "Deleting: %s" % self._name
del self._name
# Another approach is adding properties in runtime. This allows for not overriding getter, setter and deleter separately
# for every and each attribute. However I think it's generally what you want to do, so I don't think this pattern is very
# useful and this is apparent even in this basic example.
# I'd stick to the examples above.
class PersonRuntimeDescriptor(object):
def addProperty(self, attribute):
# create local getter and setter
getter = lambda self: self._getProperty(attribute)
setter = lambda self, value: self._setProperty(attribute, value)
# construct property attribute and add it to the class
setattr(self.__class__, attribute, property(fget=getter, \
fset=setter, \
doc="Auto-generated property"))
def _setProperty(self, attribute, value):
print "Setting: %s = %s" %(attribute, value)
setattr(self, '_' + attribute, value.title()) # note that applying title or any other specific function
# to every property is not typically what we want to do.
# This is partly why I don't think this runtime style is a very useful pattern.
# Generally you'd want specific management to every attribute that isn't the same for every property.
# This is achieved better and more clearly with the methods outlined above.
def _getProperty(self, attribute):
print "Getting: %s" %attribute
return getattr(self, '_' + attribute)
if __name__ == "__main__":
for user in (Person(), PersonUsingDescriptorViaProperty(), PersonDescriptorViaPropertyDecorators()):
# each iteration should work the same
user.name = 'winston churchill'
print user.name
del user.name
# trying to access user.name would give an AttributeError now because the attribute was removed rather than just its content
print "\n-----\n"
# it's also possible to do it at runtime by using setattr and getattr
print "**** runtime descriptor ****"
user = PersonRuntimeDescriptor()
user.addProperty('name')
user.addProperty('email')
user.name = 'christopher columbus'
user.email = '[email protected]'
print user.name, user.email, user.__dict__
> python descriptorExample.py
Setting: winston churchill
Getting: Winston Churchill
Winston Churchill
Deleting: Winston Churchill
-----
Setting: winston churchill
Getting: Winston Churchill
Winston Churchill
Deleting: Winston Churchill
-----
Setting: winston churchill
Getting: Winston Churchill
Winston Churchill
Deleting: Winston Churchill
-----
**** runtime descriptor ****
Setting: name = christopher columbus
Setting: email = [email protected]
Getting: name
Christopher Columbus Getting: email
[email protected] {'_email': '[email protected]', '_name': 'Christopher Columbus'}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment