Python子类方法从超类方法继承装饰器

2024-05-11

我有一个具有retrieve() 方法的超类,并且它的子类每个都实现自己的retrieve() 方法。我希望每个retrieve()方法都被装饰为在收到相同参数时缓存返回值,而不必在每个子类中装饰该方法。

装饰器似乎不能被继承。我可能可以调用超类的方法来设置缓存,但目前我的超类引发了一个 NotImplemented 异常,这是我喜欢的。

import json
import operator
from cachetools import cachedmethod, TTLCache

def simple_decorator(func):
    def wrapper(*args, **kwargs):
        #check cache
        print("simple decorator")
        func(*args, **kwargs)
        #set cache
    return wrapper


class AbstractInput(object):
    def __init__(self, cacheparams = {'maxsize': 10, 'ttl': 300}):
        self.cache = TTLCache(**cacheparams)
        super().__init__()

    @simple_decorator
    def retrieve(self, params):
        print("AbstractInput retrieve")
        raise NotImplementedError("AbstractInput inheritors must implement retrieve() method")

class JsonInput(AbstractInput):
    def retrieve(self, params):
        print("JsonInput retrieve")
        return json.dumps(params)

class SillyJsonInput(JsonInput):
    def retrieve(self, params):
        print("SillyJsonInput retrieve")
        params["silly"] = True
        return json.dumps(params)

实际结果:

>>> ai.retrieve(params)
ai.retrieve(params)
simple decorator
AbstractInput retrieve
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 8, in wrapper
  File "<string>", line 22, in retrieve
NotImplementedError: AbstractInput inheritors must implement retrieve() method
>>> ji.retrieve(params)
ji.retrieve(params)
JsonInput retrieve
'{"happy": "go lucky", "angry": "as a wasp"}'

期望的结果:

>>> ai.retrieve(params)
ai.retrieve(params)
simple decorator
AbstractInput retrieve
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 8, in wrapper
  File "<string>", line 22, in retrieve
NotImplementedError: AbstractInput inheritors must implement retrieve() method
>>> ji.retrieve(params)
simple decorator
ji.retrieve(params)
JsonInput retrieve
'{"happy": "go lucky", "angry": "as a wasp"}'

是的,使用元类强制装饰器使用特定方法,正如您输入自己的答案是正确的。通过一些更改,可以使要装饰的方法不固定 - 例如,装饰函数中设置的属性可以用作“标记”,应在重写方法上强制使用此类装饰器。

除此之外,从Python 3.6开始,还有一个新的类级别机制——特殊方法__init_subclass__,其具体目标是减少对元类的需求。元类可能很复杂,如果您的类层次结构需要组合多个元类,您可能会感到头疼。

The __init_subclass__方法放在基类上,每次创建子类时都会调用一次。包装逻辑可以放在那里。

基本上,您可以修改装饰器以放置我上面提到的标记,并将此类添加到您的继承层次结构中 - 它可以作为多重继承中的 mixin 类放置,因此如果需要,它可以重用于各种类树:

def simple_decorator(func):
    def wrapper(*args, **kwargs):
        print("check cache")
        rt = func(*args, **kwargs)
        print("set cache")
        return rt
    wrapper.inherit_decorator = simple_decorator
    return wrapper

class InheritDecoratorsMixin:
    def __init_subclass__(cls, *args, **kwargs):
         super().__init_subclass__(*args, **kwargs)
         decorator_registry = getattr(cls, "_decorator_registry", {}).copy()
         cls._decorator_registry = decorator_registry
         # Check for decorated objects in the mixin itself- optional:
         for name, obj in __class__.__dict__.items():
              if getattr(obj, "inherit_decorator", False) and not name in decorator_registry:
                  decorator_registry[name] = obj.inherit_decorator
         # annotate newly decorated methods in the current subclass:
         for name, obj in cls.__dict__.items():
              if getattr(obj, "inherit_decorator", False) and not name in decorator_registry:
                  decorator_registry[name] = obj.inherit_decorator
         # finally, decorate all methods anottated in the registry:
         for name, decorator in decorator_registry.items():
              if name in cls.__dict__ and getattr(getattr(cls, name), "inherit_decorator", None) != decorator:
                    setattr(cls, name, decorator(cls.__dict__[name]))

所以,就是这样 - 每个新的子类都会有自己的_decorator_registry属性,其中所有祖先中修饰方法的名称,以及which要应用的装饰器已注释。

如果装饰器应该为该方法使用一次,并且在被重写的方法执行该方法时不重复使用super()调用它的祖先(不是在装饰缓存时的情况,因为不会调用超级方法),这会变得更棘手 - 但可以完成。

然而,这样做很棘手 - 因为超类中的装饰器实例将是子类上的装饰器之外的其他实例 - 将信息传递给“此方法的装饰器代码已经在此链调用中运行”的一种方法是使用实例级标记 - 如果代码要支持并行性,则该标记应该是线程局部变量。

所有这些检查将导致相当复杂的样板文件放入一个简单的装饰器中 - 因此我们可以为我们想要运行一次的“装饰器”创建一个“装饰器”。换句话说,decoratos 装饰有childmost波纹管将仅在“最子级”类上运行,但在超类中调用时不会在超类中的相应方法上运行super()



import threading

def childmost(decorator_func):

    def inheritable_decorator_that_runs_once(func):
        decorated_func = decorator_func(func)
        name = func.__name__
        def wrapper(self, *args, **kw):
            if not hasattr(self, f"_running_{name}"):
                setattr(self, f"_running_{name}", threading.local())
            running_registry = getattr(self, f"_running_{name}")
            try:
                if not getattr(running_registry, "running", False):
                    running_registry.running = True
                    rt = decorated_func(self, *args, **kw)
                else:
                    rt = func(self, *args, **kw)
            finally:
                running_registry.running = False
            return rt

        wrapper.inherit_decorator = inheritable_decorator_that_runs_once
        return wrapper
    return inheritable_decorator_that_runs_once

使用第一个列表的示例:

class A: pass

class B(A, InheritDecoratorsMixin):
    @simple_decorator
    def method(self):
        print(__class__, "method called")

class C(B):
   def method(self):
       print(__class__, "method called")
       super().method()

将清单 1 和这些 A=B-C 类粘贴到 解释器,结果是这样的:

In [9]: C().method()                                                                         
check cache
<class '__main__.C'> method called
check cache
<class '__main__.B'> method called
set cache
set cache

(这里的“A”类完全是可选的,可以省略)


使用第二个清单的示例:


# Decorating the same decorator above:

@childmost
def simple_decorator2(func):
    def wrapper(*args, **kwargs):
        print("check cache")
        rt = func(*args, **kwargs)
        print("set cache")
        return rt
    return wrapper

class D: pass

class E(D, InheritDecoratorsMixin):
    @simple_decorator2
    def method(self):
        print(__class__, "method called")

class F(E):
   def method(self):
       print(__class__, "method called")
       super().method()

结果:


In [19]: F().method()                                                                        
check cache
<class '__main__.F'> method called
<class '__main__.E'> method called
set cache

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

Python子类方法从超类方法继承装饰器 的相关文章

  • 仅检索子类的属性[重复]

    这个问题在这里已经有答案了 我有一个像这样的课程 class parent public foo class child extends parent public lol public function getFields return
  • 在 Dash 中使用单选项目在图表之间切换

    我是 Dash 新手 我想制作一个带有在两个图表之间切换的单选项目的应用程序 但我不知道该怎么做 任何帮助将不胜感激 我已经写了一段代码 但我不知道我是否接近 如果可能的话 我想在最后制作的散点图和散点图2之间进行交换 import num
  • 使用 imaplib 库连接到电子邮件时遇到 AUTHENTICATIONFAILED 错误

    如何连接到 imaplib 库而不遇到 AUTHENTICATIONFAILE 错误 通过网络浏览器登录时 我的 Gmail 收件箱显示严重的安全警报 登录尝试被阻止 IMAP SERVER imap gmail com USERNAME
  • 此 C++ 模板中的迭代器类型应该是什么?

    前一段时间在处理一些图形代码时 我使用整数作为底层坐标持有者编写了 Rect 和 Region 类 并且效果很好 Region 被实现为 STL 列表的简单类扩展 并且仅包含 矩形列表 现在我还需要使用双精度作为底层坐标持有者的相同类型的类
  • 无法在 VS Code 中导入

    我是 python 新手 一直在使用 VS code 现在我正在研究汤普森采样问题 需要 numpy 和 matplotlib 我已经导入了这两个库 但 VS code 给出了无法导入的错误 我知道我必须使用 PIP 进行安装 并且我已经看
  • 如何使用BeautifulSoup查找所有下一个链接

    我目前正在通过预设一个名为 number of pages 的变量来抓取特定网站的所有页面 预设此变量一直有效 直到添加了我不知道的新页面 例如 下面的代码适用于 3 个页面 但网站现在有 4 个页面 base url https secu
  • Python列表内存存储[重复]

    这个问题在这里已经有答案了 据我了解 Python 列表本质上是 C 数组 它们分配特定的顺序内存块 但是 这些内存块实际上存储列表中的数据还是它们只是指向内存中存储实际数据的另一个位置 它可能取决于列表中存储的对象的大小吗 因为您可以轻松
  • 除下一行的值并在数据框中创建列

    我有一个像这样的csv id value 1 100 1 150 1 200 1 250 2 300 2 350 2 400 2 450 我想根据每个唯一 ID 的值生成一列 例如 id 1 的前 2 行值为 100 150 我正在尝试创建
  • Pandas:如何根据另一个数据框的值对数据框上的列求和

    我是 Pandas 新手 我正在尝试做以下事情 我有一个名为的数据框comms包含articleID和commentScore列 等等 我有另一个名为arts带有列文章 ID 我需要创建arts一个名为文章评分 每篇文章必须具有articl
  • Python 3.10 中有 setUpClass 的异步等效项吗?

    我一直在使用unittest IsolatedAsyncioTestCase测试我的异步方法 我一直在利用setUpClass asyncSetUp创建夹具和asyncTearDown进行清理 到目前为止一切进展顺利 但现在我有一个新的要求
  • Python Pandas:沿一列比较两个数据帧,并返回另一个数据帧中两个数据帧的行内容

    我正在处理两个 csv 文件并作为数据框 df1 和 df2 导入 df1 有 50000 行 df2 有 150000 行 我想将 df2 的 时间 与 df1 求时间差并返回所有列的值 对应相似的行 保存在df3中 时间同步 例如 35
  • 在 python3 中优雅地退出多进程[重复]

    这个问题在这里已经有答案了 我想通过 Ctrl C SIGINT 或用户输入优雅地退出程序 如果可能的话 终端应该提示类似的内容 按 Enter 键终止 Python 3 6 执行的代码 def worker process i 0 whi
  • FastAPI/Pydantic 接受任意 post 请求正文吗?

    我想创建一个 FastAPI 端点 它只接受任意的 post 请求正文并返回它 如果我发送 foo bar 我想得到 foo bar 后退 但我也希望能够发送 foo1 bar1 foo2 bar2 并把它拿回来 我试过 from fast
  • 循环列表的值[重复]

    这个问题在这里已经有答案了 我是编码新手 正在尝试编写一个简单的代码 该代码将采用一个列表 例如 1 2 3 并循环元素 n 次 所以如果n 1 我应该得到A 3 1 2 如果n 2 我应该得到A 2 3 1 我写的代码是 n 1 j 0
  • Hibernate 每个子类一个表继承策略的效率

    我正在考虑 Hibernate 管理的类层次结构的表布局 当然 每个子类表技术在我看来是一般意义上最合适的 然而 通过逻辑思考 我对其性能有些担忧 尤其是随着子类数量的扩展 举一个非常简短 且经典 的示例 假设您有以下类 public ab
  • Python3如何安装.ttf字体文件?

    我想使用 python3 更精确的 Python 3 6 代码在 Windows 10 上安装 ttf 字体文件 我用谷歌搜索 但我发现的唯一的就是这个使用python在windows上安装TTF字体 https stackoverflow
  • 如何在离线绘图中绘制垂直线?

    如何使用 python 以离线方式绘制一条垂直线 我想在 x 20 x 40 和 x 60 处添加线条 所有线条都在同一个图中 def graph contracts self trace1 go Scatter x np array ra
  • pandas 在单元格中缩写字典

    我有一个相当复杂的嵌套字典 它使用 pandas 很好地打印为 html 但是 有一个字典作为打印在单元格中的值之一 如下所示 pd set option display max colwidth 1 已设置 所以这不应该是问题 这是产生问
  • 如何将交互式 matplotlib 图形插入 tkinter 画布

    我正在尝试将交互式 matplotlib 图形 具有滑块 重置按钮和单选按钮的图形 放入 tkinter Canvas 中 我已成功添加非交互式图表 但当它变为交互式时找不到问题 我尝试将所有内容更改为使用 matplotlib Figur
  • 类型错误:只能使用标量值执行操作

    如果您能让我知道如何为所提供的表格绘制一些信息丰富的图表 我将不胜感激here https www iasplus com en resources ifrs topics use of ifrs 例如 我需要一个名为 国内非上市公司 非上

随机推荐

  • 如何从字符串转换为数组?

    If s 1 2 3 4 5 我们如何从中获得一个整数数组 我想返回 5 个元素Array Int64 1 1 2 3 4 5 正如 isebarn 使用的那样 split s 对于将字符串拆分为单词非常有用 默认情况下按空格拆分 juli
  • 如何隐藏 URL 中的 ID

    我以前在 Stack Overflow 上见过这类问题 但没有一个真正有帮助 我也用谷歌搜索过 但没有骰子 我想知道如果用户单击选项卡本身是否可以隐藏 URL 中的 ID 这是网页 www planet nu dev new experia
  • Android:如何根据视图模型实时数据属性为片段编写单元测试?

    我的片段 UI 中有一个列表视图 其元素集取决于来自视图模型 LiveData 属性的值的状态 我想为片段创建工具测试 该片段包含与该属性的值集相关的 3 个场景测试用例 但我不知道从哪里开始 我的代码应该如下所示 class MyView
  • 如何让 MSIX 应用安装程序在每次生成/发布期间输出正确的设置?

    问题 如何获取 MSIXappinstaller在每次构建 发布期间输出正确的设置 Context 这是一个后续问题启用侧面加载后 为什么 MSIX 不会在每次应用程序运行时自动检查更新 https stackoverflow com q
  • 关于java中同步的问题;何时/如何/到什么程度

    我正在开发我的第一个多线程程序 并在同步的几个方面陷入困境 我已经浏览了 oracle sun 主页上的多线程教程 以及这里的一些关于 SO 的问题 所以我相信我知道什么是同步 然而 正如我提到的 有几个方面我不太确定如何弄清楚 我以明确问
  • 函数参数的生命周期是多少(需要引用)? [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 函数参数的生命周期是否等于作为 右值 引用传递的未命名临时变量 等于调用该函数的表达式 我的 gcc 编译器表明确实如此 但我想看到一个实际
  • Scala REPL 中的递归重载语义 - JVM 语言

    使用 Scala 的命令行 REPL def foo x Int Unit def foo x String Unit println foo 2 gives error type mismatch found Int 2 required
  • 如何用VTK存储矢量场? C++、VTKWriter

    比方说 我有一个向量场 u 其分量为 ux uy 和 uz 定义在空间 rx ry 和 rz 中的 非结构化 位置 我想要的只是用 VTK 格式存储这个向量场 即使用 libvtk 中的 vtkwriter 类来存储 Paraview 的可
  • 如何为 Visual Studio 代码设置 boost

    所以我基本上构建了整个boost库并将它们放在C boost include boost 1 73中 但我有一种感觉 在使用这个库之前我必须包含更多步骤 我一直遵循本指南 直到将其设置为 Visual Studio 代码 所以现在我正在互联
  • Spring MVC 中 ApplicationContext 和 WebApplicationContext 有什么区别?

    应用程序上下文和 Web 应用程序上下文有什么区别 我知道WebApplicationContext用于面向Spring MVC架构的应用程序 我想知道有什么用ApplicationContext在 MVC 应用程序中 以及定义了什么样的b
  • mongodb - 检索数组子集

    看似简单的任务对我来说是一个挑战 我有以下 mongodb 结构 services TCP80 data status 1 delay 3 87 ts 1308056460 status 1 delay 2 83 ts 1308058080
  • 具有 JPA、PostgreSQL 和 NULL 值的 JodaTime

    我试图将 JPA 的 JodaTime DateTime 字段保留到 PostgreSQL 但遇到了指向数据库 NULL 值的空指针的问题 我正在使用 NetBeans 7 beta 2 IDE 持久性实现是 EclipseLink 2 2
  • WPF应用程序,在打开窗口之前运行异步任务

    我遇到了一个问题 我需要在主窗口打开并显示之前运行异步任务 IE STAThread static void Main string args MainWindow window new MainWindow SplashScreen Sh
  • 在模态上自动滚动引导模态('show')

    我有一个用于评论的引导模式 评论表格附加在评论列表的底部 我希望此模式在打开时滚动到列表底部 如果有超过 2 或 3 条评论 我已经安装了这个幻灯片插件 http jesseprice com jquery slide to plugin
  • Android中的OpenGL用于视频显示

    是否可以使用 OpenGL 来显示视频并能够在运行时调整视图大小 是的 它的工作原理是通过 glTexSubImage2D 将每个帧作为纹理上传 我已经测试了基于 FFmpeg 的解码器的输出 效果很好
  • C++ 压缩字节数组

    大家好 我加载一组图像并生成体积数据 我将此体积数据保存在 无符号字符 体积 array 现在我想将此数组保存在文件中并检索 但在保存之前我想压缩字节数组 因为卷数据很大 这方面有什么建议吗 提前致谢 volume在你的例子中不是一个数组
  • Android studio 模拟器中运行的WebView无法访问互联网,但Chrome可以?

    我安装了 Android studio 1 0 1 并按照此页面操作https developer chrome com multidevice webview gettingstarted https developer chrome c
  • 使用DockerOperator时如何同时使用xcom_push=True和auto_remove=True?

    Problem 跑步时DockerOperator with xcom push True xcom all True and auto remove True 任务会引发错误 就好像容器在读取其内容之前被删除一样STDOUT Exampl
  • flutter - SliverList / SliverChildBuilderDelegate 提供初始索引或允许负索引

    我目前正在 Flutter 中使用 SliverList 和 SliverChildBuilderDelegate 构建日历视图 这样我就不必一次渲染日历中的每个项目 第一个日期是纪元时间 即 1970 年 1 月 1 日 最后一个日期是在
  • Python子类方法从超类方法继承装饰器

    我有一个具有retrieve 方法的超类 并且它的子类每个都实现自己的retrieve 方法 我希望每个retrieve 方法都被装饰为在收到相同参数时缓存返回值 而不必在每个子类中装饰该方法 装饰器似乎不能被继承 我可能可以调用超类的方法