Skip to content

Instantly share code, notes, and snippets.

@verdimrc
Last active May 4, 2017 07:23
Show Gist options
  • Save verdimrc/a990bc5ca18180916685cff422157787 to your computer and use it in GitHub Desktop.
Save verdimrc/a990bc5ca18180916685cff422157787 to your computer and use it in GitHub Desktop.
A Python property decorator that can chain decorated property.
#!/home/verdi/miniconda3/bin/python3.6
'''
Demonstrate a property decorator that can chain decorated property.
For chaining to work, decoratee must be a plain function or a property with
fget attribute.
Sample outputs:
(py36) verdi@verdi-VirtualBox:/tmp$ ./haha.py
########
class Test...
########
deco.__call__(fget=<function Test.x at 0xb6f0a614>)
deco.__call__(fget=<function Test.x at 0xb6f0a614>)
deco.__call__(fget=<function Test._y at 0xb6f0a5cc>)
########
class TTest(...)
########
deco.__call__(fget=<function TTest.x at 0xb6f0a584>)
deco.__call__(fget=<__main__.deco object at 0xb6f183ec>)
deco.__call__(fget=<property object at 0xb6f28824>)
########
t.x, t.y, t.x2
########
[decoratee == plain old function] deco(darg=100): name=x, <function Test.x at 0xb6f0a614>, val=10
[decoratee == plain old function] deco(darg=200): name=y, <function Test._y at 0xb6f0a5cc>, val=20
[decoratee == plain old function] deco(darg=102): name=x2, <function Test.x at 0xb6f0a614>, val=10
results: 10 20 10
########
tt.x3, tt.y2
########
[decoratee == property or deco] deco(darg=103): name=x3, <__main__.deco object at 0xb6f183ec>, recursing...
[decoratee == plain old function] deco(darg=100): name=x, <function TTest.x at 0xb6f0a584>, val=11
=> deco(darg=103): recurse ended, val=11
[decoratee == property or deco] deco(darg=200): name=y2, <property object at 0xb6f28824>, recursing...
=> deco(darg=200): recurse ended, val=21
results: 11 21
y.setter invoked
[decoratee == property or deco] deco(darg=103): name=x3, <__main__.deco object at 0xb6f183ec>, recursing...
[decoratee == plain old function] deco(darg=100): name=x, <function TTest.x at 0xb6f0a584>, val=11
=> deco(darg=103): recurse ended, val=11
[decoratee == property or deco] deco(darg=200): name=y2, <property object at 0xb6f28824>, recursing...
=> deco(darg=200): recurse ended, val=44
results: 11 44
'''
class deco(object):
def __init__(self, darg=100):
self.darg = darg
def __call__(self, fget):
print(f'deco.__call__(fget={fget})')
self.fget = fget
return self
def __set_name__(self, owner, name):
# New since Python 3.6
self.name = name
def __get__(self, inst, owner):
if not hasattr(self.fget, 'fget'):
print(f'[decoratee == plain old function] deco(darg={self.darg}): name={self.name}, {self.fget}, ', end='')
val = self.fget(inst)
print(f'val={val}')
else:
print(f'[decoratee == property or deco] deco(darg={self.darg}): name={self.name}, {self.fget}, recursing...')
#descriptor = self.__dict__['fget']
#val = descriptor.__get__(inst, owner)
val = self.fget.__get__(inst, owner)
print(f' => deco(darg={self.darg}): recurse ended, val={val}')
return val
print('#' * 8)
print('class Test...')
print('#' * 8)
class Test(object):
@deco()
def x(self):
return 10
x2 = deco(darg=102)(x.fget)
def _y(self):
return 20
y = deco(darg=200)(_y)
print('\n' + '#' * 8)
print('class TTest(...)')
print('#' * 8)
class TTest(object):
def __init__(self):
self._y = 21
@deco()
def x(self):
return 11
x3 = deco(darg=103)(x)
@property
def y(self):
return self._y
@y.setter
def y(self, yy):
print("y.setter invoked")
self._y = yy
y2 = deco(darg=200)(y)
if __name__ == '__main__':
print('\n' + '#' * 8)
print('t.x, t.y, t.x2')
print('#' * 8)
t = Test()
print('results:', t.x, t.y, t.x2)
print('\n' + '#' * 8)
print('tt.x3, tt.y2')
print('#' * 8)
tt = TTest()
print('results:', tt.x3, tt.y2)
tt.y = 44
print('results:', tt.x3, tt.y2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment