如何使用给定的装饰器获取Python类的所有方法?
如何获取给定类 A 中用 @decorator2 装饰的所有方法?
class A():
def method_a(self):
pass
@decorator1
def method_b(self, b):
pass
@decorator2
def method_c(self, t=5):
pass
How to get all methods of a given class A that are decorated with the @decorator2
?
class A():
def method_a(self):
pass
@decorator1
def method_b(self, b):
pass
@decorator2
def method_c(self, t=5):
pass
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
方法1:基本注册装饰器
我已经在这里回答了这个问题:调用Python 中按数组索引调用函数 =)
方法 2:源代码解析
如果您无法控制类定义,这是对您的内容的一种解释我想假设,这是不可能(没有代码读取反射),因为例如装饰器可能是一个无操作装饰器(就像在我的链接示例中),它仅返回未修改的函数。 (尽管如此,如果您允许自己包装/重新定义装饰器,请参阅方法 3:将装饰器转换为“自我意识”,然后您将找到一个优雅的解决方案)
这是一个可怕的黑客,但是您可以使用
inspect
模块读取源代码本身并解析它。这在交互式解释器中不起作用,因为检查模块将拒绝以交互模式提供源代码。然而,下面是一个概念证明。它有效!:
请注意,必须注意解析和 python 语法,例如
@deco
和@deco(...
是有效结果,但如果我们仅仅请求
。我们注意到,根据 http://docs.python.org/reference/compound_stmts.html 装饰器如下:'deco'
,则不应返回 @deco2我们松了一口气,因为不必处理案例但请注意,如果您有非常复杂的装饰器,例如
@getDecorator(...)
,例如因此,这种解析代码的最佳策略无法检测到这样的情况,但如果您使用此方法,您真正需要的是定义中方法之上编写的内容。在这种情况下是
getDecorator
根据规范,将
@foo1.bar2.baz3(...)
作为装饰器也是有效的。您可以扩展此方法来使用它。您还可以扩展此方法以返回
而不是函数名称,这需要花费大量精力。然而,这种方法是黑客且可怕的。方法 3:将装饰器转换为“自我意识”
如果您无法控制装饰器定义(这是您想要的另一种解释),那么所有这些问题都会消失,因为您可以控制装饰器的应用方式。因此,您可以通过包装来修改装饰器,以创建您自己的装饰器,并使用that来装饰您的函数。让我再说一遍:您可以创建一个装饰器来装饰您无法控制的装饰器,“启发”它,在我们的例子中,这使它执行之前所做的事情,但还附加一个它返回的可调用对象的
.decorator
元数据属性,允许您跟踪“这个函数是否被装饰?让我们检查 function.decorator!”。然后然后您可以迭代类的方法,然后检查装饰器是否具有适当的.decorator
属性! =) 如此处所示:@decorator
的演示:它有效!:
但是,“注册装饰器”必须是最外层装饰器,否则
.decorator< /code> 属性注释将会丢失。例如,在一列中,
您只能看到
decoOutermost
公开的元数据,除非我们保留对“更内部”包装器的引用。旁注:上述方法还可以构建一个
.decorator
来跟踪应用的装饰器和输入函数以及装饰器工厂参数的整个堆栈。 =) 例如,如果您考虑注释掉的行R.original = func
,那么使用这样的方法来跟踪所有包装层是可行的。如果我编写一个装饰器库,这就是我个人会做的,因为它允许深入自省。@foo
和@bar(.. .)
。虽然它们都是规范中定义的“装饰器表达式”,但请注意foo
是一个装饰器,而bar(...)
返回一个动态创建的装饰器,它然后应用。因此,您需要一个单独的函数makeRegisteringDecoratorFactory
,它有点像makeRegisteringDecorator
,但更多元:演示
@decorator(...)
:这个生成器工厂包装器也可以工作:
额外让我们尝试使用方法 #3 进行以下操作:
结果:
如您所见,与方法 2 不同,@deco 被正确识别,即使它从未明确写入班级。与 method2 不同,如果该方法是在运行时添加(手动、通过元类等)或继承的,这也将起作用。
请注意,您还可以装饰一个类,因此如果您“启发”一个用于装饰方法和类的装饰器,然后在要分析的类的主体中编写一个类 ,那么
methodsWithDecorator
将返回修饰类以及修饰方法。人们可以认为这是一项功能,但您可以通过检查装饰器的参数(即.original
)轻松编写逻辑来忽略这些功能,以实现所需的语义。Method 1: Basic registering decorator
I already answered this question here: Calling functions by array index in Python =)
Method 2: Sourcecode parsing
If you do not have control over the class definition, which is one interpretation of what you'd like to suppose, this is impossible (without code-reading-reflection), since for example the decorator could be a no-op decorator (like in my linked example) that merely returns the function unmodified. (Nevertheless if you allow yourself to wrap/redefine the decorators, see Method 3: Converting decorators to be "self-aware", then you will find an elegant solution)
It is a terrible terrible hack, but you could use the
inspect
module to read the sourcecode itself, and parse it. This will not work in an interactive interpreter, because the inspect module will refuse to give sourcecode in interactive mode. However, below is a proof of concept.It works!:
Note that one has to pay attention to parsing and the python syntax, e.g.
@deco
and@deco(...
are valid results, but@deco2
should not be returned if we merely ask for'deco'
. We notice that according to the official python syntax at http://docs.python.org/reference/compound_stmts.html decorators are as follows:We breathe a sigh of relief at not having to deal with cases like
@(deco)
. But note that this still doesn't really help you if you have really really complicated decorators, such as@getDecorator(...)
, e.g.Thus, this best-that-you-can-do strategy of parsing code cannot detect cases like this. Though if you are using this method, what you're really after is what is written on top of the method in the definition, which in this case is
getDecorator
.According to the spec, it is also valid to have
@foo1.bar2.baz3(...)
as a decorator. You can extend this method to work with that. You might also be able to extend this method to return a<function object ...>
rather than the function's name, with lots of effort. This method however is hackish and terrible.Method 3: Converting decorators to be "self-aware"
If you do not have control over the decorator definition (which is another interpretation of what you'd like), then all these issues go away because you have control over how the decorator is applied. Thus, you can modify the decorator by wrapping it, to create your own decorator, and use that to decorate your functions. Let me say that yet again: you can make a decorator that decorates the decorator you have no control over, "enlightening" it, which in our case makes it do what it was doing before but also append a
.decorator
metadata property to the callable it returns, allowing you to keep track of "was this function decorated or not? let's check function.decorator!". And then you can iterate over the methods of the class, and just check to see if the decorator has the appropriate.decorator
property! =) As demonstrated here:Demonstration for
@decorator
:It works!:
However, a "registered decorator" must be the outermost decorator, otherwise the
.decorator
attribute annotation will be lost. For example in a train ofyou can only see metadata that
decoOutermost
exposes, unless we keep references to "more-inner" wrappers.sidenote: the above method can also build up a
.decorator
that keeps track of the entire stack of applied decorators and input functions and decorator-factory arguments. =) For example if you consider the commented-out lineR.original = func
, it is feasible to use a method like this to keep track of all wrapper layers. This is personally what I'd do if I wrote a decorator library, because it allows for deep introspection.There is also a difference between
@foo
and@bar(...)
. While they are both "decorator expressons" as defined in the spec, note thatfoo
is a decorator, whilebar(...)
returns a dynamically-created decorator, which is then applied. Thus you'd need a separate functionmakeRegisteringDecoratorFactory
, that is somewhat likemakeRegisteringDecorator
but even MORE META:Demonstration for
@decorator(...)
:This generator-factory wrapper also works:
bonus Let's even try the following with Method #3:
Result:
As you can see, unlike method2, @deco is correctly recognized even though it was never explicitly written in the class. Unlike method2, this will also work if the method is added at runtime (manually, via a metaclass, etc.) or inherited.
Be aware that you can also decorate a class, so if you "enlighten" a decorator that is used to both decorate methods and classes, and then write a class within the body of the class you want to analyze, then
methodsWithDecorator
will return decorated classes as well as decorated methods. One could consider this a feature, but you can easily write logic to ignore those by examining the argument to the decorator, i.e..original
, to achieve the desired semantics.为了扩展@ninjagecko在方法2:源代码解析中的优秀答案,只要inspect模块可以访问源代码,您就可以使用Python 2.6中引入的ast模块来执行自检查。
我添加了一个稍微复杂的装饰方法:
结果:
To expand upon @ninjagecko's excellent answer in Method 2: Source code parsing, you can use the
ast
module introduced in Python 2.6 to perform self-inspection as long as the inspect module has access to the source code.I added a slightly more complicated decorated method:
Results:
如果您确实可以控制装饰器,则可以使用装饰器类而不是函数:
如果我调用
awesome.methods(Robot)
它会返回If you do have control over the decorators, you can use decorator classes rather than functions:
and if I call
awesome.methods(Robot)
it returns对于我们这些只想要绝对最简单的情况的人 - 即单文件解决方案,我们可以完全控制我们正在使用的类和我们正在尝试跟踪的装饰器,我有一个答案。 ninjagecko 链接到了一个解决方案,用于当您可以控制要跟踪的装饰器时,但我个人发现它很复杂并且很难理解,可能是因为到目前为止我从未使用过装饰器。因此,我创建了以下示例,目标是尽可能简单明了。它是一个装饰器,一个具有多个装饰方法的类,以及用于检索+运行所有应用了特定装饰器的方法的代码。
运行上面的示例会给出以下输出:
请注意,此示例中的修饰方法具有不同类型的返回值和不同的签名,因此能够检索和运行它们的实际价值有点可疑。但是,在有许多类似方法的情况下,所有方法都具有相同的签名和/或返回值类型(例如,如果您正在编写一个连接器来从一个数据库检索非标准化数据,对其进行标准化,然后将其插入到第二个数据库中)标准化数据库,并且您有一堆类似的方法,例如 15 个 read_and_normalize_table_X 方法),能够即时检索(并运行)它们可能会更有用。
For those of us who just want the absolute simplest possible case - namely, a single-file solution where we have total control over both the class we're working with and the decorator we're trying to track, I've got an answer. ninjagecko linked to a solution for when you have control over the decorator you want to track, but I personally found it to be complicated and really hard to understand, possibly because I've never worked with decorators until now. So, I've created the following example, with the goal of being as straightforward and simple as possible. It's a decorator, a class with several decorated methods, and code to retrieve+run all methods that have a specific decorator applied to them.
Running the above example gives us the following output:
Note that the decorated methods in this example have different types of return values and different signatures, so the practical value of being able to retrieve and run them all is a bit dubious. However, in cases where there are many similar methods, all with the same signature and/or type of return value (like if you're writing a connector to retrieve unnormalized data from one database, normalize it, and insert it into a second, normalized database, and you have a bunch similar methods, e.g. 15 read_and_normalize_table_X methods), being able to retrieve (and run) them all on the fly could be more useful.
也许,如果装饰器不太复杂(但我不知道是否有一种不那么麻烦的方法)。
Maybe, if the decorators are not too complex (but I don't know if there is a less hacky way).
我不想添加太多内容,只是 ninjagecko 方法 2 的一个简单变体。它的效果非常好。
相同的代码,但使用列表理解而不是生成器,这正是我所需要的。
I don't want to add much, just a simple variation of ninjagecko's Method 2. It works wonders.
Same code, but using list comprehension instead of a generator, which is what I needed.
解决此问题的一个简单方法是将代码放入装饰器中,将传入的每个函数/方法添加到数据集(例如列表)中。
例如
,现在每个带有 deco 装饰器的函数都将被添加到 functions 中。
A simple way to solve this problem is to put code in the decorator that adds each function/method, that is passed in, to a data set (for example a list).
e.g.
now every function with the deco decorator will be added to functions.