This question already has answers here:
How can I access variables from the caller, even if it isn't an enclosing scope (i.e., implement dynamic scoping)?
(4 answers)
Closed 6 months ago.
I want to do this (dummy example):
def func():
nonlocal var
print (var)
class A:
var = 'hola'
func()
But I get: "SyntaxError: no binding for nonlocal 'var' found"
What I really intend to do is append a method name to a list in the scope of the class if that method is decorated. Something like this:
def decorator(func):
nonlocal decorated
decorated.append(func.__name__)
return func
class A:
decorated = []
#decorate
def f(self):
pass
Python just doesn't let you do this. You can access the class namespace by using locals(). But at this point, you might as well just pass the variable you're interested in to the decorator.
# using locals()
def decorator(class_namespace):
def _decorator(func):
class_namespace["decorated"].append(func)
return func
return _decorator
class A:
store = decorator(locals())
decorated = []
#store
def func(self):
pass
del store
Generally, it's easy to use a pair of decorators. One to mark the functions you're interested in, and one to collect them.
from types import FunctionType
def collect(cls):
for item in vars(cls).values():
print(item)
if isinstance(item, FunctionType) and getattr(item, "marked", False):
cls.marked_funcs.append(item)
return cls
def mark(func):
func.marked = True
return func
#collect
class B:
marked_funcs = []
#mark
def func(self):
pass
But in your case it might just be simpler to create the set of function names at the end of the class. eg.
class C:
def func(self):
pass
func_names = [f.__name__ for f in [func]]
Use the decorator to mark the functions, then have decorated be a class method which returns all decorated functions.
import inspect
def decorate(func):
func.decorated = True
return func
class A:
def foo():
print('foo')
#decorate
def bar():
print('bar')
#classmethod
def decorated(cls):
def is_decorated_method(obj):
try:
return inspect.isfunction(obj) and obj.decorated
except AttributeError:
# The object has no decorated attribute
return False
return [result[1] for result in inspect.getmembers(cls, predicate=is_decorated_method)]
print(A.decorated())
# [<function A.bar at 0x6ffffc0aa60>]
y = 20
def f():
x = 7
def g():
nonlocal x # 7
global y # 20
nonlocal qualifier refers to a name in the outer function scope, which does not include the module scope. While global does the complementary. So you are using nonlocal incorrectly.
How about that?
decorated = []
def decorator(func):
decorated.append(func.__name__)
def wrapper(self):
print('wrapper called')
func(self)
return wrapper
class A:
#decorator
def f(self): print('function called')
print(decorated)
A().f()
OUTPUT:
['f']
wrapper called
function called
NOTES:
The code you provided experiencing the issue you've described because var is class variable so it must be referenced as A.var but you cannot do that in your code because you try to use it before A is defined. if it were different classes it would be possible:
class X:
decorated = []
def decorator(func):
X.decorated.append(func.__name__)
return func
class A:
#decorator
def f(self):
pass
print(X.decorated)
Please note that you don't need to specify nonlocal if you don't assign to variable but call methods like append().
Related
I want to write a class Tool whose some member functions will be processed by another class, so I want to mark them explicitly and elegantly. For example,
class Tool:
def __init__(self):
self._marked = []
#mark # how to add func1 to self._marked?
def func1():
"""I will be processed"""
def func2():
"""I will not be processed"""
def marked_funcs():
"""return all marked functions"""
return self._marked
However, the function decorator mark cannot access self, so I cannot add the decorated functions to self._marked. How can I do this, or is there any other elegant way to achieve this requirement?
I think this is close to what you want to achieve.
I find it difficult to design a decorator to edit the class object (self._marked in the original code). So I create a "global" storage for the marked functions (_marked).
When invoking the mark decorator, this function is added to the _marked set.
I define the marked_funcs as a class method. This is because what the decorator receive is Tool.func1, not the method of the instance.
As a result, _marked_funcs returns its member functions that are marked.
_marked = set()
def mark(func):
_marked.add(func)
return func
def is_marked(func):
return callable(func) and (func in _marked)
# test
#mark
def one():
return 1
is_marked(one)
# True
class Tool:
#mark # how to add func1 to self._marked?
def func1():
"""I will be processed"""
def func2():
"""I will not be processed"""
#classmethod
def marked_funcs(cls):
"""return all marked functions"""
out = []
for name in dir(cls):
obj = getattr(cls, name)
if is_marked(obj):
out.append(obj)
return out
t = Tool()
t.marked_funcs()
# [<function __main__.Tool.func1()>]
Tool.marked_funcs()
# [<function __main__.Tool.func1()>]
I have a simple idea and it works. There is no need to use a list to store marked functions. I can add new property to the functions that I need to mark, and when necessary, I search for functions which has this property through traversal. For example:
def register(func):
func.x = 1
return func
class A:
#register
def hello1(self, a, b, c):
print(f"a={a}, b={b}, c={c}")
#register
def hello2(self, a, b, c):
print(f"a={a}, b={b}, c={c}")
def registered_functions(self):
res = []
for func_name in dir(self):
func = getattr(self, func_name)
if hasattr(func, 'x'):
res.append(func)
return res
if __name__ == '__main__':
a = A()
print(a.registered_functions())
# [<bound method A.hello1 of <__main__.A object at 0x7f7bd815aca0>>, <bound method A.hello2 of <__main__.A object at 0x7f7bd815aca0>>]
I've a decorator which takes parameters.
def decorator(abc):
def inner(func):
def wrapper(*args, **kwargs):
print(abc) # prints base class variable
# do something
return True
return wrapper
return inner
I've Child-Parent Classes as follow:-
class ABC():
abc = 1
#decorator(abc)
def func(self):
return True
class DEF(ABC):
abc = 2
obj = DEF()
print(obj.func())
The problem that I'm facing is while passing child class variable to the decorator, it still takes the base class variable.
How do I pass the child class variable to the decorator?
Your decorator is being passed a concrete value at definition time of the base class and its function. There is no attribute access whatsoever and the function does not get magically redefined and redecorated with a new value in the derived class.
It also makes little sense to pass a class attribute to a method decorator in the first place as the method has dynamic access to it via the passed instance anyway. You can do the much simpler:
def decorator(func):
def inner(self, *args, **kwargs):
print(self.__class__.abc) # actually prints the class attribute
# do something
return True
return inner
class ABC(object):
abc = 1
#decorator
def func(self):
return True
>>> a = ABC()
>>> a.func()
1
True
>>> b = DEF()
>>> b.func()
2
True
A simple way would be to considere that what you want to process in the decorator is the run time value of an attribute. Then you just pass the attribute name and use it at run time. That is enough, because a method gets its object as its first parameter:
def decorator(attr):
def inner(func):
def wrapper(*args, **kwargs):
print(getattr(args[0], attr)) # prints current attribute
# do something
return True
return wrapper
return inner
class ABC():
abc = 1
#decorator('abc')
def func(self):
return True
class DEF(ABC):
abc = 2
obj = DEF()
obj.func()
gives as expected:
2
True
One other solution could be to override the method in DEF, since according to DEF.__mro__, since python cannot find the method declared in DEF, it will go to parent class and find it there, passing in ABC's class attribute.
Output of DEF.__mro__:
(__main__.DEF, __main__.ABC, object)
A proposed solution:
class ABC(object):
abc = 1
#decorator(abc)
def func(self):
return True
class DEF(ABC):
abc = 2
#decorator(abc)
def func(self):
return True
Given a class and a set of its methods - how can you determine from within that class which methods have been decorated with a certain decorator?
My goal is to basically get the actual values of the methods that are decorated, so something like:
class A():
def get_values(self):
...
# returned {'a-special-name': 1, 'b': 2}
#my_dec('a-special-name') # Ideally be able to also put optional name
def a(self):
return 1
#my_dec
def b(self):
return 2
Any idea on how to accomplish this?
Edit: this should also work on parent classes, that is, if A is a subclass of:
class B():
#my_dec
def c(self):
return 3
then get_values() of A instance should return {'a-special-name': 1, 'b': 2, 'c': 3} (order is irrelevant of course)
Edit: class based decorator that works but not with inheritance. Any idea how to make it work with inheritance but without having to decorate the class itself?
class my_dec(object):
def __init__(self, func, name=None):
self.func = func
self.name = name or func.__name__
self.func._some_flag = True
def __get__(self, instance, cls=None):
if instance is None:
return self
return self.func(instance)
If you can define the decorator yourself, then simply have it "mark" the method object in some way:
def my_dec(method):
method._this_be_decorated = True
return method
The class can then look for those marked methods; something like:
from inspect import isfunction
class A:
def get_values(self):
return filter(lambda i: isfunction(i) and hasattr(i, '_this_be_decorated'),
vars(type(self)).values())
This will return an iterable of function objects which you can process further as needed.
def my_dec(name):
if callable(name):
# name is callable – take its name
func = name # no make the code more readable
func.special_name = func.__name__
return func
else:
# name is the name to give – add an inner layer of functions
def inner(function_object):
function_object.special_name = name
return function_object
return inner
class A():
def get_values(self):
# return a dict of special name to call result mapping for every class member that has a special_name.
return {func.special_name: func(self) for func in self.__class__.__dict__.values() if hasattr(func, 'special_name')}
# returned {'a-special-name': 1, 'b': 2}
#my_dec('a-special-name') # Ideally be able to also put optional name
def a(self):
return 1
#my_dec
def b(self):
return 2
def no_dec(self):
return 42
should do what you want.
As deceze mentions, decorators can do whatever they want so there's no reliable generic answer. If you "own" the decorator you can add special properties to it's return value ie (Q&D py2.7 example):
def mydec(name=''):
# py27 hack - for py3 you want nonlocal instead
n = [name]
def innerdec(func):
# py27 hack - for py3 you want nonlocal instead
name = n[0] or func.__name__
def wrapper(*args, **kw):
print("in mydec.wrapper for {}".format(name))
return func(*args, **kw)
wrapper.ismydec = True # so we know this is decorated by mydec
wrapper.func = func # so we can get the original func
wrapper.name = name
return wrapper
return innerdec
def collect_dec(cls):
decorated = {}
for attname in dir(cls):
obj = getattr(cls, attname)
if getattr(obj, "ismydec", False):
decorated[obj.name] = obj.func
cls._decorated_funcs = decorated
return cls
#collect_dec
class A():
def get_values(self):
return {
name:func(self) for name, func in self._decorated_funcs.items()
}
#mydec('a-special-name') # Ideally be able to also put optional name
def a(self):
return 1
#mydec() # no name
def b(self):
return 2
a = A()
print(a.get_values())
Which outputs:
{'a-special-name': 1, 'b': 2}
I know there is similar question, but my scenario is somehow different: refer to codes:
class MyClass(object):
def __init__(self, log_location)
self.logs = logging(log_location) # create log object by the log_location, this object should be used by the decorator fucntion
def record_log(log_object):
""" this is the decorator function
"""
def deco(func):
def wrap(*args, **kwargs):
rs = func()
# use log object to record log
if rs:
log_object.record('success')
else:
log_object.record('fail')
return wrap
return deco
#record_log(self.logs)
def test(self):
rs = do_some_thing
if rs:
return True
return False
def main():
my_class = MyClass()
my_class.test()
But, there is an error like this:
#record_log(self.logs)
NameError: name 'self' is not defined
Hos should I use the instance attribute self.logs in a decorator function in such scenario like this??
Thanks very much!
You can not pass a reference to self or any attribute of self at this point. The #record_log line is executed (the method is decorated) before the code in main is executed, i.e. before any instance of MyClass is created at all -- in fact, even before the definition of MyClass has been completed! But remember that
#record_log(self.logs)
def test(self, n):
is actually just syntactic sugar for
test = record_log(self.logs)(test)
So one way to work around your problem would be to redefine test in your __init__, i.e.
def __init__(self, log_location)
self.logs = logging(log_location)
self.test = record_log(self.logs)(self.test)
Also note that your decorator is not passing any parameters to func and not returning the results. Also, it should probably be defined on module level (before the class).
def record_log(log_object):
def deco(func):
def wrap(*args, **kwargs):
rs = func(*args, **kwargs) # pass parameters
if rs:
log_object.record('success')
else:
log_object.record('fail')
return rs # return result
return wrap
return deco
There are several objections about your code:
deco() is redundant. You can directly return wrap from record_log().
If you only plan to decorate MyClass's methods, then there is no point in passing log_object to the decorator, as self.logs will always be used. Otherwise, consider moving the decorator to module level, as already suggested by others.
The decorated method's return value is currently lost.
The call to the decorated function does not pass self to it.
The proper code would therefore be:
class MyClass(object):
def __init__(self, log_location):
self.logs = logging(log_location)
def record_log(func):
""" this is the decorator function
"""
def wrap(self):
rs = func(self)
# use log object to record log
if rs:
print 1
self.logs.record('success')
else:
print 2
self.logs.record('fail')
return rs
return wrap
#record_log
def test(self):
rs = do_some_thing
if rs:
return True
return False
This question already has answers here:
Attaching a decorator to all functions within a class
(11 answers)
Closed 5 years ago.
I have several classes and they have same implements name but difference realization. I want to decorate all methods in some classes but others not. I have thought about inheritance, but some classes have some methods do not need to be decorated. The problem is that I don't want to decorate methods one by one, some classes they need to be decorated by a same decorator, Is there any solution to fix it?
Your can start all method that required to be decorated with some prefix and then use something like this:
class Xobject(object):
def __init__(self, decorator):
for method_name in dir(self):
if method_name.startswith("dec_"):
attr = getattr(self, method_name)
wrapped = decorator(attr)
setattr(self, method_name, wrapped)
def dec_me_1(self):
print("In dec_me1")
return 0
def dec_me_2(self):
print("In dec_me2")
return 1
def decorator(func):
def wrapped(*args):
print("TEST")
return func(*args)
return wrapped
x = Xobject(decorator)
x.dec_me_1()
x.dec_me_2()
UPDATE:
You can decorate class by mean of function below. When using Python you should know that class in Python is also object so you could change it and pass it to the other function.
def decorator(func):
def wrapped(*args):
print("TEST")
return func(*args)
return wrapped
def decorate_object(p_object, decorator):
for method_name in dir(p_object):
if method_name.startswith("dec_"):
attr = getattr(p_object, method_name)
wrapped = decorator(attr)
setattr(p_object, method_name, wrapped)
decorate_object(Xobject, decorator)
x = Xobject()
x.dec_me_1()
x.dec_me_2()
Also your can decorate already instantiated object same way:
x = Xobject()
x.dec_me_1()
x.dec_me_2()
decorate_object(x, decorator)
x.dec_me_1()
x.dec_me_2()
I'm sure there are a few approaches to this, but the main leading options are:
Create a custom metaclass, where the __new__ method iterates across the attribute dictionary, identifies methods, and decorates them. See http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/ for an example of Python metaclass programming. Disadvantages: that may be more complex than we'd want to get into here.
Do the same in a regular class's __init__ method. Disadvantages: that only decorates instance methods and not class or static methods, and it's slower because it runs every time you create a new instance.
Do it outside the class:
class Foo(object):
def bar(self):
print 'bar'
for name, ref in vars(Foo):
if callable(ref): ...
Disadvantages: You only get one chance to do it right: at import time. Subclasses don't get modified.
Do it in a class-level decorator. Same disadvantages as doing it outside the class (I think).
At some point you have to be explicit about what gets wrapped and what doesn't.
If I've understood you correctly, I think you could do something like this:
def wrapper(func):
def inner(*args, **kwargs):
print "%s was called" func.__name__
return func(*args, **kwargs)
return inner
class A(object):
def foo(self):
print "foo called"
def bar(self):
print "BAR CALLED"
class B(A):
#wrapper
def foo(self):
super(B, self).foo()
class C(A):
#wrapper
def bar(self):
super(C, self).bar()
Stick = A()
Dave = B()
Jupiter = C()
Jupiter.foo() #prints "foo called"
Jupiter.bar() #prints "bar wrapped" and "BAR CALLED"