Decorator and Inheritance with parameters - python

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)

Related

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

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?

Log common method among classes in python

I am importing several classes from a library with a common method, like
class BarClass1:
def __init__(self):
pass
def bar(self, x):
return x + 1
class BarClass2:
def __init__(self):
pass
def bar(self, x):
return x + 2
class BarClass3:
def __init__(self):
pass
def bar(self, x):
return x + 3
I want to add logging to the bar method of each class, and for that purpose I create children for these classes in the following way:
def log_something(x):
print(f'input is {x}')
class DerivedBarClass1(BarClass1):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
class DerivedBarClass2(BarClass2):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
class DerivedBarClass3(BarClass3):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
I feel I am doing a lot of code repetition, is there a simpler way of doing this? My main constraint is not being able to modify the code in BarClass1, BarClass2 or BarClass3.
If you can't modify the code, you can always monkey-patch the classes...
import functools
def add_logging_single_arg(f): # maybe a better name...
#functools.wraps(f)
def wrapper(self, x):
log_something(x)
return f(x)
return wrapper
for klass in [BarClass1, BarClass2, BarClass3]:
klass.bar = add_logging_single_arg(bar)

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')

Override and extend method defined in parent class in Python

I want to redefine the method Old.do(self) in New.do(self, x) so that it takes one argument as below:
#!/usr/bin/env python3
class Old(object):
def __init__(self):
self.a = 0
self.do()
def do(self):
print(self.a)
class New(Old):
def __init__(self):
Old.__init__(self)
b = 1
self.do(b)
def do(self, b):
print(self.a + b)
if __name__ == '__main__':
new = New()
I can do it with the name mangling:
#!/usr/bin/env python3
class Old(object):
def __init__(self):
self.a = 0
self.__do()
def do(self):
print(self.a)
__do = do
class New(Old):
def __init__(self):
Old.__init__(self)
b = 1
self.do(b)
def do(self, b):
print(self.a + b)
if __name__ == '__main__':
new = New()
or I can do it with an explicit reference to the base class:
#!/usr/bin/env python3
class Old(object):
def __init__(self):
self.a = 0
Old.do(self)
def do(self):
print(self.a)
class New(Old):
def __init__(self):
Old.__init__(self)
b = 1
self.do(b)
def do(self, b):
print(self.a + b)
if __name__ == '__main__':
new = New()
Is there any other way to get the same result? Can super() do this?
Thanks
You can do what you trying with super, like this:
class Old(object):
def __init__(self):
self.a = 1
Old.do(self, self.a)
def do(self, a):
print(a)
class New(Old):
def __init__(self):
super(New, self).__init__()
self.b = 2
self.do(self.b)
def do(self, b):
print(self.a + b)
new = New()
the first call will return 1, and the second call 3

How to pass a function as a parameter to a class in python

I want to pass a function to a class when I initialize it. Here's a toy example I came up with and it works:
def addition(self):
return self.a + self.b
def multiplication(self):
return self.a * self.b
class Test:
def __init__(self, a, b, fcn):
self.a = a
self.b = b
self.fcn = fcn
t = Test(3, 3, addition)
print t.fcn(t)
t = Test(3, 3, multiplication)
print t.fcn(t)
Is it possible to simply call t.fcn() as you would any other class method?
did you try it?
the answer is yes
def do_op(x,y,fn):
return fn(x,y)
def add(a,b):
return a+b
print do_op(5,4,add)
same with a class
class whatever:
def __init__(self,fn):
self.fn = fn
def do_it(self,*args,**kwargs):
return self.fn(*args,**kwargs)
#if you wanted the fn to have self as the first argument
#return self.fn(self,*args,**kwargs) #just pass self as first argument
x = whatever(add)
print x.do_it(5,8)
further along what you are asking for (if im reading it right)
def add(self):
return self.a + self.b
class whatever:
def __init__(self,fn,a,b):
self.__dict__[fn.__name__] = fn
self.a,self.b = a,b
def do_it(self):
return self.fn(self)
x = whatever(add,6,7)
x.do_it()
or perhaps you want something like
from functools import partial
def add(self):
return self.a + self.b
class whatever:
def __init__(self,fn,a,b):
self.__dict__[fn.__name__] = partial(fn,self)
self.a,self.b = a,b
x = whatever(add,5,6)
x.add()
this kind of introspection is somewhat risky in deployed code ...

Categories

Resources