Created
April 17, 2012 22:09
-
-
Save kra3/2409397 to your computer and use it in GitHub Desktop.
ProxyProperty: Properties with proxy & instance varibale emulation
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
# -*- coding: utf-8 -*- | |
__author__ = "Arun KR (kra3) <the1.arun@gmail.com>" | |
__license__ = "Simplified BSD" | |
''' | |
Can be found at: https://gist.github.com/2409397 | |
Public Clone URL: git://gist.github.com/2409397.git | |
Private Clone URL: git@gist.github.com:2409397.git | |
''' | |
class ProxyProperty(object): | |
''' | |
How to use: | |
1) For class variable: set <is_class_var> to True, also pass in a <name> | |
2) For instance variable: just create an object with a <name>; | |
3) Optionally provide (in case #1 & #2), gfn & sfn: those should be closures or functions, which take a single value and transform it into another value. | |
PS: You should catch errors in gfn/sfn, if you need different behaviour than class is provided. | |
Default behaviour if gfn raised error will be: default value will be send back, which is None. | |
Default behaviour if sfn raised error will be: value will not be stored. | |
PS: a instance/class variable using provided 'name' will be created in the original class, automatically. | |
it will not be accessible as <name> in the object. If you really need it, use _ProxyProperty__<name> | |
What if second option is not there? | |
-> You can't emulate instance variable behaviour. | |
-> provided the purpose of doing all this is to transform data when getting & saving using closures. | |
Behaviour when applied to a class variable: | |
-> As instance variable if by default, as class variable if argument <is_class_var> is True. | |
Now: Here are some examples: | |
>>> class B(object): | |
... aa = ProxyProperty('aa', True) # creating class variable | |
... bb = ProxyProperty('bb') # this is instance variable | |
... def __init__(self): | |
... self._bb = '' | |
>>> b=B() | |
>>> b.aa | |
>>> B.aa | |
>>> b.bb | |
>>> B.bb | |
>>> #b.aa = 1 # this will raise Exception | |
>>> b.bb = 2 | |
>>> b.bb | |
2 | |
>>> B.aa = 1 | |
>>> B.aa | |
1 | |
>>> b.aa | |
1 | |
''' | |
def __init__(self, name, is_class_var=False, gfn=lambda s:s, sfn=lambda s:s): | |
self.is_class_var = is_class_var | |
# prepend name with _ProxyProperty__, so that there will be no clash in actual object about storing variable that we create (automatically) in originating class | |
self.name = "_ProxyProperty__%s" % name | |
self.gfn = gfn | |
self.sfn = sfn | |
def __get__(self, instance, owner): | |
try: | |
if not self.is_class_var: | |
value = self.gfn(getattr(instance, self.name)) | |
else: | |
value = self.gfn(getattr(owner, self.name)) | |
except: | |
return None # default value | |
else: | |
return value | |
def __set__(self, instance, value): | |
try: | |
value = self.sfn(value) | |
except: | |
# sfn caused error, so we will not save value | |
pass | |
else: | |
if not self.is_class_var: | |
setattr(instance, self.name, value) | |
else: | |
setattr(owner, self.name, value) | |
if __name__ == '__main__': | |
''' Run as "python discriptor_magic.py -v" to see verbose doctest result''' | |
import doctest | |
doctest.testmod() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment