python - 使用 __new__ 覆盖子类中的 __init__

标签 python object methods instance object-initializers

我对使用 __new__ 功能将代码注入(inject)子类的 __init__ 函数很感兴趣。我从文档中了解到,python 将在 __new__ 返回的实例上调用 __init__。但是,我在从 __new__ 返回实例之前更改实例中 __init__ 的值的努力似乎不起作用。

class Parent(object):

    def __new__(cls, *args, **kwargs):
        new_object = super(Parent, cls).__new__(cls)
        user_init = new_object.__init__
        def __init__(self, *args, **kwargs):
            print("New __init__ called")
            user_init(self, *args, **kwargs)
            self.extra()
        print("Replacing __init__")
        setattr(new_object, '__init__', __init__)
        return new_object

    def extra(self):
        print("Extra called")

class Child(Parent):

    def __init__(self):
        print("Original __init__ called")
        super(Child, self).__init__()

c = Child()

上面的代码打印:

Replacing __init__
Original __init__ called

但我希望它能打印出来

Replacing __init__
New __init__ called
Original __init__ called
Extra called

为什么不呢?

我觉得 Python 正在调用 __init__ 的原始值,而不管我在 __new__ 中将其设置为什么。在 c.__init__ 上运行内省(introspection)显示新版本已就位,但尚未在对象创建过程中调用它。

最佳答案

好吧,在调用 __init__ 之前,新对象应该是空的。因此,作为优化,python 可能不会费心查询对象并直接从类中获取 __init__

因此,您必须自己修改子类的 __init__。幸运的是,Python 有一个工具,元类。

在 Python 2 中,您可以通过设置特殊成员来设置元类:

class Parent(object):
    __metaclass__ = Meta
    ...

参见 Python2 documentation

在 Python 3 中,你通过父列表中的关键字属性设置元类,所以

class Parent(metaclass=Meta):
    ...

参见 Python3 documentation

元类是类实例的基类。它必须派生自 type 并且在它的 __new__ 中它可以修改正在创建的类(我相信 __init__ 也应该被调用,但是这些示例覆盖了 __new__,所以我将使用它)。 __new__ 将与您拥有的类似:

class Meta(type):
    def __new__(mcs, name, bases, namespace, **kwargs):
        new_cls = super(Meta, mcs).__new__(mcs, name, bases, namespace, **kwargs)
        user_init = new_cls.__init__
        def __init__(self, *args, **kwargs):
            print("New __init__ called")
            user_init(self, *args, **kwargs)
            self.extra()
        print("Replacing __init__")
        setattr(new_cls, '__init__', __init__)
        return new_cls

(使用 Python 3 示例,但 Python 2 中的签名似乎相同,只是没有 **kwargs,但添加它们应该不会有什么坏处;我没有测试它).

关于python - 使用 __new__ 覆盖子类中的 __init__,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34781840/

相关文章:

python - 守护进程模式下的 celery

oop - 何时使用方法和对象

javascript - 将 html 元素值添加到 javascript 对象属性

java - 如何修复java代码以将结果显示为名称和分数

在 Raku 中测试私有(private)方法

python - 在 OpenCv 中以给定角度测量轮廓宽度

java - 调用 midlet jar 将数据检索到 python 代码中

Python 正则表达式没有返回任何重复的内容?

javascript - 将 Javascript 对象编码为 Json 字符串

javascript - 如何迭代名称数组并将每个名称分配为对象中属性的值?