python-3.x - 使用类包装器和 __new__ 装饰类

标签 python-3.x

代码:

import functools


class MyInt1(int):

    def __new__(cls, x, value):
        print("MyInt1.__new__", cls, x, value)
        return super().__new__(cls, x, base=2)

    def __init__(self, x, value):
        print("MyInt1.__init__", self, x, value)
        self.value = value
        super().__init__()


class MyInt2:

    def __init__(self, x, value):
        print("MyInt2.__init__", self, x, value)
        self.value = value


def decorator(class_):

    class Wrapper(class_):

        def __new__(cls, *args, **kwargs):
            print("Wrapper.__new__", cls, args, kwargs)
            obj = super().__new__(cls, *args, **kwargs)
            ...
            return obj

        def __init__(self, *args, **kwargs):
            print("Wrapper.__init__", self, args, kwargs)
            functools.update_wrapper(self, class_)
            super().__init__(*args, **kwargs)

    return Wrapper


c = decorator(MyInt1)("101", 42)
print(c, c.value)
c = decorator(MyInt2)("101", 42)
print(c, c.value)

输出:

Wrapper.__new__ <class '__main__.decorator.<locals>.Wrapper'> ('101', 42) {}
MyInt1.__new__ <class '__main__.decorator.<locals>.Wrapper'> 101 42
Wrapper.__init__ 5 ('101', 42) {}
MyInt1.__init__ 5 101 42
5 42
Wrapper.__new__ <class '__main__.decorator.<locals>.Wrapper'> ('101', 42) {}
Traceback (most recent call last):
  File "tmp2.py", line 42, in <module>
    c = decorator(MyInt2)("101", 42)
  File "tmp2.py", line 28, in __new__
    obj = super().__new__(cls, *args, **kwargs)
TypeError: object() takes no parameters
  • 如果我真的不知道传递给装饰器的类应该如何处理?
  • 为什么它不适用于常见模式?
  • 为什么默认 __new__ 不接受 __init__ 参数?

我发现的唯一方法是inspect.isbuiltin检查super().__new__和分支,但这很脏。

最佳答案

很难找到有关 Python 基类(对象)行为的详细信息,这些行为是为了实际实现 __init__ 而实现的。创建新类时:

object自己的__init____new__除了 self 之外,方法不接受任何参数和cls 。然而,如果它们是从子类(恰好是 Python 中定义的所有其他类)调用的,这些方法的每个方法都会检查子类是否定义了其中一个而不是另一个(即对象的 __init__ 检查是否被实例化的类也定义了 __new__ 或未定义)。

如果任一方法发现反向方法已被覆盖而其本身没有被覆盖,它只会吞掉任何额外的参数:因此用户的类 __init__可以有参数,而不必担心相同的参数 - 将传递给 object.__new__会导致错误,

所以,您遇到的问题是,在此检查期间,例如对象的 __new__发现你的 wrapper 有 __init__定义 - 因此它不会吞咽任何参数 - 和错误,因为有额外的参数。

如果您保留此模式,解决此问题的唯一方法是在装饰器中重新实现相同的逻辑:

def decorator(class_):

    def has_method(cls, meth):
        # (FIXME:the check bellow does not take in account other applications of this decorator)
        return any(meth in ancestor.__dict__ for ancestor in cls.__mro__[:-1]):

    def has_new(cls):
        return has_method(cls, "__new__")

    def has_init(cls):
        return has_method(cls, "__init__")

    class Wrapper(class_):

        def __new__(cls, *args, **kwargs):
            print("Wrapper.__new__", cls, args, kwargs)
            if  (args or kwargs) and not has_new(cls) and has_init(cls):
                args, kwargs = (), {}
            obj = super().__new__(cls, *args, **kwargs)
            ...
            return obj

        def __init__(self, *args, **kwargs):
            print("Wrapper.__init__", self, args, kwargs)
            functools.update_wrapper(self, class_)
            cls = self.__class__
            if (args or kwargs) and not has_init(cls) and has_new(cls):
                args, kwargs = (), {}
            super().__init__(*args, **kwargs)

    return Wrapper

https://mail.python.org/pipermail/python-list/2016-March/704027.html是存在这种行为的暗示 - 我已经在官方文档中看到过它,但在某些地方我不记得是哪个。

关于python-3.x - 使用类包装器和 __new__ 装饰类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41956677/

相关文章:

python - 如何在Python中对多个术语使用正向和负向前瞻?

python-3.x - Pandas 不可哈希类型 : 'numpy.ndarray' with pandas groupby

python - Collat​​z 环结构

python 3.5 asyncio 和 aiohttp Errno 101 网络无法访问

Python:如何导入没有 python-filename 作为子模块的模块?

python - 如何获取运算符模块的 sorted 函数来存储数据,以便我可以追加文件?

python-3.x - 贪心算法和时间复杂度#2

python - 在 Python 中从元组的元组转换为嵌套元组

python - 如何重命名 pandas 数据框中索引上方的列号 0

python - 我需要从 python 中的机器学习模型中打印出超参数和参数