What is the name of this design pattern with "cascading attributes"? - python

Say I have the following classes:
DO_NOT_OVERRIDE = type() # this dictates if an attribute won't be overridden.
class Parent():
def __init__(self, a: int = 0, b: int = 0):
self.a = a
self.b = b
class Child():
def __init__(self, parent: Parent, a: int = DO_NOT_OVERRIDE, b: int = DO_NOT_OVERRIDE):
self.parent = parent
self._a = a
self._b = b
#property
def a(self):
if self._a is DO_NOT_OVERRIDE:
return self.parent.a
return self._a
#a.setter
def a(self, value: int):
self._a = value
#property
def b(self):
if self._b is DO_NOT_OVERRIDE:
return self.parent.b
return self._b
#b.setter
def b(self, value: int):
self._b = value
Now, let's create some objects.
parent_obj = Parent(a = 1, b = 2)
child_obj_1 = Child(parent = parent_obj)
child_obj_1.a would return 1 and child_obj_1.b would return 2, which are both values from parent.a and parent.b respectively
But consider another Child:
child_obj_2 = Child(parent_obj, a = 20)
child_obj_2.a would return 20 which is a value set in child_obj_2, though child_obj_2.bwould still return2sinceb` is not "overridden" by the child object.
What is this design pattern?

Related

Access to class that intantiated another class

Considering I have the following classes:
class A:
def __init__(self, origin, value):
self.origin = origin
self.origin.b1 = value
class B:
def __init__(self):
self.b1 = 0
self.b2 = A(self, 1)
print(self.b1)
b = B()
I wish to know if there is any way of avoiding passing the self when instancing class A, thus getting the origin (class B) of the instance from within the class A (replacing of course the "???"):
class A:
def __init__(self, value):
self.origin = ???
self.origin.b1 = value
class B:
def __init__(self):
self.b1 = 0
self.b2 = A(1)
print(self.b1)
b = B()

how to create multiple property decorators in python

How can I create multiple property decorators with self defined function as getter and setter based on following class structure? I have try to use
setattr(self, 'a', property(_to_get('a'), _to_set('a'))) but it does not work.
class ABC:
def __init__(self):
pass
def _to_get(self, attr):
return something_function(attr)
def _to_set(self, attr, value):
dosomething_function(attr, value)
#property
def a(self):
res = self._to_get('a')
return res.split(' ')[0]
#a.setter
def a(self, value)
self._to_set('a', value)
#property
def b(self):
res = self._to_get('b')
return res.split(' ')[1]
#b.setter
def b(self, value)
self._to_set('b', value)
#property
def c(self):
res = self._to_get('c')
return res.split(' ')[2]
#c.setter
def c(self, value)
self._to_set('c', value)
No reason why something like this wouldn't work:
class A(object):
def __init__(self):
self._a = None
#property
def a(self):
return self._a
#a.setter
def a(self, x):
self._a = x
#a.deleter
def a(self):
del self._a
#property
def b(self):
return self._b
#b.setter
def b(self, x):
self._b = x
#b.deleter
def b(self):
del self._b
#property
def c(self):
return self._c
#c.setter
def c(self, x):
self._c = x
#c.deleter
def c(self):
del self._c
Consider your original class written without decorator syntax. (The translation may not be 100% accurate, but should be close enough to illustrate the point I want to make.)
class ABC:
def _to_get(self, attr):
return something_function(attr)
def _to_set(self, attr, value):
dosomething_function(attr, value)
a = property(lambda self: ABC._to_get(self, 'a').split(' ')[0],
lambda self, value: ABC._to_set(self, 'a', value))
b = property(lambda self: ABC._to_get(self, 'b').split(' ')[1],
lambda self, value: ABC._to_set(self, 'b', value))
c = property(lambda self: ABC._to_get(self, 'c').split(' ')[2],
lambda self, value: ABC._to_set(self, 'c', value))
a, b and c are all basically the same thing, but parameterized
by the name of the property and an integer.
def make_getter(attr, x):
def getter(self):
return self._to_get(attr).split(' ')[x]
return getter
def make_setter(attr):
def setter(self, value):
self._to_set(attr, value)
return setter
class ABC:
def _to_get(self, attr):
return something_function(attr)
def _to_set(self, attr, value):
dosomething_function(attr, value)
a = property(make_getter('a', 0), make_setter('a'))
b = property(make_getter('b', 1), make_setter('b'))
c = property(make_getter('c', 2), make_setter('c'))
Something like the following should also work (not heavily tested), moving the logic into a subclass of property.
class Foo(property):
def __init__(self, x):
super().__init__(self._to_get, self._to_set)
self.x = x
# name is the class attribute the instance of Foo
# will be assigned to
def __set_name__(self, owner, name):
self.attr = name
# In both of the following, obj is the instance that actually
# invokes the parameter. You would probably want to pass it
# to something_function and do_something as well.
def _to_get(self, obj):
return something_function(self.attr).split(' ')[self.x]
def _to_set(self, obj, value):
do_something(self.attr, value)
class ABC:
a = Foo(0) # Will call a.__set_name__(ABC, 'a')
b = Foo(1) # Will call b.__set_name__(ABC, 'b')
c = Foo(2) # Will call c.__set_name__(ABC, 'c')

Decorator and Inheritance with parameters

There is a decorator with inheritance. It works well:
class bar(object):
def __init__(self,val):
self.val = val
#staticmethod
def decor(func):
def increment(obj, x):
return func(obj, x) + obj.val
return increment
class foo(bar):
def __init__(self):
bar.__init__(self)
#bar.decor
def add(self, x):
return x
But I want to add a parameter in the class foo:
class foo(bar):
def __init__(self,B):
bar.__init__(self)
self.B = B
And I want to input B into the decorator as an parameters, I've tried a scratch:
class bar(object):
def __init__(self,val):
self.val = val
#staticmethod
def decor(B):
def wrap(func):
def increment(obj, x):
return func(obj, x) + obj.val + B
return increment
return wrap
class foo(bar):
def __init__(self,B):
bar.__init__(self)
self.B = B
#bar.decor(B)
def add(self, x):
return x
But it didn't work. What am I doing wrong?
class bar(object):
def __init__(self, val):
self.val = val
#staticmethod
def decor(func):
def increment(obj, x):
return func(obj, x) + obj.val + obj.B
return increment
class foo(bar):
def __init__(self,val,B):
bar.__init__(self,val)
self.B = B
#bar.decor
def add(self, x):
return x
aa = foo(4, 1.5)
a = aa.add(1)
print(a)

How to memoize a property in Python?

Consider the following minimal example:
class Foo(object):
def __init__(self):
self.b = self.a = 1
#property
def sum(self):
print 'Recalculating sum'
return self.a + self.b
foo = Foo()
print foo.sum
print foo.sum # Prints 'Recalculating sum' even though neither a or b has changed since previous call
foo.a = 2
print foo.sum # a has been changed to 2 so recalculation is necessary
I would like to memoize sum such that if self.a and self.b doesn't change, then we don't need to keep recalculating the property.
The property should only be recalculated when either self.a or self.b has changed -- is there an simple way to do this?
python3:
from functools import lru_cache as memoized
#property
#memoized(maxsize=1)
def sum(self):
return self.a + self.b
python 3.8
from functools import cached_property
#cached_property
def sum(self):
return self.a + self.b
Use properties for a and b too and clear up your cache in the setters:
class Foo(object):
def __init__(self):
self.a = 1
self.b = 1
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
self._clearsum()
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b = value
self._clearsum()
def _clearsum(self):
self._sum = None
#property
def sum(self):
if self._sum is None:
self._sum = self.a + self.b
return self._sum
Or if you want something a bit more generic, you can check this too:
Storing calculated values in an object
Edit : someone recently suggested adding self._sum = None in __init__ to "avoid an error when accessing sum", but that's actually not necessary - __init__ invokes a.setter, which invokes _clearsum, which sets the _sum attribute, so it's garanteed self._sum will be created whatever.
there is a module that does this. Pypi link here: https://pypi.org/project/memoized-property/
For the above code I have this with using the module:
In [2]: from memoized_property import memoized_property
In [3]: class test():
...: def __init__(self):
...: self.a = 0
...: self.b = 0
...: #memoized_property
...: def sum(self):
...: print('calculating...')
...: return self.a + self.b
In [4]: t=test()
calculating...
In [5]: t.sum
Out[5]: 0
In [7]: t.a=5
In [8]: t.sum
Out[8]: 0

How to implement this mechanism:

i want to implement a dynamic relation mechanism with python something like:
a:=10
b:=30
c:=a+b
print c
a+=20
print c
output:
40
60
c is always result of a+b.
so if a or b change, then c automatically updates value. i write a code in C# and do this by set and get mechanism. now want to translate it to python code for using in another program (FontLab Studio 5). I'm not so familiar with Python. does he have a get,set feature like C#? if not ho to implement one?
This is probably overkill, but it illustrates how you should create getters/setters in Python and achieve the functionality you want:
class Calc(object):
def __init__(self, a = 0, b = 0):
self._a = a
self._b = b
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b = value
#property
def c(self):
return self._a + self._b
def __str__(self):
return str(self.c)
calc = Calc()
calc.a = 1
calc.b = 2
print calc.c
calc.a += 10
print calc.c
If you don't want to make a and b a property, the code can be simplified:
class Calc(object):
def __init__(self, a = 0, b = 0):
self.a = a
self.b = b
#property
def c(self):
return self.a + self.b
def __str__(self):
return str(self.c)
In your situation, c actually is a function which must be called.
You could use something like this:
a = 10
b = 30
c = lambda: a + b
print c()
a += 20
print c()
If you dislike that the method call is made explicit for c, you could use a general Calc object, which hides this implementation:
class Calc(object):
def __init__(self):
object.__setattr__(self, '_params', dict())
def __getattr__(self, name):
param = self._params[name]
if callable(param):
return param()
else:
return param
def __setattr__(self, name, value):
self._params[name] = value
def __delattr__(self, name):
del self._params[name]
And then you could do:
c = Calc()
c.a = 10
c.b = 30
c.c = lambda: c.a + c.b
print c.c
c.a += 20
print c.c
New-style Python classes support properties.
something like this:
class C:
def __init__(self):
self.x = 0
self.y = 0
def get(self):
return self.x + self.y
def __str__(self):
return self.__unicode__()
def __unicode__(self):
return str(self.get())
c = C()
c.x = 1
print c
c.y =2
print c
With new style classes and annotations you can probably make it better.

Categories

Resources