Given the following example:
class A:
def f(self, x):
return 2*x
I would like to write another method which uses f above but adds a constant, i.e.
class A:
def f(self, x):
return 2*x
def g(self, x):
return self.f(x) + 10
This would be one way. However, this smells very much like decorating! What would be the proper pythonic way to do this?
Extending answer of #Ajax1234, you may try to:
- parametrize your decorator,
- call your decorator explicitly (without #).
def add_val(_value):
def decorator(f):
def wrapper(cls, _x):
return f(cls, _x) + _value
return wrapper
return decorator
class A:
def f(self, x):
return 2*x
g = add_val(10)(f)
[EDIT]
You may also improve the decorator with functools.wraps() (which is a decorator itself). All you need to do is to change your wrapper declaration:
#functools.wraps(f)
def wrapper(cls, _x):
return f(cls, _x) + _value
You can write a simple function outside the class:
def add_val(f):
def wrapper(cls, _x):
return f(cls, _x) + 10
return wrapper
class A:
#add_val
def f(self, x):
return 2*x
print(A().f(20))
Output:
50
Edit: you can utilize functools.wraps with a classmethod in Python3. The wrapped function f will return x*2 and the decorator g will add 10 to the returned result of the function passed to it. However, to be able to save the original functionality of f, you can utilize the __wrapped__ attribute:
import functools
def g(f):
#functools.wraps(f)
def wrapper(cls, _x):
return f(cls, _x)+10
return wrapper
class A:
#classmethod
#g
def f(cls, x):
return x*2
f_1 = functools.partial(A.f.__wrapped__, A)
print(A.f(4))
print(f_1(4))
Output:
18
8
Related
I am trying to enhance the default #property behavior in Python:
from functools import wraps
def MyProperty(func):
def getter(self):
"""Enhance the property"""
return func(self) + 1
return property(getter)
class MyClass(object):
def __init__(self, foo):
self._foo = foo
#MyProperty
def foo(self):
return self._foo
This all works very fine, I get the desired effect
A = MyClass(5)
A.foo
>>> 6
Since I have learned it that way, I want to apply the wraps decorator to the wrapper for good-practice reasons. But if I do write the wrapper as
def MyProperty(func):
#wraps
def getter(self):
"""Enhance the property"""
return func(self) + 1
return property(getter)
I now get
A = MyClass(5)
A.foo
>>> <__main__.MyClass object at 0x7f209f4aa0d0>
Which is not what I expect. Any suggestions?
Use this:
def MyProperty(func):
#wraps(func)
def getter(self):
"""Enhance the property"""
return func(self) + 1
return property(getter)
I am decorating a function foo with a_decorator
#a_decorator(params)
def foo(x):
# print('Called',decorator_name)
# other magic
Is there a way to access the name a_decorator inside foo so that I can print
'Called a_decorator'
def a_decorator(some_function):
def wrapper():
some_function()
return some_val
return wrapper
It can be done by attaching decorator name to wrapper function:
from functools import wraps
def a_decorator(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
val = fn(*args, **kwargs)
# some action
return val
wrapper.decorated_by = a_decorator
return wrapper
#a_decorator
def foo(x):
print(x, foo.decorated_by.__name__)
foo('test') # prints: test a_decorator
Function in python are first class and you can treat them as an object, attach attributes, etc.
I want to write class decorator which for all non-magic methods, to decorate these methods. The idea is that to all methods of class print its name after call; I do not want decor all methods but only class. Decorator log_method works. I have problem with log_class decorator. There are no errors and no output.
import traceback
import inspect
def log_method(func):
def inner(*args, **kwargs):
print("{}{}".format(int(len(traceback.extract_stack()) / 2) * " ", func.__name__))
return func(*args, **kwargs)
return inner
def log_class(cls):
for m in dir(cls):
if not m.startswith("__") and inspect.isfunction(getattr(cls, m)):
m = log_method(m)
print(m)
return cls
#log_class
class Cls:
def __init__(self):
pass
def A(self):
self.B()
def B(self):
self.C()
def C(self):
pass
Cls().A()
"""
Excepted output:
A
B
C
"""
You should rebind the method to the class object using setattr passing the method name m; assigning to the local name m like you've done, does nothing.
More so, you're currently passing m, a string, to log_method. Instead, you should pass the function object itself after retrieving via getattr:
def log_method(func):
def inner(*args, **kwargs):
print("{}{}".format(int(len(traceback.extract_stack()) / 2) * " ", func.__name__))
return func(*args, **kwargs)
return inner
def log_class(cls):
for m in dir(cls):
if not m.startswith("__") and inspect.isfunction(getattr(cls, m)):
setattr(cls, m, log_method(getattr(cls, m))) # here
return cls
Cls.A()
# A
# B
# C
PS: log_method is never used for decorating, so it's not a decorator.
I'd like to be able to use a wrapper on a class method:
def wrapper(f):
def inner(*args,**kwargs):
print 'inner '+f.__name__
x = f(*args,**kwargs)
return x
return inner
class A:
#wrapper
def f1(x=55):
return x
print A().f1()
This returns <main.A instance at 0x05FF7530>
How can I return the result of the wrapped function A.f1()?
You have forgotten the self-argument:
class A:
#wrapper
def f1(self, x=55):
return x
If you truly want to call it as stated in the question, make it a classmethod:
class A:
#classmethod
#wrapper
def f1(self, x=55):
return x
>>> A.f1()
inner f1
55
>>>
I have question about clean thory in Python. When:
#decorator_func
def func(bla, alba):
pass
Is equivalent to:
def func(bla, alba):
pass
func = decorator_func(func)
So:
#decorator_func(aaa, bar)
def func(bla, alba):
pass
Is equvalent to...?
It's equivalent to:
def func(bla, alba):
pass
func = decorator_func(aaa, bar)(func)
Or:
def func(bla, alba):
pass
decorator = decorator_func(aaa, bar)
func = decorator(func)
So in your second example, decorator_func should be a callable that returns a callable.
Here's an example of such a construction:
class prepend_two_arguments:
def __init__(self, a, b):
self.a = a
self.b = b
def __call__(self, f):
def wrapped_function(*args, **kwargs):
return f(self.a, self.b, *args, **kwargs)
return wrapped_function
#prepend_two_arguments(1,2)
def f(a, b, c):
return a+b+c
print(f(3)) # 6
And another one, using only functions:
def add_to_result(x):
def decorator(fn):
def wrapped_function(*args, **kwargs):
return fn(*args, **kwargs)+x
return wrapped_function
return decorator
#add_to_result(3)
def my_func(a, b):
return a+b
print(my_func(1,2)) # 6
Here's an example of a decorator function that works using closures:
def print_string_before(string):
def decorator_fn(fn):
def wrapped_fn(*args, **kwargs):
print string
return fn(*args, **kwargs)
return wrapped_fn
return decorator_fn
Note that decorators can equally return the decorated function (or class), having modified it in some way (e.g. setting an attribute).