Last active
February 20, 2017 11:18
-
-
Save is55555/de79c7bc981ebf78330435f7af491788 to your computer and use it in GitHub Desktop.
basic example outlining how to use descriptors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ***** "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__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
> 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