Ожидаемые случаи:
- Нормальная функция
- ClassMethod
- InstanceMethod
- StaticMethod
- Метод в подклассе
Примеры:
def ff(): pass
class A: @staticmethod def sm(): pass @classmethod def cm(cls): pass def im(self): pass
class B: class C: def im(self): pass @classmethod def cm(cls): pass @staticmethod def sm(): pass @classmethod def cm(cls): def f_(): pass return f_ def im(): def f_(): def f__(): pass return f__ return f_
Факты:
>>> cm.__qualname__, sm.__qualname__, im.__qualname__, ff.__qualname__ ('A.cm', 'A.sm', 'A.im', 'ff')
>>> str(A.cm), str(A.sm), str(A.im), str(ff) ("<bound method cls.cm of <class '__main__.cls'>>", '<function cls.sm at 0x113977378>', '<function cls.im at 0x114367950>', '<function ff at 0x113977158>')
>>> from inspect import * >>> ismethod(A.cm), ismethod(A.sm), ismethod(A.im), ismethod(ff) (True, False, False, False) >>> isfunction(cm), isfunction(sm), isfunction(im), isfunction(ff) (False, True, True, True)
>>> getmembers(A, ismethod) [('cm', <bound method A.cm of <class '__main__.A'>>), ('xm', <bound method A.xm of <class '__main__.A'>>), ('xxm', <bound method A.xxm of <class '__main__.A'>>)] >>> getmembers(A, isfunction) [('im', <function __main__.A.im(self)>), ('sm', <function __main__.A.sm()>)]
>>> from types import * >>> isinstance(A.cm, MethodType), isinstance(A.sm, MethodType),isinstance(A.im, MethodType),isinstance(ff, MethodType) (True, False, False, False) >>> isinstance(A.cm, FunctionType), isinstance(A.sm, FunctionType),isinstance(A.im, FunctionType),isinstance(ff, FunctionType) (False, True, True, True)
Простые тесты для запуска:
# Class methods assert inspect_type(A.sm) == ('staticmethod', A) assert inspect_type(A.cm) == ('classmethod', A) assert inspect_type(A.im) == ('instancemethod', A) # Function assert inspect_type(ff) == ('function', None) # Sub classes # assert B.cm().__qualname__ == 'B.cm.<locals>.f_' assert inspect_type(B.im()) == ('function', None) assert inspect_type(B.C.im) == ('instancemethod', B.C) assert inspect_type(B.C.cm) == ('classmethod', B.C) assert inspect_type(B.C.sm) == ('staticmethod', B.C)
print('OK.')
Решение 1:
import inspect import sys
def inspect_type(func): ftype = None if '.' not in func.__qualname__: ftype = 'function' print('Normal function') else # __qualname__: 'className.functioNname' cls_name = func.__qualname__.rsplit('.', 1)[0] # Get the class by name cls = getattr(sys.modules[func.__module__], cls_name) # cls.__dict__[func.__name__] should return like <class 'staticmethod'> ftype = cls.__dict__[func.__name__].__class__.__name__ if ftype == 'staticmethod': print('staticmethod') elif ftype == 'classmethod': print('classmethod') elif ftype == 'function': print('instance-method') else: raise TypeError('Unknown Type %s, Please check input is method or function' % func) return ftype
inspect_type(A.sm) inspect_type(A.cm) inspect_type(A.im) inspect_type(ff)
Недостаток:
Это решение НЕ РАБОТАЕТ для методов в SUBCLASS.
Решение 2
import sys
def get_func_class(func): classes = [] parent = sys.modules[func.__module__] names = func.__qualname__.split('.')[:-1] for n in names: parent = parent.__dict__[n] if isinstance(parent, type): classes.append(parent) return classes[-1] if classes else None
def inspect_type(func): ftype = None if func.__name__ == func.__qualname__: return 'function', None elif '.<locals>' in func.__qualname__: return 'function', None cls = get_func_class(func) ftype = cls.__dict__.get(func.__name__)
if type(ftype) == staticmethod: return 'staticmethod', cls elif type(ftype) == classmethod: return 'classmethod', cls elif ftype.__class__.__name__ == 'function': return 'instancemethod', cls else: raise TypeError('Unknown Type %s, Please check input is method or function' % func)