Skip to content

Instantly share code, notes, and snippets.

@artfwo
Created January 15, 2021 20:27
Show Gist options
  • Select an option

  • Save artfwo/99217c0cc0419cc4d635d63da501c244 to your computer and use it in GitHub Desktop.

Select an option

Save artfwo/99217c0cc0419cc4d635d63da501c244 to your computer and use it in GitHub Desktop.

Revisions

  1. artfwo created this gist Jan 15, 2021.
    76 changes: 76 additions & 0 deletions property.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,76 @@
    #! /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