TL;DR How do I find out whether a function was defined using #classmethod or something with the same effect?
My problem
For implementing a class decorator I would like to check if a method takes the class as its first argument, for example as achieved via
#classmethod
def function(cls, ...):
I found a solution to check for #staticmethod via the types module (isinstance(foo, types.UnboundMethodType) is False if the foo is static, see here), but did not find anything on how to do so for #classmethod
Context
What I am trying to do is something along the lines of
def class_decorator(cls):
for member in cls.__dict__:
if (isclassmethod(getattr(cls, member))):
# do something with the method
setattr(cls, member, modified_method)
return cls
and I do not know how to implement what I called isclassmethod in this example
If the object is a method object, and so has a method.__self__ attribute, and that attribute is the class you got the attribute from, then it'll take the class as the first argument. It has been bound to the class.
Note that you already have a bound object at this point, so you don't need to pass in the class again, unless you first extract the original function from method.__func__.
Here is an illustration, the class Foo has a class method bar and a regular method baz, which is not bound when you access it directly on the class:
>>> class Foo:
... #classmethod
... def bar(cls):
... pass
... def baz(self):
... pass
...
>>> Foo.baz
<function Foo.baz at 0x1097d1e18>
>>> Foo.bar
<bound method Foo.bar of <class '__main__.Foo'>>
>>> Foo.bar.__self__
<class '__main__.Foo'>
>>> Foo.bar.__self__ is Foo
True
Calling Foo.bar() automatically passes in Foo.bar.__self__ as the first argument.
If you need to test such methods, use inspect.ismethod(), and if that returns True test the __self__ attribute:
import inspect
if inspect.ismethod(cls.method) and cls.method.__self__ is cls:
# method bound to the class, e.g. a classmethod
This should work for any custom descriptors that work like classmethod does, as well.
If you need to know with certainty that the method was produced by a classmethod object, you'll need to look up the attributes directly in the class namespace (cls.__dict__ or vars(cls)), and do so in each class in the class hierarchy in method resolution order:
def isclassmethod(method):
bound_to = getattr(method, '__self__', None)
if not isinstance(bound_to, type):
# must be bound to a class
return False
name = method.__name__
for cls in bound_to.__mro__:
descriptor = vars(cls).get(name)
if descriptor is not None:
return isinstance(descriptor, classmethod)
return False
and a full test of the above two approaches using a base class and a derived class, with a custom descriptor that binds a function the same way a classmethod would, but is not, itself, a classmethod:
>>> class notclassmethod:
... def __init__(self, f):
... self.f = f
... def __get__(self, _, typ=None):
... return self.f.__get__(typ, typ)
...
>>> class Base:
... #classmethod
... def base_cm(cls): pass
... #notclassmethod
... def base_ncm(cls): pass
... def base_m(self): pass
...
>>> class Derived(Base):
... #classmethod
... def derived_cm(cls): pass
... #notclassmethod
... def derived_ncm(cls): pass
... def derived_m(self): pass
...
>>> inspect.ismethod(Derived.base_cm) and Derived.base_cm.__self__ is Derived
True
>>> inspect.ismethod(Derived.base_ncm) and Derived.base_ncm.__self__ is Derived
True
>>> inspect.ismethod(Derived.base_m) and Derived.base_m.__self__ is Derived
False
>>> inspect.ismethod(Derived.derived_cm) and Derived.derived_cm.__self__ is Derived
True
>>> inspect.ismethod(Derived.derived_ncm) and Derived.derived_ncm.__self__ is Derived
True
>>> inspect.ismethod(Derived.derived_m) and Derived.derived_m.__self__ is Derived
False
>>> isclassmethod(Derived.base_cm)
True
>>> isclassmethod(Derived.base_ncm)
False
>>> isclassmethod(Derived.base_m)
False
>>> isclassmethod(Derived.derived_cm)
True
>>> isclassmethod(Derived.derived_ncm)
False
>>> isclassmethod(Derived.derived_m)
False
The isclassmethod() function correctly distinguishes between the classmethod and notclassmethod descriptors.
Historical note: this answer included references to Python 2, but with Python 2 having reached EOL were removed as no longer relevant.
You should use inspect.ismethod. It works because classmethod binds the function to the class object. See the following code:
>>> class Foo:
... #classmethod
... def bar():
... pass
... def baz():
... pass
...
>>> Foo.bar
<bound method type.bar of <class '__main__.Foo'>>
>>> Foo.baz
<function Foo.baz at 0x0000000002CCC1E0>
>>> type(Foo.bar)
<class 'method'>
>>> type(Foo.baz)
<class 'function'>
>>> import inspect
>>> inspect.ismethod(Foo.bar)
True
>>> inspect.ismethod(Foo.baz)
False
class Foo(object):
#classmethod
def baaz(cls):
print "baaz"
isinstance(Foo.__dict__["baaz"], classmethod)
None of the answers address the problem of identifying whether a method is decorated with class method from an instance of the class. Following code explores the class dict of an instance to distinguish between classmethod from other methods.
class MyClass(object):
#classmethod
def class_method(cls):
pass
def instance_method(self):
pass
#staticmethod
def static_method():
pass
def blas(): pass
t = MyClass()
isinstance(t.__class__.__dict__[t.class_method.__name__], classmethod) # True
isinstance(t.__class__.__dict__[t.static_method.__name__], classmethod) # False
isinstance(t.__class__.__dict__[t.instance_method.__name__], classmethod) # False
isinstance(t.__class__.__dict__[t.blas.__name__], classmethod) # False
This will work for both Python 2 and 3.
This works for me:
def is_classmethod(method):
"""
Is method a classmethod?
"""
return isinstance(getattr(method, '__self__', None), type)
It basically tests if method.__self__ exists and is a class, as in Martijn's answer, but does not require access to the class itself.
Related
I'm trying to "wrap" an existing classmethod, i.e.,
def Foo:
#classmethod
def bar(cls, x): return x + 2
old_bar = Foo.bar
def wrapped_bar(cls, x):
result = old_bar(cls, x) # Results in an error
return result
Foo.bar = wrapped_bar
It seems that Foo.bar is already bound with cls = Foo, how do I get the unbound version of the function bar?
[I'm not allowed to modify Foo, it exists in another codebase that I'm patching]
Suppose, you have:
>>> class Foo:
... #classmethod
... def bar(cls, x):
... return x*42
...
>>> Foo.bar(2)
84
Then one way is to access the name-space of your class directly. Then you should be able to access the classmethod object and obtain the decorated function available at the __func__ attribute:
>>> vars(Foo)['bar']
<classmethod object at 0x103eec520>
>>> vars(Foo)['bar'].__func__
<function Foo.bar at 0x1043e49d0>
Alternatively, it is accessible on the bound-method object itself:
>>> bound = Foo.bar
>>> bound
<bound method Foo.bar of <class '__main__.Foo'>>
>>> bound.__func__
<function Foo.bar at 0x1043e49d0>
The __func__ attribute holds the wrapped method. Note that your wrapper will also need the #classmethod decorator in order to attach properly (otherwise it'll act as an instance method).
#classmethod
def wrapped_bar(cls, x):
result = Foo.bar.__func__(cls, x)
return result
Foo.bar = wrapped_bar
I know that in Python, given a class ClassA, with
inspect.getmembers(ClassA, predicate=inspect.ismethod)
I can iterate over the different methods present in ClassA. Inherited methods are also gathered, which is convenient in my case. But what I would need is, given a particular method method1 of ClassA, to get the class from which ClassA inherited method1. It might be ClassA itself, or any of its parents/grandparents. I thought I could recursively traverse the __bases__ attribute, looking for the method1 attribute at each step. But maybe this functionality is already implemented somewhere. Is there another way?
Look through the MRO (Method Resolution Order), using inspect.getmro() (which works on both old and new-style classes):
def class_for_method(cls, method):
return next((c for c in inspect.getmro(cls)
if method.__func__ in vars(c).values()), None)
There is currently no stdlib method to do this search for you, no.
Demo:
>>> import inspect
>>> def class_for_method(cls, method):
... return next((c for c in inspect.getmro(cls)
... if method.__func__ in vars(c).values()), None)
...
>>> class Base1(object):
... def foo(self): pass
...
>>> class Base2(object):
... pass
...
>>> class ClassA(Base1, Base2):
... pass
...
>>> class_for_method(ClassA, ClassA.foo)
<class '__main__.Base1'>
If no base class is found, the above expression returns None:
>>> class Bar:
... def spam(): pass
...
>>> class_for_method(ClassA, Bar.spam) is None
True
class A:
def foo(self):
print "foo()"
getattr(A, foo) # True
A.foo() # error
getattr(A(), foo) # True
A().foo() # prints "foo()"
That being said, here is my problem:
I wish to store test case meta information as attributes of the Test Case class objects themselves, not on instances of them.
I have a list of attribute names to extract, but if there is an instance method of the same name, then getattr(class_obj, attr) will return True, but getattr(class_obj, attr)() raises an Error.
Is there a way to tell getattr not to include attributes of the instantiated class and only of the class object itself?
EDIT: I tried accessing class_obj.__dict__ directly (which I understand is bad practice), but it does not include some attributes like __name__
EDIT: Rephrase of the question. Is there a way to differentiate between methods of the class obj and the methods of an instance of the class?
Is this good enough?
import types
class Test(object):
#staticmethod
def foo():
print 'foo'
def bar(self):
print 'bar'
In combination with:
>>>(isinstance(getattr(Test, 'foo'), types.FunctionType),
isinstance(getattr(Test, 'bar'), types.FunctionType))
True, False
You can also use the inspect module:
>>> inspect.isfunction(Test.foo)
True
>>> inspect.isfunction(Test.bar)
False
With a little additional work you can even distinguish class methods from instance methods and static methods:
import inspect
def get_type(cls, attr):
try:
return [a.kind for a in inspect.classify_class_attrs(cls) if a.name == attr][0]
except IndexError:
return None
class Test(object):
#classmethod
def foo(cls):
print 'foo'
def bar(self):
print 'bar'
#staticmethod
def baz():
print 'baz'
You can use it as:
>>> get_type(Test, 'foo')
'class method'
>>> get_type(Test, 'bar')
'method'
>>> get_type(Test, 'baz')
'static method'
>>> get_type(Test, 'nonexistant')
None
Your results from an incorrect definition of foo, not any underlying semantics of class attributes. By default, a function declared inside a class is an instance method, which must take at least one argument, an instance of the class. Conventionally, it is referred to as self:
class A:
def foo(self):
print "foo()"
Normally, you would call such a method like this:
a = A()
a.foo() # passes the object 'a' implicitly as the value of the parameter 'self'
but this is legal as well
a = A()
A.foo(a) # pass the object 'a' explicitly as the value of the parameter 'self'
In order to define a function inside a class that doesn't take any such implicit arguments, you need to decorate it with the #staticmethod decorator:
class A:
#staticmethod
def foo():
print "foo()"
Now, you can call foo the way you tried to previously:
>>> A.foo()
foo()
You want something like this:
from inspect import ismethod
from collections import Callable
def can_i_call(func):
if not isinstance(func, Callable):
# not a callable at all
return False
if not ismethod(func):
# regular function or class or whatever
return True
# func is a method
return func.im_self is not None
Note: this will only test whether or not an attempt to call will error out because you're calling an unbound method without a self. It doesn't guarantee that func() will succeed, i.e. not fail for any other reason.
assume following class definition:
class A:
def f(self):
return 'this is f'
#staticmethod
def g():
return 'this is g'
a = A()
So f is a normal method and g is a static method.
Now, how can I check if the funcion objects a.f and a.g are static or not? Is there a "isstatic" funcion in Python?
I have to know this because I have lists containing many different function (method) objects, and to call them I have to know if they are expecting "self" as a parameter or not.
Lets experiment a bit:
>>> import types
>>> class A:
... def f(self):
... return 'this is f'
... #staticmethod
... def g():
... return 'this is g'
...
>>> a = A()
>>> a.f
<bound method A.f of <__main__.A instance at 0x800f21320>>
>>> a.g
<function g at 0x800eb28c0>
>>> isinstance(a.g, types.FunctionType)
True
>>> isinstance(a.f, types.FunctionType)
False
So it looks like you can use types.FunctionType to distinguish static methods.
Your approach seems a bit flawed to me, but you can check class attributes:
(in Python 2.7):
>>> type(A.f)
<type 'instancemethod'>
>>> type(A.g)
<type 'function'>
or instance attributes in Python 3.x
>>> a = A()
>>> type(a.f)
<type 'method'>
>>> type(a.g)
<type 'function'>
To supplement the answers here, in Python 3 the best way is like so:
import inspect
class Test:
#staticmethod
def test(): pass
isstatic = isinstance(inspect.getattr_static(Test, "test"), staticmethod)
We use getattr_static rather than getattr, since getattr will retrieve the bound method or function, not the staticmethod class object. You can do a similar check for classmethod types and property's (e.g. attributes defined using the #property decorator)
Note that even though it is a staticmethod, don't assume it was defined inside the class. The method source may have originated from another class. To get the true source, you can look at the underlying function's qualified name and module. For example:
class A:
#staticmethod:
def test(): pass
class B: pass
B.test = inspect.getattr_static(A, "test")
print("true source: ", B.test.__qualname__)
Technically, any method can be used as "static" methods, so long as they are called on the class itself, so just keep that in mind. For example, this will work perfectly fine:
class Test:
def test():
print("works!")
Test.test()
That example will not work with instances of Test, since the method will be bound to the instance and called as Test.test(self) instead.
Instance and class methods can be used as static methods as well in some cases, so long as the first arg is handled properly.
class Test:
def test(self):
print("works!")
Test.test(None)
Perhaps another rare case is a staticmethod that is also bound to a class or instance. For example:
class Test:
#classmethod
def test(cls): pass
Test.static_test = staticmethod(Test.test)
Though technically it is a staticmethod, it is really behaving like a classmethod. So in your introspection, you may consider checking the __self__ (recursively on __func__) to see if the method is bound to a class or instance.
I happens to have a module to solve this. And it's Python2/3 compatible solution. And it allows to test with method inherit from parent class.
Plus, this module can also test:
regular attribute
property style method
regular method
staticmethod
classmethod
For example:
class Base(object):
attribute = "attribute"
#property
def property_method(self):
return "property_method"
def regular_method(self):
return "regular_method"
#staticmethod
def static_method():
return "static_method"
#classmethod
def class_method(cls):
return "class_method"
class MyClass(Base):
pass
Here's the solution for staticmethod only. But I recommend to use the module posted here.
import inspect
def is_static_method(klass, attr, value=None):
"""Test if a value of a class is static method.
example::
class MyClass(object):
#staticmethod
def method():
...
:param klass: the class
:param attr: attribute name
:param value: attribute value
"""
if value is None:
value = getattr(klass, attr)
assert getattr(klass, attr) == value
for cls in inspect.getmro(klass):
if inspect.isroutine(value):
if attr in cls.__dict__:
bound_value = cls.__dict__[attr]
if isinstance(bound_value, staticmethod):
return True
return False
Why bother? You can just call g like you call f:
a = A()
a.f()
a.g()
I have a two part question.
>>> class One(object):
... pass
...
>>> class Two(object):
... pass
...
>>> def digest(constr):
... c = apply(constr)
... print c.__class__.__name__
... print constr.__class__.__name__
...
>>> digest(Two)
Two
type
How would one create object 'Two'? Neither constr() or c() work; and it seems that apply turns it into a type.
What happens when you pass a class rather and an instance into a method?
Classes are high level objects, so you can simply pass them like this:
def createMyClass ( myClass ):
obj = myClass()
return obj
class A ( object ):
pass
>>> x = createMyClass( A )
>>> type( x )
<class '__main__.A'>
How would one create object 'Two'?
Neither constr() or c() work; and it
seems that apply turns it into a
type.
The above comment was made in regards to this code:
>>> def digest(constr):
... c = apply(constr)
... print c.__class__.__name__
... print constr.__class__.__name__
apply (deprecated: see #pyfunc's answer) certainly does not turn the class Two into a type: It already is one.
>>> class Two(object): pass
...
>>> type(Two)
<type 'type'>
Classes are first class objects: they're instances of type. This makes sense if you look at the next example.
>>> two = Two()
>>> type(two)
<class '__main__.Two'>
You can see that a class very clearly functions as a type because it can be returned from type. Here's another example.
>>> Three = type('Three', (Two, ), {'foo': 'bar'})
>>> type(Three)
<type 'type'>
>>> three = Three()
>>> type(three)
<class '__main__.Three'>
You can see that type is a class that can be instantiated. Its constructor takes three arguments: the name of the class, a tuple of base classes and a dictionary containing the class attributes. It returns a new type aka class.
As to your final question,
What happens when you pass a class
rather and an instance into a method?
You're going to have to be more specific. Classes are just instances of type and so are first class objects. Asking what happens if I pass a class into a method is like asking what happens if I pass an integer into a method: It depends entirely on what the method is expecting.
Just another one example:
def InstanceFactory(classname):
cls = globals()[classname]
return cls()
class A(object):
def start(self):
print "a.start"
class B(object):
def start(self):
print "b.start"
InstanceFactory("A").start()
InstanceFactory("B").start()
If the class belongs to another module:
def InstanceFactory(modulename, classname):
if '.' in modulename:
raise ValueError, "can't handle dotted modules yet"
mod = __import__(modulename)
cls = getattr(mod, classname]
return cls()
I am confused though. Wasn't apply() deprecated since 2.3
http://www.python.org/dev/peps/pep-0290/
We don't need this any more.
apply(f, args, kwds) --> f(*args, **kwds)
Others have been moved / considered deprecated in modern usage:
buffer()
coerce()
and intern()
Simply use : Classname() to create an object.