如何使用给定的装饰器获取python类的所有方法

时间:2011-05-06 11:23:39

标签: python class methods decorator inspect

如何获取用@ decorator2?

修饰的给定A类的所有方法
class A():
    def method_a(self):
      pass

    @decorator1
    def method_b(self, b):
      pass

    @decorator2
    def method_c(self, t=5):
      pass

7 个答案:

答案 0 :(得分:100)

答案 1 :(得分:14)

要扩展@ninjagecko方法2中的优秀答案:源代码解析,只要inspect模块可以访问源代码,就可以使用Python 2.6中引入的ast模块进行自检。

def findDecorators(target):
    import ast, inspect
    res = {}
    def visit_FunctionDef(node):
        res[node.name] = [ast.dump(e) for e in node.decorator_list]

    V = ast.NodeVisitor()
    V.visit_FunctionDef = visit_FunctionDef
    V.visit(compile(inspect.getsource(target), '?', 'exec', ast.PyCF_ONLY_AST))
    return res

我添加了一个稍微复杂的装饰方法:

@x.y.decorator2
def method_d(self, t=5): pass

结果:

> findDecorators(A)
{'method_a': [],
 'method_b': ["Name(id='decorator1', ctx=Load())"],
 'method_c': ["Name(id='decorator2', ctx=Load())"],
 'method_d': ["Attribute(value=Attribute(value=Name(id='x', ctx=Load()), attr='y', ctx=Load()), attr='decorator2', ctx=Load())"]}

答案 2 :(得分:0)

也许,如果装饰者不是太复杂(但我不知道是否有一种不太常见的方式)。

def decorator1(f):
    def new_f():
        print "Entering decorator1", f.__name__
        f()
    new_f.__name__ = f.__name__
    return new_f

def decorator2(f):
    def new_f():
        print "Entering decorator2", f.__name__
        f()
    new_f.__name__ = f.__name__
    return new_f


class A():
    def method_a(self):
      pass

    @decorator1
    def method_b(self, b):
      pass

    @decorator2
    def method_c(self, t=5):
      pass

print A.method_a.im_func.func_code.co_firstlineno
print A.method_b.im_func.func_code.co_firstlineno
print A.method_c.im_func.func_code.co_firstlineno

答案 3 :(得分:0)

解决此问题的一种简单方法是将代码放在装饰器中,该装饰器将传入的每个函数/方法添加到数据集(例如列表)中。

例如

def deco(foo):
    functions.append(foo)
    return foo

现在所有具有 deco 装饰器的功能都将添加到功能

答案 4 :(得分:0)

我不想添加太多,只是ninjagecko的方法2的简单变体。它的确很神奇。

相同的代码,但是使用列表推导而不是生成器,这是我需要的。

def methodsWithDecorator(cls, decoratorName):

    sourcelines = inspect.getsourcelines(cls)[0]
    return [ sourcelines[i+1].split('def')[1].split('(')[0].strip()
                    for i, line in enumerate(sourcelines)
                    if line.split('(')[0].strip() == '@'+decoratorName]

答案 5 :(得分:0)

如果您确实可以控制装饰器,则可以使用装饰器类而不是函数:

class awesome(object):
    def __init__(self, method):
        self._method = method
    def __call__(self, obj, *args, **kwargs):
        return self._method(obj, *args, **kwargs)
    @classmethod
    def methods(cls, subject):
        def g():
            for name in dir(subject):
                method = getattr(subject, name)
                if isinstance(method, awesome):
                    yield name, method
        return {name: method for name,method in g()}

class Robot(object):
   @awesome
   def think(self):
      return 0

   @awesome
   def walk(self):
      return 0

   def irritate(self, other):
      return 0

如果我打电话给awesome.methods(Robot),它将返回

{'think': <mymodule.awesome object at 0x000000000782EAC8>, 'walk': <mymodulel.awesome object at 0x000000000782EB00>}

答案 6 :(得分:0)

对于我们这些只想要绝对最简单的情况的人 - 即,我们可以完全控制我们正在使用的类和我们试图跟踪的装饰器的单文件解决方案,我已经得到了答案。 ninjagecko 链接到一个解决方案,用于当您可以控制要跟踪的装饰器时,但我个人发现它很复杂且很难理解,可能是因为直到现在我从未使用过装饰器。因此,我创建了以下示例,目的是尽可能简单明了。它是一个装饰器,一个具有多个装饰方法的类,以及用于检索并运行应用了特定装饰器的所有方法的代码。

# our decorator
def cool(func, *args, **kwargs):
    def decorated_func(*args, **kwargs):
        print("cool pre-function decorator tasks here.")
        return_value = func(*args, **kwargs)
        print("cool post-function decorator tasks here.")
        return return_value
    # add is_cool property to function so that we can check for its existence later
    decorated_func.is_cool = True
    return decorated_func

# our class, in which we will use the decorator
class MyClass:
    def __init__(self, name):
        self.name = name

    # this method isn't decorated with the cool decorator, so it won't show up 
    # when we retrieve all the cool methods
    def do_something_boring(self, task):
        print(f"{self.name} does {task}")
    
    @cool
    # thanks to *args and **kwargs, the decorator properly passes method parameters
    def say_catchphrase(self, *args, catchphrase="I'm so cool you could cook an egg on me.", **kwargs):
        print(f"{self.name} says \"{catchphrase}\"")

    @cool
    # the decorator also properly handles methods with return values
    def explode(self, *args, **kwargs):
        print(f"{self.name} explodes.")
        return 4

    def get_all_cool_methods(self):
        """Get all methods decorated with the "cool" decorator.
        """
        cool_methods =  {name: getattr(self, name)
                            # get all attributes, including methods, properties, and builtins
                            for name in dir(self)
                                # but we only want methods
                                if callable(getattr(self, name))
                                # and we don't need builtins
                                and not name.startswith("__")
                                # and we only want the cool methods
                                and hasattr(getattr(self, name), "is_cool")
        }
        return cool_methods

if __name__ == "__main__":
    jeff = MyClass(name="Jeff")
    cool_methods = jeff.get_all_cool_methods()    
    for method_name, cool_method in cool_methods.items():
        print(f"{method_name}: {cool_method} ...")
        # you can call the decorated methods you retrieved, just like normal,
        # but you don't need to reference the actual instance to do so
        return_value = cool_method()
        print(f"return value = {return_value}\n")

运行上面的例子给我们以下输出:

explode: <bound method cool.<locals>.decorated_func of <__main__.MyClass object at 0x00000220B3ACD430>> ...
cool pre-function decorator tasks here.
Jeff explodes.
cool post-function decorator tasks here.
return value = 4

say_catchphrase: <bound method cool.<locals>.decorated_func of <__main__.MyClass object at 0x00000220B3ACD430>> ...
cool pre-function decorator tasks here.
Jeff says "I'm so cool you could cook an egg on me."
cool post-function decorator tasks here.
return value = None

请注意,本示例中的装饰方法具有不同类型的返回值和不同的签名,因此能够检索和运行它们的实际价值有点令人怀疑。但是,如果有许多类似的方法,所有方法都具有相同的签名和/或返回值类型(例如,如果您正在编写一个连接器来从一个数据库中检索未规范化的数据,对其进行规范化,然后将其插入到第二个数据库中,标准化数据库,并且您有一堆类似的方法,例如 15 个 read_and_normalize_table_X 方法),能够即时检索(并运行)它们可能会更有用。