(AttributeError: __enter__) when using __new__ magic method

(AttributeError: __enter__) when using __new__ magic method

我正在尝试使用 __new__ 方法在处理之前检查参数有效性 (str),所以我正在执行以下操作:

class foo:

    def __new__(cls, argument):
        if not isinstance(argument, str):
            raise TypeError('argument must be str')
        print("passed __new__")

    def __init__(self, argument):
        self.argument = argument
        print("passed __init__")

    def __enter__(self):
        print("passed __enter__")

    def __exit__(self, *args):
        print("passed __exit__")

然后,当我使用调用 __enter____exit__

with 关键字调用它时
with foo('test'):
    print("i am between __enter__ and __exit__, damn!")

我得到这个输出:

passed __new__
AttributeError: __enter__

我期望这个输出:

passed __new__
passed __init__
passed __enter__
i am between __enter__ and __exit__, damn!
passed __exit__

我在完全删除 __new__ 方法时注意到;它会起作用。为什么使用 __new__ 会破坏它?以及如何在 __new__ 现有的情况下让它工作?

__new__ 必须实际创建 return 您的 class.

的一个实例
class foo:

    def __new__(cls, argument):
        if not isinstance(argument, str):
            raise TypeError('argument must be str')
        print("passed __new__")
        <b>return super().__new__(cls)</b>

    def __init__(self, argument):
        self.argument = argument
        print("passed __init__")

    def __enter__(self):
        print("passed __enter__")

    def __exit__(self, *args):
        print("passed __exit__")

在您的原始代码中,foo.__new__ returned None,实际上 None 没有属性 __enter__。此外,由于 None 不是 foo 的实例,因此抑制了对 foo.__init__ 的隐式调用。


因为 footype 的实例,调用 foo('test')

的缩写
type.__call__(foo, 'test')

你可以想象 __call__ 的定义类似于

def __call__(cls, *args, **kwargs):
    obj = cls.__new__(*args, **kwargs)
    if isinstance(obj, cls):
        obj.__init__(*args, **kwargs)
    return obj

覆盖 __new__ 意味着您想要更改 创建实例的方式 ,而覆盖 __init__ 意味着您想要更改已创建实例的方式得到初始化。在这种情况下,如果参数类型错误,您可以完全避免创建新实例。