从元类

时间:2018-01-14 21:31:50

标签: python python-2.7 oop instance metaclass

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

为了在python中练习使用元类,我想创建一个类,它将被用作“银河系漫游指南”一书中的超级计算机“Deep Thought”。

我班级的目的是存储超级计算机从用户那里获得的各种查询 在底线,它只会得到一些参数并存储它们 如果其中一个给定的参数是数字42或字符串“生命,宇宙和一切的答案”,我不想创建一个新对象,而是返回一个指向现有对象的指针。
这背后的想法是那些对象完全相同,所以当使用is运算符来比较这两个对象时,结果将是真的。

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

我想做类似的事情:

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__方法只得到四个参数:
元类实例本身,类的名称,基数和属性 如何将构造函数中的参数发送到元类? 我能做些什么来实现我的目标?

1 个答案:

答案 0 :(得分:2)

你不需要一个元类。

事实是__init__不是"构造函数"相反,它通常被称为"初始化器" 。 __new__更接近于"构造函数"在其他语言中,它不仅适用于元类 - 所有类都有__new__方法。如果没有明确实现,则直接调用object.__new__

实际上,它是object.__new__,它在Python中创建了一个新对象。从纯Python代码开始,没有其他可能的方法来创建对象:它将始终贯穿始终。这意味着如果在自己的类上实现__new__方法,则可以选择不创建新实例,而是返回同一类(或任何其他对象)的另一个预先存在的实例。

您只需要记住:如果__new__返回同一个类的实例,那么默认行为是在同一个实例上调用__init__。否则,不会调用__init__

值得注意的是,近年来有一些创造单身人士的方法"在Python中使用元类变得流行 - 它实际上是一种矫枉过正的方法,最重要的__new__也是创建单例的首选。

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

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__

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

对于元类,在实际创建使用该元类创建的类的新实例时,不会调用其任何方法。仅通过在使用该元类创建的类上装饰或创建新的__new__方法来自动插入此行为是有用的。由于这种行为更易于跟踪,维护和整体使用普通继承与其他类组合,因此根本不需要元类。