I have this class:
class Foo(object):
handlers = []
def __init__(self):
pass
def run(self):
pass
def foo(self):
pass
def bar(self):
pass
How could I implement decorator #collect_handler
class Foo(object):
handlers = []
def __init__(self):
pass
def run(self):
pass
#collect_handler
def foo(self):
pass
#collect_handler
def bar(self):
pass
so that:
foo = Foo()
foo.handlers # [foo, bar]
?
is this possible?
class Foo(object):
handlers = []
def collect_handler(handlers):
def wrapper(func):
handlers.append(func)
return func
return wrapper
collect_handler = collect_handler(handlers)
def __init__(self):
pass
def run(self):
pass
#collect_handler
def foo(self):
pass
#collect_handler
def bar(self):
pass
foo = Foo()
print(foo.handlers)
yields
[<function foo at 0xb770d994>, <function bar at 0xb770d9cc>]
These are not unbound methods; they are just plain functions. (There is no checking that the the first argument is an instance of Foo.) However, they should suffice. (Note in Python3 there are no more unbound methods; the distinction between unbound methods and plain functions was removed.)
Just an alternate way without using decorators.
f = Foo()
[m for m in dir(f) if getattr(f,m).__doc__ == "COLLECT"]
The above statement uses List comprehension in Python.
dir is a built-in function which would return all attributes of an object.
getattr is a built-in function to retrieve an attribute of an object.
__doc__ is a python variable which holds the docstring for any python artifacts.
This should be your class definition:
class Foo(object):
def __init__(self):
pass
def run(self):
pass
def foo(self):
"COLLECT"
pass
def bar(self):
"COLLECT"
pass
Related
Consider this situation:
class Foo(ABC):
#abstractmethod
def some_method(self):
return
class Bar(Foo):
def some_method(self, param):
# do stuff
return
class Baz(Foo):
def some_method(self, param):
# do stuff differently
return
def do_something_with_obj(some_obj: Foo):
some_param = 'stuff'
some_obj.some_method(some_param)
def main(cond):
if cond:
obj = Bar()
else:
obj = Baz()
do_something_with_obj(obj)
I get an Expected 0 positional arguments error when I try to call some_method() under the do_something_with_obj() method. Of course, this is because I'm essentially calling the abstract method. My question is, how can I dynamically refer to the child class method since I have to choose the right child class based on some condition beforehand?
I want to create a python decorator that adds a certain function to a list of functions of that class that will, sometimes, be processed. Example code:
class A:
# every subclass should define _list
#classmethod
def decorator(cls, f):
# cls = B
cls._flist.append(f)
return f
#classmethod
def processFunctions(cls):
for f in cls._flist:
...
class B(A):
_flist = []
#B.decorator # Obviously not possible because cls is not defined (yet)
def foo(self):
print("Inside foo")
Is it possible to replicate this behaviour? The class (cls) should be passed when decorating the function, so I can't use the usual approach of creating a wrapper function that "unpacks" cls and the other arguments.
Ok, I think I've worked something out.
You need an instance of A, but as a class variable inside B.
Then each method will need an instance:
class A:
def __init__(self):
self._flist = []
def decorator(self, f):
self._flist.append(f)
return f
def processFunctions(self, other):
for f in self._flist:
f(other)
class B:
a=A()
#a.decorator
def foo(self):
print("Inside foo")
def processFunctions(self):
B.a.processFunctions(self)
b = B()
b.processFunctions()
Output
Inside foo
The following way is based on the implementation behavior of locals() in CPython but there is PEP 558 to make it documented standard behavior:
class A:
# every subclass should define _list
#staticmethod
def decorator(loc):
def registrator(f):
loc['_flist'].append(f)
return f
return registrator
#classmethod
def processFunctions(cls):
for f in cls._flist:
...
class B(A):
_flist = []
#decorator(locals())
def foo(self):
print("Inside foo")
Another approach, like the one the package ABC uses is making the decorator add a flag to the function, and then go through the functions of this class that have that flag activated.
class A:
#staticmethod
def decorator(f):
def wraps(f)
f.__processable__ = True
return f
return wraps
def processFunctions(self):
for d in dir(self):
try:
f = getattr(self, d).__func__
if f.__processable__:
f() # Or whatever we want to do with the function
# Instead of try/except we could use a bunch of nested ifs
except AttributeError:
pass
When defining a Python class, I'd like to use decorators to register some of its methods into a class variable list. Here's an example of incorrect python that outlines what I'm looking for:
class MyClass:
dangerous_methods = []
#classmethod
def dangerous_method(cls, func):
cls.dangerous_methods.append(func)
return func
#MyClass.dangerous_method
def incinerate(self):
pass
def watch_tv(self):
pass
#MyClass.dangerous_method
def stab(self):
pass
def print_dangerous_methods(self):
print(self.dangerous_methods)
obj = MyClass()
obj.print_dangerous_methods()
with the expected output being
[<function MyClass.incinerate at 0x000001A42A629280>, <function MyClass.stab at 0x000001A42A629281>]
Is it possible to do this without torturing Python too much?
All you really want to do is to set dangerous on the methods. Remember that python functions and methods are first-class objects, you can set arbitrary attributes on them.
def print_dangerous_methods(cls):
""" yes, you could also return a list """
for name in dir(cls):
f = getattr(cls, name)
if callable(f) and getattr(f, "dangerous", False):
print(name)
def dangerous(func):
setattr(func, "dangerous", True)
return func
class MyClass:
#dangerous
def incinerate(self):
print("incinerate")
def watch_tv(self):
pass
#dangerous
def stab(self):
return "you've been stabbed"
class_born_dangerous = print_dangerous_methods
print("\non instance")
obj = MyClass()
print_dangerous_methods(obj)
print("\non class")
print_dangerous_methods(MyClass)
print("\nand yes, they work")
obj.incinerate()
print (obj.stab())
print("\nas a classmethod")
obj.class_born_dangerous()
output:
on instance
incinerate
stab
on class
incinerate
stab
and yes, they work
incinerate
you've been stabbed
as a classmethod
incinerate
stab
If you want to generalize this approach and set arbitrary attributes, you need to set up a parametrized decorator:
def annotate_func(**kwds):
"""set arbitrary attributes"""
def actual_decorator(func):
for k, v in kwds.items():
setattr(func, k, v)
return func
return actual_decorator
which you would use as follows:
#annotate_func(dangerous=1,range=1000)
def shoot(self, times):
for i in range(0, times):
print("bang!")
This is one way to implement that:
class MyClass:
def __init__(self):
self.dangerous_methods = []
def dangerous_method(func):
def inner(self):
self.dangerous_methods.append(func)
return func(self)
return inner
#dangerous_method
def incinerate(self):
print('Incinerate called!')
pass
def watch_tv(self):
print('Watch_tv called!')
pass
#dangerous_method
def stab(self):
print('Stab called!')
pass
def print_dangerous_methods(self):
print(self.dangerous_methods)
obj = MyClass()
obj.incinerate()
# Incinerate called!
obj.watch_tv()
# Watch_tv called!
obj.stab()
# Stab called!
obj.incinerate()
# Incinerate called!
obj.print_dangerous_methods()
# [<function MyClass.incinerate at 0x0000029C11666EE8>, <function MyClass.stab at 0x0000029C11666B88>, <function MyClass.incinerate at 0x0000029C11666EE8>]
Just note that in this way, functions are being added to the list ONLY once they've called and there is a risk that a function being added to the list multiple times. However, if you know that there are some functions in mind that you want to add to the list and they're constants, you can simply add them while the object is being constructed:
class MyClass:
def __init__(self):
self.dangerous_methods = [self.incinerate, self.stab]
def incinerate(self):
print('Incinerate called!')
pass
def watch_tv(self):
print('Watch_tv called!')
pass
def stab(self):
print('Stab called!')
pass
def print_dangerous_methods(self):
print(self.dangerous_methods)
obj = MyClass()
obj.print_dangerous_methods()
# [<bound method MyClass.incinerate of <__main__.MyClass object at 0x0000029C11388F08>>, <bound method MyClass.stab of <__main__.MyClass object at 0x0000029C11388F08>>]
The following snippet does exactly what you described.
Note that print_dangerous_methods is declared as a class method, because that’s what it really is (it applies to the class, not to a certain instance). That means you can call it even without creating an instance.
class MyClass:
def dangerous_method(meth):
meth.is_dangerous = True
return meth
#dangerous_method
def incinerate(self):
pass
def watch_tv(self):
pass
#dangerous_method
def stab(self):
pass
#classmethod
def print_dangerous_methods(cls):
print ([
meth for meth in [
getattr(cls, methname) for methname in dir(cls)
]
if getattr(meth, "is_dangerous", False)
])
MyClass.print_dangerous_methods()
This question already has answers here:
Python: Bind an Unbound Method?
(5 answers)
Closed 2 years ago.
Problem Description
I want to use a decorator to define a class method, but this requires me to manually give the 'self' object when I shouldn't have to provide that.
def func_wrapper(func):
def call_func(self):
print(self.a)
func()
return call_func
def func():
print('hello')
class test:
def __init__(self, func):
self.a = 0
self.call_func = func_wrapper(func)
mytest = test(func)
#mytest.call_func() #why does this not work?
mytest.call_func(mytest) #this works
I want to be able to mytest.call_func() but this doesn't work, presumably because call_func is bound to the func_wrapper and not mytest. If I manually pass in the object, e.g. mytest.call_func(mytest) this will work, but I don't want to have to manually pass in the object - this creates inconsistent call signatures if one inherited the test class and wrote their own call_func method, because then the method would be properly bound to the class.
Solution Attempts
def func_wrapper2(func, obj):
def call_func():
print(obj.a)
func()
return call_func
class test:
def __init__(self, func):
self.a = 0
self.call_func = func_wrapper2(func, self)
Here is a solution which lets me test.call_func() as desired, but here func_wrapper is not a true decorator as it requires to be passed in the object as well.
Looking on the web I found this blog https://medium.com/#vadimpushtaev/decorator-inside-python-class-1e74d23107f6 which talks about this issue and recommends to define the decorator either in a nested class, or a helper class. However their solution doesn't seem to work and I am getting type errors from passing the wrong number of inputs.
class test2:
class test2helper:
#classmethod
def func_wrapper(func):
print(self.a)
func()
def __init__(self):
self.a = 0
#test2helper.func_wrapper
def call_func(self):
print('hello')
So what is the proper way to use decorators with class methods? Every way to do it seems to cause different issues with how the self is being handled. I am going to use the func_wrapper2 design unless there is a better way to do this.
You are missing one level:
class test2:
class test2helper:
#classmethod
def decorator(cls, func): # this must return a function!
def func_wrapper(self): # ... namely this one, the "wrapper"
print(self.a) # ... where you have access to the instance
func(self) # ... upon which the method is called
return func_wrapper
def __init__(self):
self.a = 0
#test2helper.decorator
def call_func(self):
print('hello')
>>> t = test2()
>>> t.call_func()
0
hello
Or, if you want to go with the earlier attempt without nested class:
def decorator(func): # you are decorating an unbound function!
def func_wrapper(obj):
print(obj.a)
func(obj) # which has to be passed all the arguments
return func_wrapper
class test:
def __init__(self):
self.a = 0
#decorator
def call_func(self):
print('hello')
You can define a class decorator to do what you want:
def class_decorator(cls):
def call_func(self):
print(self.a)
return func()
setattr(cls, 'call_func', call_func)
return cls
def func():
print('hello')
#class_decorator
class Test:
def __init__(self, func):
self.a = 0
mytest = Test(func)
mytest.call_func() # This now works.
Output:
0
hello
I have a class with a constructor, two other methods, and a member list with the names of the two methods.
class Foo():
def __init__(self):
self.methods = ["self.foo", "self.bar"]
def foo(self):
print("foo")
return 0
def bar(self):
print("bar")
return 0
I have a function that takes a function as an argument, like this.
myFunction(func)
The function has global scope and would be used like this.
myFunction(self.foo)
I want to iterate through the items in the self.methods list and make a call to the function for each method name, but, as expected, a string is passed rather than the method itself. How do I pass the method like the above example, so like self.foo not "self.foo"?
From what I understand you can try this.
class Foo():
def __init__(self):
self.method=['foo','bar']
def foo(self):
print('foo')
def bar(self):
print('bar')
def run_all(self):
for m in self.method:
getattr(self,m)()
a=Foo()
a.run_all() # iterating through self.method and executing them
# foo
# bar
You want somwthing like this?
class Foo():
def __init__(self):
self.methods = [self.foo(), self.bar()]
def foo(self):
print("foo")
return 0
def bar(self):
print("bar")
return 0
foo_obj = Foo()
What about:
class Foo():
def __init__(self):
self.methods = self.foo, self.bar
def run_methods(self):
for method in self.methods:
print('Running method {}'.format(method.__name__))
method()
def foo(self):
print("foo")
return 0
def bar(self):
print("bar")
return 0
So, you can run your methods by calling run_methods. If you want to access their names as well, you can always do that via their respective __name__ methods, as above.
f = Foo()
f.run_methods()
# Output:
#
# Running method foo
# foo
# Running method bar
# bar
EDIT: As another person suggested, you should edit your question to describe in more detail what myFunction(fun) does. But inded, you should probably use a different approach than passing the actual names as strings.