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

2024-02-01

如何获取给定类 A 的所有用@decorator2?

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

    @decorator1
    def method_b(self, b):
      pass

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

方法一:基本注册装饰器

我已经在这里回答了这个问题:Python中通过数组索引调用函数 https://stackoverflow.com/questions/5707589/calling-functions-by-array-index-in-python/5707605#5707605 =)


方法二:源码解析

如果您无法控制class定义,这是你想要假设的一种解释,这是不可能的(没有代码读取反射),因为例如装饰器可能是一个无操作装饰器(就像在我的链接示例中),它仅返回未修改的函数。 (尽管如此,如果您允许自己包装/重新定义装饰器,请参阅方法三:将装饰器转变为“自我意识”,那么你会找到一个优雅的解决方案)

这是一个可怕的黑客,但你可以使用inspect模块读取源代码本身并解析它。这在交互式解释器中不起作用,因为检查模块将拒绝以交互模式提供源代码。然而,下面是一个概念证明。

#!/usr/bin/python3

import inspect

def deco(func):
    return func

def deco2():
    def wrapper(func):
        pass
    return wrapper

class Test(object):
    @deco
    def method(self):
        pass

    @deco2()
    def method2(self):
        pass

def methodsWithDecorator(cls, decoratorName):
    sourcelines = inspect.getsourcelines(cls)[0]
    for i,line in enumerate(sourcelines):
        line = line.strip()
        if line.split('(')[0].strip() == '@'+decoratorName: # leaving a bit out
            nextLine = sourcelines[i+1]
            name = nextLine.split('def')[1].split('(')[0].strip()
            yield(name)

有用!:

>>> print(list(  methodsWithDecorator(Test, 'deco')  ))
['method']

请注意,必须注意解析和 python 语法,例如@deco and @deco(...是有效的结果,但是@deco2如果我们只是要求,则不应退回'deco'。我们注意到,根据官方 python 语法http://docs.python.org/reference/compound_stmts.html http://docs.python.org/reference/compound_stmts.html装饰器如下:

decorator      ::=  "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE

我们松了一口气,因为不必处理类似的情况@(deco)。但请注意,如果您有非常非常复杂的装饰器,这仍然没有真正帮助您,例如@getDecorator(...), e.g.

def getDecorator():
    return deco

因此,这种解析代码的最佳策略无法检测到这样的情况。不过,如果您使用此方法,那么您真正想要的是定义中该方法之上所写的内容,在本例中是getDecorator.

根据规范,也有效@foo1.bar2.baz3(...)作为装饰者。您可以扩展此方法来使用它。您也许还可以扩展此方法以返回<function object ...>而不是函数的名称,需要付出很多努力。然而,这种方法是黑客且可怕的。


方法三:将装饰器转变为“自我意识”

如果您无法控制装饰者定义(这是对您想要的内容的另一种解释),那么所有这些问题都会消失,因为您可以控制装饰器的应用方式。因此,您可以通过以下方式修改装饰器wrapping它,创造你的own装饰器,并使用that来装饰你的功能。让我再说一遍:你可以创建一个装饰器来装饰你无法控制的装饰器,“启发”它,在我们的例子中,这使它执行之前所做的事情,但是also附加一个.decorator它返回的可调用对象的元数据属性,允许您跟踪“这个函数是否被装饰?让我们检查 function.decorator!”。和then您可以迭代类的方法,然后检查装饰器是否具有适当的方法.decorator财产! =) 如此处所示:

def makeRegisteringDecorator(foreignDecorator):
    """
        Returns a copy of foreignDecorator, which is identical in every
        way(*), except also appends a .decorator property to the callable it
        spits out.
    """
    def newDecorator(func):
        # Call to newDecorator(method)
        # Exactly like old decorator, but output keeps track of what decorated it
        R = foreignDecorator(func) # apply foreignDecorator, like call to foreignDecorator(method) would have done
        R.decorator = newDecorator # keep track of decorator
        #R.original = func         # might as well keep track of everything!
        return R

    newDecorator.__name__ = foreignDecorator.__name__
    newDecorator.__doc__ = foreignDecorator.__doc__
    # (*)We can be somewhat "hygienic", but newDecorator still isn't signature-preserving, i.e. you will not be able to get a runtime list of parameters. For that, you need hackish libraries...but in this case, the only argument is func, so it's not a big issue

    return newDecorator

示范为@decorator:

deco = makeRegisteringDecorator(deco)

class Test2(object):
    @deco
    def method(self):
        pass

    @deco2()
    def method2(self):
        pass

def methodsWithDecorator(cls, decorator):
    """ 
        Returns all methods in CLS with DECORATOR as the
        outermost decorator.

        DECORATOR must be a "registering decorator"; one
        can make any decorator "registering" via the
        makeRegisteringDecorator function.
    """
    for maybeDecorated in cls.__dict__.values():
        if hasattr(maybeDecorated, 'decorator'):
            if maybeDecorated.decorator == decorator:
                print(maybeDecorated)
                yield maybeDecorated

有用!:

>>> print(list(   methodsWithDecorator(Test2, deco)   ))
[<function method at 0x7d62f8>]

然而,“注册装饰师”必须是最外面的装饰器,否则.decorator属性注释将会丢失。例如在一列火车上

@decoOutermost
@deco
@decoInnermost
def func(): ...

您只能看到以下元数据decoOutermost暴露,除非我们保留对“更内部”包装器的引用。

旁注:上面的方法还可以构建一个.decorator跟踪应用装饰器和输入函数以及装饰器工厂参数的整个堆栈。 =) 例如,如果您考虑注释掉的行R.original = func,使用这样的方法来跟踪所有包装层是可行的。如果我编写一个装饰器库,这就是我个人会做的,因为它允许深入内省。

之间也有区别@foo and @bar(...)。虽然它们都是规范中定义的“装饰器表达式”,但请注意foo是一个装饰器,而bar(...)返回一个动态创建的装饰器,然后应用该装饰器。因此你需要一个单独的函数makeRegisteringDecoratorFactory,这有点像makeRegisteringDecorator但更多元:

def makeRegisteringDecoratorFactory(foreignDecoratorFactory):
    def newDecoratorFactory(*args, **kw):
        oldGeneratedDecorator = foreignDecoratorFactory(*args, **kw)
        def newGeneratedDecorator(func):
            modifiedFunc = oldGeneratedDecorator(func)
            modifiedFunc.decorator = newDecoratorFactory # keep track of decorator
            return modifiedFunc
        return newGeneratedDecorator
    newDecoratorFactory.__name__ = foreignDecoratorFactory.__name__
    newDecoratorFactory.__doc__ = foreignDecoratorFactory.__doc__
    return newDecoratorFactory

示范为@decorator(...):

def deco2():
    def simpleDeco(func):
        return func
    return simpleDeco

deco2 = makeRegisteringDecoratorFactory(deco2)

print(deco2.__name__)
# RESULT: 'deco2'

@deco2()
def f():
    pass

这个生成器工厂包装器也可以工作:

>>> print(f.decorator)
<function deco2 at 0x6a6408>

bonus我们甚至可以使用方法 #3 尝试以下操作:

def getDecorator(): # let's do some dispatching!
    return deco

class Test3(object):
    @getDecorator()
    def method(self):
        pass

    @deco2()
    def method2(self):
        pass

Result:

>>> print(list(   methodsWithDecorator(Test3, deco)   ))
[<function method at 0x7d62f8>]

正如您所看到的,与 method2 不同,@deco 被正确识别,即使它从未在类中显式编写。与 method2 不同,如果该方法是在运行时添加(手动、通过元类等)或继承的,这也将起作用。

要注意,你也可以装饰一个类,所以如果你“启蒙”一个既用来装饰方法又用来装饰类的装饰器,然后编写一个类在您要分析的类的主体内, then methodsWithDecorator将返回修饰的类和修饰的方法。人们可以认为这是一项功能,但您可以通过检查装饰器的参数轻松编写逻辑来忽略这些功能,即.original,实现所需的语义。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用给定的装饰器获取Python类的所有方法? 的相关文章

随机推荐

  • 当线程“等待”某些东西时会发生什么

    When an async method awaits a Task当前正在运行的线程会发生什么 我推测 在 UI 线程上 消息循环将恢复 而在线程池线程上 线程将被释放回线程池 但是如果手动启动线程会发生什么呢 还有其他类型的线程吗 我花
  • 强制删除 boost::signals2 中的槽

    我发现 boost signals2 使用某种连接槽的惰性删除 这使得很难使用连接来管理对象的生命周期 我正在寻找一种方法来强制在断开连接时直接删除插槽 任何有关如何通过以不同方式设计我的代码来解决问题的想法也将受到赞赏 这是我的场景 我有
  • 在 FluentAssertions 中自定义失败处理

    我尝试使用 FluentAssertions 不仅作为测试断言框架 而且还检查运行时契约 例如高级 Debug Assert 并且我已阅读this https stackoverflow com questions 49724379 sho
  • 空手道 - 如何在 java 文件中使用 karate-config.js 变量?

    我有一个 DB utils java 文件 我需要根据运行代码的环境加载数据库用户名密码 并且需要从 karate config js 导入这些环境值 如何实现这一目标 只需使用嵌入表达式即可 所以如果你有dbusername and db
  • Python 多处理:如何在错误后干净地退出?

    我正在编写一些使用的代码multiprocessing模块 然而 由于我是新手 经常发生的情况是弹出一些错误 导致主应用程序停止运行 但是 该应用程序的子级仍然保持运行 并且我得到一长串正在运行的列表pythonw我的任务管理器列表中的进程
  • 在 varchar 列中查找非数字值

    要求 通用查询 函数 用于检查表中 varchar 列中提供的值是否实际上是数字且精度不超过允许的精度 可用值 表名称 列名称 允许的精度 允许的小数位数 一般建议是创建一个函数并使用 to number 来验证该值 但它不会验证允许的长度
  • 如何在 Android 键盘上添加“下一步”

    我在某些应用程序中看到键盘中出现一个名为 next 的按钮 它将焦点放在下一个编辑文本上 我想将其添加到我的应用程序中 你知道我该怎么做吗 或者它仅在应用程序的键盘上 多谢 抱歉 我没有这方面的更多信息 在edittext的布局中添加and
  • C/C++ 中结构体的字段对齐

    结构体的成员是用 C C 封装的吗 我所说的打包是指它们是紧凑的 并且字段之间没有内存空间 这不是对齐的意思 并且不保证任何特定的对齐或打包 元素将按顺序排列 但编译器可以在其选择的位置插入填充 这其实creates 有用的 对齐 例如 对
  • Angular 服务与导出

    我有一组简单的工具方法 没有任何状态可以在应用程序中共享 不需要是单例 也不需要任何注入的服务 使用注射服务有什么优势吗 Injectable export class DateService public convertStringToD
  • SDL 错误架构 x86_64“_SDL_main”的未定义符号

    我在 mac os x 上将 C 与 SDL Cocoa 和 Foundation 框架结合使用 我收到以下错误 Undefined symbols for architecture x86 64 SDL main referenced f
  • 如何在 Jekyll / Github 页面站点的 config.yml 中的新选项卡中打开 URL?

    我是自学成才 对 Jekyll 和 Github Pages 完全陌生 并且想知道如何在 config yml 页面中使用 markdown 在新选项卡中打开 URL This https aweekj github io Kiko plu
  • 添加不同的形状到 d3 力布局

    我试图将不同的形状添加到我的 d3 力布局中 但没有成功 最终目标是根据节点对象本身的属性确定形状 我使用 Selection enter 然后 append 形状像这样 由于力导向布局仅采用一组节点 而 append 采用字符串而不是函数
  • PostgreSQL:创建索引以快速区分NULL和非NULL值

    考虑具有以下内容的 SQL 查询WHERE谓词 WHERE name IS NOT NULL Where name是 PostgreSQL 中的文本字段 没有其他查询检查该值的任何文本属性 只是检查它是否是NULL或不 所以 完整的 btr
  • 如何在 Visual Studio 2012 中创建/打开 DAC 应用程序?

    我很难弄清楚如何使用 Visual Studio 2012 实际创建甚至打开 DAC 应用程序 我使用的是 SQL Server 2012 我安装了 SSDT 从 Visual Studio 创建 根据在线文档 我应该能够看到名为 数据层应
  • Android Studio 3.1:编辑器中错误的未解析引用

    我正在运行 Ubuntu 17 10 并且刚刚更新安卓工作室从版本 3 0 1 到 3 1 以下是 帮助 gt 关于 中的版本信息 Android Studio 3 1 Build AI 173 4670197 built on March
  • Spark DataFrame 的 SparkRcollect() 和 head() 错误:参数意味着行数不同

    我从 HDFS 系统读取了 parquet 文件 path lt hdfs part 2015 AppDF lt parquetFile sqlContext path printSchema AppDF root app binary n
  • 嵌套类会产生什么后果? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我有这个示例代码 带有一个类B嵌套在类内部A class A class B count 0 def init self b1 None b2
  • 使用 CORS 和 IE9 的 ASP Web API POST 请求(XDomainRequest 对象)

    我一直在疯狂地尝试让 jquery ajax 与 ie9 一起工作 所以我有一个实现 CORS 的 ASP Web API 2 Rest API 来自所有浏览器的 CORS 请求都有效 IE9 无法工作 因为它使用 XDomainReque
  • 错误:ConnectionResult{statusCode=INTERNAL_ERROR,分辨率=null}

    我正在开发应用程序 将 Google Drive 集成到我的应用程序中 下面是我的代码 我只是从示例代码中复制而来 但在连接 Google Drive 时出现异常 例外 ConnectionResult statusCode INTERNA
  • 如何使用给定的装饰器获取Python类的所有方法?

    如何获取给定类 A 的所有用 decorator2 class A def method a self pass decorator1 def method b self b pass decorator2 def method c sel