有没有办法在继承期间持久化装饰器?

2024-10-01 17:35:16 发布

您现在位置:Python中文网/ 问答频道 /正文

当他们试图用一个未实现的方法来重写一个定义的类的值时,我将重写一个未实现的类的值。在

当我使用下面显示的代码时,子方法不调用decorator。我认为这是因为方法被重写了,这很有意义。我的问题是: 有没有一种方法可以通过方法重写使装饰器持久化?在

我不反对使用其他东西,但这是一个解决方案,很快出现在我的脑海中,我很好奇,知道是否有任何办法使它工作。在

如果与一个装饰是正确的和可能的选择,它看起来像这样:

def decorator(returntype):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            result = function(*args, **kwargs)
            if not type(result) == returntype:
                raise TypeError("Method must return {0}".format(returntype))
            else:
                return result
        return wrapper
    return real_decorator

我需要我的父类看起来像这个:

^{pr2}$

子类会做这样的事情:

^{3}$

如果需要的话,我会非常乐意澄清我的问题,并感谢所有抽出时间提前阅读此问题的人!在


Tags: 方法代码return定义defargsfunction装饰
3条回答

我不确定您是否可以按照您想要的方式持久化decorator的效果,但是您仍然可以在Parent类中装饰一个包装函数,它将不是abstractmethod并让子类实现这样的包装函数:

from abc import ABC, abstractmethod

def decorator(returntype):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            result = function(*args, **kwargs)
            if not type(result) == returntype:
                raise TypeError("Method must return {0}".format(returntype))
            else:
                return result
        return wrapper
    return real_decorator

class Parent(ABC):
    @decorator(int)
    def aye(self, a):
        return self.impl_aye(a)

    @abstractmethod
    def impl_aye(self, a):
        raise NotImplementedError


class Child(Parent):
    def impl_aye(self, a):
        return a

还有一些解决方案可以保护aye方法,使其不受要重写的Parent类的影响。例如,请参见this answer。在

否则,如果您想使用type hints并使用mypyPython的可选静态类型检查器)检查代码,如果您试图实现返回类型与其父类不兼容的子类,则可能会收到错误消息:

^{pr2}$

mypy的输出:

a.py:9: error: Return type "str" of "aye" incompatible with return type "int" in supertype "Parent"
Found 1 error in 1 file (checked 1 source file)

如果您只有想要强制执行返回类型,下面是我的非装饰性建议(最初没有将其放入,因为我不喜欢您不想在SO上执行此“答案”)。在

class Parent:

    def aye(self, a):
        res = self._aye(a)
        if not isinstance(res, int):
            raise TypeError("result should be an int")
        return res

    def _aye(self, a):
        raise NotImplementedError()

class Child(Parent):

    def _aye(self, a):
        return 1

下面是如何使用元类来实现这一点。在python3.8上测试过。应该像3.6版和更高版本一样工作。诚然,这有点复杂,使用另一种技术可能更好。在

from abc import ABCMeta, abstractmethod
from functools import wraps
from inspect import isfunction


class InheritedDecoratorMeta(ABCMeta):

    def __init__(cls, name, bases, attrs):
        for name, attr in attrs.items():
            for base in bases:
                base_attr = base.__dict__.get(name)
                if isfunction(base_attr):
                    inherited_decorator = getattr(base_attr, 'inherited_decorator', None)
                    if inherited_decorator:
                        setattr(cls, name, inherited_decorator()(attr))
                        break


def inherited_decorator(decorator, result_callback):
    def inner_decorator(method):
        method.inherited_decorator = lambda: inherited_decorator(decorator, result_callback)
        @wraps(method)
        def wrapper(*args, **kwargs):
            result = method(*args, **kwargs)
            return result_callback(method, result, args, kwargs)
        return wrapper
    return inner_decorator


def returns(type_):
    if not isinstance(type_, type) and type_ is not None:
        raise TypeError(f'Expected type or None; got {type_}')
    def callback(method, result, args, kwargs):
        result_type = type(result)
        if type_ is None:
            if result is not None:
                raise TypeError(f'Expected method {method} to return None; got {result_type}')
        elif not isinstance(result, type_):
            raise TypeError(f'Expected method {method} to return {type_}; got {result_type}')
        return result
    return inherited_decorator(returns, callback)


class MyBaseClass(metaclass=InheritedDecoratorMeta):

    @returns(int)
    @abstractmethod
    def aye(self, a):
        raise NotImplementedError

    @returns(None)
    @abstractmethod
    def bye(self, b):
        raise NotImplementedError


class MyClass(MyBaseClass):

    def aye(self, a):
        return a

    def bye(self, b):
        return b

    @returns(str)
    def cye(self, c):
        return c


if __name__ == '__main__':
    instance = MyClass()

    instance.aye(1)
    try:
        instance.aye('1')
    except TypeError as exc:
        print(exc)

    instance.bye(None)
    try:
        instance.bye(1)
    except TypeError as exc:
        print(exc)

    instance.cye('string')
    try:
        instance.cye(1)
    except TypeError as exc:
        print(exc)

相关问题 更多 >

    热门问题