Skip to content

Instantly share code, notes, and snippets.

@kra3
Created April 17, 2012 22:09
Show Gist options
  • Save kra3/2409397 to your computer and use it in GitHub Desktop.
Save kra3/2409397 to your computer and use it in GitHub Desktop.
ProxyProperty: Properties with proxy & instance varibale emulation
# -*- 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