gpt4 book ai didi

python-3.x - Python子类方法从父类(super class)方法继承装饰器

转载 作者:行者123 更新时间:2023-12-02 04:12:00 24 4
gpt4 key购买 nike

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

装饰器似乎不能被继承。我可能可以调用父类(super class)的方法来设置缓存,但目前我的父类(super class)引发了一个 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__ ,其具体目标是减少对元类的需求。元类可能很复杂,如果您的类层次结构需要组合多个元类,您可能会感到头痛。

__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 属性,其中包含所有祖先中装饰方法的名称,以及要应用的哪个装饰器已注释。

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

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

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



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()

将 list 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

关于python-3.x - Python子类方法从父类(super class)方法继承装饰器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57104276/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com