Skip to content

Instantly share code, notes, and snippets.

@artfwo
Created January 15, 2021 20:27
Show Gist options
  • Save artfwo/99217c0cc0419cc4d635d63da501c244 to your computer and use it in GitHub Desktop.
Save artfwo/99217c0cc0419cc4d635d63da501c244 to your computer and use it in GitHub Desktop.
Pure Python runtime property to expression binding example using descriptors
#! /usr/bin/env python3
#
# Pure Python runtime property to expression binding example using descriptors
# (see below for usage example).
#
# This Property implementation allows setting a value using lambda expressions.
# The value will be recalculated if any property accessed during lambda evaluation
# changes its value.
class Property:
_capture_observer = None
def __init__(self, default_value=None):
self.default_value = default_value
def __get__(self, instance, owner=None):
if self._capture_observer is not None:
if self.observers_name not in instance.__dict__:
instance.__dict__[self.observers_name] = set()
instance.__dict__[self.observers_name].add(self._capture_observer)
value = instance.__dict__.get(self.value_name)
return value or self.default_value
def __set__(self, instance, value_or_func):
if callable(value_or_func):
instance.__dict__[self.value_func_name] = value_or_func
self._capture_observer = (instance, self)
instance.__dict__[self.value_name] = value_or_func()
self._capture_observer = None
else:
instance.__dict__[self.value_func_name] = None
instance.__dict__[self.value_name] = value_or_func
if self.observers_name in instance.__dict__:
observers = instance.__dict__[self.observers_name]
if len(observers) > 0:
notify_observers = observers.copy()
observers.clear()
for instance, p in notify_observers:
p.update_value(instance)
def __set_name__(self, owner, name):
self.value_name = '__property_{}_value'.format(name)
self.value_func_name = '__property_{}_func'.format(name)
self.observers_name = '__property_{}_observers'.format(name)
def update_value(self, instance):
value_func = instance.__dict__[self.value_func_name]
self._capture_observer = instance, self
instance.__dict__[self.value_name] = value_func()
self._capture_observer = None
class Employee:
salary = Property()
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
bob = Employee(name='Bob', salary=50)
tom = Employee(name='Tom', salary=lambda: bob.salary * 2)
print("before:")
print("bob's salary:", bob.salary) # 50
print("tom's salary:", tom.salary) # 100
bob.salary = 60
print("after:")
print("bob's salary:", bob.salary) # 60
print("tom's salary:", tom.salary) # 120
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment