Python #cls.decorator - python

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

Related

How to automatically "register" methods in a python class as a list class variable?

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

decorating methods causes method to pass in objects [duplicate]

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

Pass method to function using method name from string variable

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.

Python: Use private static method as default

I have a class member which accepts a function:
class A:
def func(self, method):
...
I want to set a default method since that behavior is desired 99% of the time.
This default behavior is static since it does not depend on any members of the class. However, I would like this default method to be private and invisible to the user. Is there any way of accomplishing that?
This is what I have tried:
class A:
#staticmethod
def __meth(x):
pass
def func(self, method = meth):
pass
Error: 'staticmethod' object is not callable
class A:
#staticmethod
def __meth(x):
pass
def func(self, method = A.__meth):
pass
Error: NameError: name 'A' is not defined
class A:
#staticmethod
def __meth(x):
pass
def func(self, method = self.__meth):
pass
Error: NameError: name 'self' is not defined
I am using Python 3.5 and do not want to rely on newer features.
It's fairly idiomatic to use None as the default and assign it as needed:
class A:
#staticmethod
def __meth(x):
print(x)
def func(self, method=None):
if method is None:
method = self.__meth
method("x")
The problems start with your default parameter. These parameters are evaluated whilst the class definition is being read, and so class A is not yet defined.
You should handle it like a normal default parameter:
class A:
#staticmethod
def __meth(x):
print('meth')
def func(self, method = None):
if method is None:
self.__meth(1)
else:
method()
def foo():
print('foo')
a = A()
a.func()
a.func(foo)
Output:
meth
foo
You can delay name resolution by putting it into a lambda:
class A:
#staticmethod
def __meth(x):
pass
def func(self, method = lambda s: A.__meth(s)):
pass

Python : Set method attribute from within method

I am trying to make a python decorator that adds attributes to methods of a class so that I can access and modify those attributes from within the method itself. The decorator code is
from types import MethodType
class attribute(object):
def __init__(self, **attributes):
self.attributes = attributes
def __call__(self, function):
class override(object):
def __init__(self, function, attributes):
self.__function = function
for att in attributes:
setattr(self, att, attributes[att])
def __call__(self, *args, **kwargs):
return self.__function(*args, **kwargs)
def __get__(self, instance, owner):
return MethodType(self, instance, owner)
retval = override(function, self.attributes)
return retval
I tried this decorator on the toy example that follows.
class bar(object):
#attribute(a=2)
def foo(self):
print self.foo.a
self.foo.a = 1
Though I am able to access the value of attribute 'a' from within foo(), I can't set it to another value. Indeed, when I call bar().foo(), I get the following AttributeError.
AttributeError: 'instancemethod' object has no attribute 'a'
Why is this? More importantly how can I achieve my goal?
Edit
Just to be more specific, I am trying to find a simple way to implement static variable that are located within class methods. Continuing from the example above, I would like instantiate b = bar(), call both foo() and doo() methods and then access b.foo.a and b.doo.a later on.
class bar(object):
#attribute(a=2)
def foo(self):
self.foo.a = 1
#attribute(a=4)
def doo(self):
self.foo.a = 3
The best way to do this is to not do it at all.
First of all, there is no need for an attribute decorator; you can just assign it yourself:
class bar(object):
def foo(self):
print self.foo.a
self.foo.a = 1
foo.a = 2
However, this still encounters the same errors. You need to do:
self.foo.__dict__['a'] = 1
You can instead use a metaclass...but that gets messy quickly.
On the other hand, there are cleaner alternatives.
You can use defaults:
def foo(self, a):
print a[0]
a[0] = 2
foo.func_defaults = foo.func_defaults[:-1] + ([2],)
Of course, my preferred way is to avoid this altogether and use a callable class ("functor" in C++ words):
class bar(object):
def __init__(self):
self.foo = self.foo_method(self)
class foo_method(object):
def __init__(self, bar):
self.bar = bar
self.a = 2
def __call__(self):
print self.a
self.a = 1
Or just use classic class attributes:
class bar(object):
def __init__(self):
self.a = 1
def foo(self):
print self.a
self.a = 2
If it's that you want to hide a from derived classes, use whatever private attributes are called in Python terminology:
class bar(object):
def __init__(self):
self.__a = 1 # this will be implicitly mangled as __bar__a or similar
def foo(self):
print self.__a
self.__a = 2
EDIT: You want static attributes?
class bar(object):
a = 1
def foo(self):
print self.a
self.a = 2
EDIT 2: If you want static attributes visible to only the current function, you can use PyExt's modify_function:
import pyext
def wrap_mod(*args, **kw):
def inner(f):
return pyext.modify_function(f, *args, **kw)
return inner
class bar(object):
#wrap_mod(globals={'a': [1]})
def foo(self):
print a[0]
a[0] = 2
It's slightly ugly and hackish. But it works.
My recommendation would be just to use double underscores:
class bar(object):
__a = 1
def foo(self):
print self.__a
self.__a = 2
Although this is visible to the other functions, it's invisible to anything else (actually, it's there, but it's mangled).
FINAL EDIT: Use this:
import pyext
def wrap_mod(*args, **kw):
def inner(f):
return pyext.modify_function(f, *args, **kw)
return inner
class bar(object):
#wrap_mod(globals={'a': [1]})
def foo(self):
print a[0]
a[0] = 2
foo.a = foo.func_globals['a']
b = bar()
b.foo() # prints 1
b.foo() # prints 2
# external access
b.foo.a[0] = 77
b.foo() # prints 77
While You can accomplish Your goal by replacing self.foo.a = 1 with self.foo.__dict__['a'] = 1 it is generally not recommended.
If you are using Python2 - (and not Python3) - whenever you retrieve a method from an instance, a new instance method object is created which is a wrapper to the original function defined in the class body.
The instance method is a rather transparent proxy to the function - you can retrieve the function's attributes through it, but not set them - that is why setting an item in self.foo.__dict__ works.
Alternatively you can reach the function object itself using: self.foo.im_func - the im_func attribute of instance methods point the underlying function.
Based on other contributors's answers, I came up with the following workaround. First, wrap a dictionnary in a class resolving non-existant attributes to the wrapped dictionnary such as the following code.
class DictWrapper(object):
def __init__(self, d):
self.d = d
def __getattr__(self, key):
return self.d[key]
Credits to Lucas Jones for this code.
Then implement a addstatic decorator with a statics attribute that will store the static attributes.
class addstatic(object):
def __init__(self, **statics):
self.statics = statics
def __call__(self, function):
class override(object):
def __init__(self, function, statics):
self.__function = function
self.statics = DictWrapper(statics)
def __call__(self, *args, **kwargs):
return self.__function(*args, **kwargs)
def __get__(self, instance, objtype):
from types import MethodType
return MethodType(self, instance)
retval = override(function, self.statics)
return retval
The following code is an example of how the addstatic decorator can be used on methods.
class bar(object):
#attribute(a=2, b=3)
def foo(self):
self.foo.statics.a = 1
self.foo.statics.b = 2
Then, playing with an instance of the bar class yields :
>>> b = bar()
>>> b.foo.statics.a
2
>>> b.foo.statics.b
3
>>> b.foo()
>>> b.foo.statics.a
3
>>> b.foo.statics.b
5
The reason for using this statics dictionnary follows jsbueno's answer which suggest that what I want would require overloading the dot operator of and instance method wrapping the foo function, which I am not sure is possible. Of course, the method's attribute could be set in self.foo.__dict__, but since it not recommended (as suggested by brainovergrow), I came up with this workaround. I am not certain this would be recommended either and I guess it is up for comments.

Categories

Resources