Skip to content

Instantly share code, notes, and snippets.

@willrazen
Created May 21, 2021 01:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willrazen/bef3fcb26a83dffb6692e5e10d3e67ac to your computer and use it in GitHub Desktop.
Save willrazen/bef3fcb26a83dffb6692e5e10d3e67ac to your computer and use it in GitHub Desktop.
Super wrapper which allows property setting & deletion.
class duper:
"""Super wrapper which allows property setting & deletion.
Super can't be subclassed with empty __init__ arguments.
Works with multiple inheritance.
References:
https://mail.python.org/pipermail/python-dev/2010-April/099672.html
https://bugs.python.org/issue14965
https://bugs.python.org/file37546/superprop.py
Usage: duper(super())
"""
def __init__(self, osuper):
object.__setattr__(self, 'osuper', osuper)
def _find(self, name):
osuper = object.__getattribute__(self, 'osuper')
if name != '__class__':
mro = iter(osuper.__self_class__.__mro__)
for cls in mro:
if cls == osuper.__thisclass__:
break
for cls in mro:
if isinstance(cls, type):
try:
return object.__getattribute__(cls, name)
except AttributeError:
pass
return None
def __getattr__(self, name):
return getattr(object.__getattribute__(self, 'osuper'), name)
def __setattr__(self, name, value):
osuper = object.__getattribute__(self, 'osuper')
desc = object.__getattribute__(self, '_find')(name)
if hasattr(desc, '__set__'):
return desc.__set__(osuper.__self__, value)
return setattr(osuper, name, value)
def __delattr__(self, name):
osuper = object.__getattribute__(self, 'osuper')
desc = object.__getattribute__(self, '_find')(name)
if hasattr(desc, '__delete__'):
return desc.__delete__(osuper.__self__)
return delattr(osuper, name)
def test_duper():
accessed = ''
class A:
def __init__(self):
self.value = 0
@property
def go(self):
nonlocal accessed
accessed += 'A'
return self.value
@go.setter
def go(self, value):
nonlocal accessed
accessed += 'A'
self.value = value
class B(A):
pass
class C(B):
@property
def go(self):
nonlocal accessed
accessed += 'C'
return duper(super()).go
@go.setter
def go(self, value):
nonlocal accessed
accessed += 'C'
duper(super()).go = value
class D(B):
@property
def go(self):
nonlocal accessed
accessed += 'D'
return duper(super()).go
@go.setter
def go(self, value):
nonlocal accessed
accessed += 'D'
duper(super()).go = value
class E(C,D):
@property
def go(self):
nonlocal accessed
accessed += 'E'
return duper(super()).go
@go.setter
def go(self, value):
nonlocal accessed
accessed += 'E'
duper(super()).go = value
e = E()
print('Getting initial value...')
print('Value:', e.go)
print('Access order:', accessed)
tmp = accessed
accessed = ''
print('\nSetting new value to 1...')
e.go = 1
print('Access order:', accessed)
assert tmp == accessed
assert accessed == 'ECDA'
print('Getting final value...')
print('Value:', e.go)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment