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
Related
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
There is an answered question about classmethod and property combined together: Using property() on classmethods
I still don't understand the cause of the problem, please help.
My understanding of classmethod was that it simply replaces self with cls. With this in mind I wrote several classmethods during the past few years and now I see I was wrong all that time.
So what is the difference between #classmethod and #cm from the code below?
def cm(func):
def decorated(self, *args, **kwargs):
return func(self.__class__, *args, **kwargs)
return decorated
class C:
V = 0
#property
#classmethod
def inc1(cls):
cls.V += 1
print("V1 =", cls.V)
#property
#cm
def inc3(cls):
cls.V += 3
print("V3 =", cls.V)
c = C()
#c.inc1 # fails with: TypeError: 'classmethod' object is not callable
c.inc3 # works
inc3 with cm works, but inc1 with classmethod does not.
what is the difference between #classmethod and #cm from the code below?
decorator is calling during class creation time before an instance is created.
In your case, since #cm returns func(self.__class__, *args, **kwargs), which is relied on self, it should be used as a instance method.
On the other hand, #classmethod is able to use before an instance is created.
def cm(func):
def decorated(self, *args, **kwargs):
return func(self.__class__, *args, **kwargs)
return decorated
class C:
#classmethod
def inc1(cls):
(blablabla)
#cm
def inc3(cls):
(blablabla)
C().inc1() # works as a instance method
C.inc1() # works as a classmethod
C().inc3() # works as a instance method
C.inc3() # TypeError: unbound method decorated() must be called with C instance as first argument (got nothing instead)
For a combination of classmethod and property, it could be done by return an customized object. Reference
class ClassPropertyDescriptor(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
return self.f.__get__(obj, klass)()
def classproperty(func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
return ClassPropertyDescriptor(func)
class C:
#classproperty
def inc1(cls):
(blablabla)
C.inc1 # works as a classmethod property
[Edit]
Q. What does the classmethod() call do with the method it decorates to achieve that?
The implementation can be done by using descriptor
class ClassMethodDescriptor(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def newfunc(*args):
return self.f(klass, *args)
return newfunc
def myclassmethod(func):
return ClassMethodDescriptor(func)
class C:
#myclassmethod
def inc1(cls):
(blablabla)
C.inc1() # works as a classmethod
Q. Why is the result not callable?
Because the implementation of ClassMethodDescriptor does not define __call__ function. Once using #property, it will return ClassMethodDescriptor which is not callable.
The difference is that classmethod is not callable, and cm method is callable. This means that when the property(class) makes a call to the inputed func(which it is supposed to do), it works as you'll except for cm, but will not work for classmethod since classmethod does not have a call implemented.
class method does not know anything about instance and does not require it.
instance method knows about it's instance and it's class.
class Foo:
some = 'some'
class Bar(Foo):
def __init__(self):
self.some = 'not some'
#classmethod
def cls_some(cls):
print(cls.some)
def instance_some(self):
print(self.some)
Bar.cls_some()
>>>some
Bar().instance_some()
>>>not some
Also as you can see you don't need an instance to call classmethod.
How do I reference class object inside class definition? Could you advice me how you would do it? Or more specifically how do you pass class object inside decorator of class method?
Here is a simple example, I'm trying to pass second method I'm declaring to decorator of first one.
def decorate(w):
def _wrap(f):
def _call(*args, **kwargs):
return w(f(*args, **kwargs))
def _call
return _wrap
class A():
#dec(A.w)
def f():
return 2
def w(f):
return fr + 5
As expected exception is raised
NameError: name 'A' is not defined
As a result of my investigation i learned that globals() doesn't contain A key while i'm inside decorate or _wrap functions, but defined inside _call. So I could probably find passed method by string name (e.g #dec('A.w')), but in that case it is impossible to cache method search inside _wrap closure.
So how do you fix that? :)
You cannot, because during class definition, the class does not yet exist.
You can apply the decorator after the class has been created:
class A():
def f(self):
return 2
def w(self, f):
return fr + 5
A.f = dec(A.w)(A.f.__func__)
or you can swap the order of the two method definitions and refer to w as a local name still:
class A():
def w(self, f):
return fr + 5
#dec(w)
def f(self):
return 2
In both cases you are passing in a callable A.w that is not bound to an instance. No self will be passed in, so you need to add that yourself, in the decorator:
def decorate(w):
def _wrap(f):
def _call(self, *args, **kwargs):
return w(self, f(*args, **kwargs))
def _call
return _wrap
If you didn't expect w to be bound (it acting as a static method instead), you could just use a normal function instead here.
Generally speaking, creating a decorator to call another method on the same instance is somewhat pointless; why not just call w from within f, directly?
class A():
def f(self):
return self.w(2)
def w(self, f):
return fr + 5