从元类访问构造函数的参数

Accessing the parameters of a constructor from a metaclass

TL;DR -
我有一个使用 metaclass.
的 class 我想在初始化过程之前从 metaclass 访问对象构造函数的参数,但我找不到访问这些参数的方法。
如何从元class函数__new__访问构造函数的参数?


为了练习在 python 中使用 metaclasses,我想创建一个 class 用作超级计算机 "Deep Thought"这本书 "The Hitchhiker's Guide to the Galaxy"。

我的 class 的目的是存储超级计算机从用户那里获得的各种查询。
最后,它只会获取一些参数并存储它们。
如果给定的参数之一是数字 42 或字符串 "The answer to life, the universe, and everything",我不想创建一个新对象,而是 return 一个指向现有对象的指针。
这背后的想法是这些对象将完全相同,因此当使用 is 运算符比较这两个对象时,结果将为真。

为了能够使用 is 运算符并得到 True 作为答案,我需要确保这些变量指向同一个对象。所以,为了return一个指向现有对象的指针,我需要在对象的初始化过程中间进行干预。我无法检查构造函数本身的给定参数并相应地修改对象的内部变量,因为为时已晚:如果我仅检查给定参数作为 __init__ 函数的一部分,那么这两个对象将分配给内存的不同部分(它们可能相等,但在使用 is 运算符时不会 return True)。

我想过这样做:

class SuperComputer(type):

    answer = 42

    def __new__(meta, name, bases, attributes):
        # Check if args contains the number "42" 
        # or has the string "The answer to life, the universe, and everything"
        # If so, just return a pointer to an existing object:
        return SuperComputer.answer

        # Else, just create the object as it is:
        return super(SuperComputer, meta).__new__(meta, name, bases, attributes)


class Query(object):
    __metaclass__ = SuperComputer

    def __init__(self, *args, **kwargs):
        self.args = args
        for key, value in kwargs.items():
            setattr(self, key, value)


def main():
    number = Query(42)
    string = Query("The answer to life, the universe, and everything")
    other = Query("Sunny", "Sunday", 123)
    num2 = Query(45)

    print number is string  # Should print True
    print other is string  # Should print False
    print number is num2  # Should print False


if __name__ == '__main__':
    main()

但我一直坚持从构造函数获取参数。
我看到 __new__ 方法只有四个参数:
metaclass 实例本身、class 的名称、它的基础和它的属性。
如何将参数从构造函数发送到 metaclass?
我可以做些什么来实现我的目标?

你不需要元class。

事实是 __init__ 不是 Python 中对象的 "constructor",而是通常称为 "initializator" 的对象。 __new__ 在其他语言中更接近于 "constructor" 的作用,并且它不仅仅适用于元 class - 所有 classes 都有一个 __new__ 方法。如果未显式实现,则直接调用 object.__new__

实际上,是 object.__new__ 在 Python 中创建了一个新对象。从纯 Python 代码,没有其他可能的方法来创建对象:它总是会通过那里。这意味着如果您自己实现 __new__ 方法 class,您可以选择不创建新实例,而是 return 相同 [=] 的另一个预先存在的实例53=](或任何其他对象)。

您只需记住:如果 __new__ return 是相同 class 的实例,则默认行为是 __init__ 被调用在同一个实例上。否则,__init__ 不会被调用。

还值得注意的是,近年来一些使用 metaclasses 在 Python 中创建 "singletons" 的方法变得流行 - 这实际上是一种矫枉过正的方法,a s overriding __new__ 也更适合创建单例。

在你的情况下,你只需要有一个字典,其中包含你想要跟踪的参数作为你的键,并检查你是否创建了一个新实例或 "recycle" 每当 __new__ 运行时。字典可以是一个 class 属性,或者是模块级别的全局变量——这是你的选择:

class Recycler:
   _instances = {}
   def __new__(cls, parameter1, ...):
       if parameter1 in cls._instances:
           return cls._instances[parameter1] 
       self = super().__new__(cls) # don't pass remaining parameters to object.__new__
       _instances[parameter1] = self
       return self

如果您在 __init__ 中还有其他代码,请将其也移至 __new__

您可以拥有一个具有此行为的基础class,并拥有一个 class 层次结构,而无需为每个 class 重新实现 __new__

对于元class,在实际创建使用该元class 创建的 classes 的新实例时,将调用其方法的 none。只有在使用该元 class 创建的 class 上通过装饰或创建新的 __new__ 方法来自动插入此行为才有用。由于此行为更易于跟踪、维护,并且与其他 classes 整体结合使用普通继承,因此根本不需要元 class。