Python 从元类访问构造函数的参数
TL;DR-Python 从元类访问构造函数的参数,python,python-2.7,oop,instance,metaclass,Python,Python 2.7,Oop,Instance,Metaclass,TL;DR- 我有一个使用元类的类。 我想在初始化过程之前从元类访问对象构造函数的参数,但找不到访问这些参数的方法。 如何从元类函数\uuuu new\uuuu访问构造函数的参数? 为了在python中练习元类的使用,我想创建一个类,该类将用作《银河系漫游指南》一书中的超级计算机“深入思考” 我的课程的目的是存储超级计算机从用户那里得到的各种查询。 归根结底,它只需要获取一些参数并存储它们。 如果给定的参数之一是数字42或字符串“生命、宇宙和一切的答案”,我不想创建新对象,而是返回指向现有对象
我有一个使用元类的类。
我想在初始化过程之前从元类访问对象构造函数的参数,但找不到访问这些参数的方法。
如何从元类函数
\uuuu new\uuuu
访问构造函数的参数?
为了在python中练习元类的使用,我想创建一个类,该类将用作《银河系漫游指南》一书中的超级计算机“深入思考” 我的课程的目的是存储超级计算机从用户那里得到的各种查询。
归根结底,它只需要获取一些参数并存储它们。
如果给定的参数之一是数字42或字符串“生命、宇宙和一切的答案”,我不想创建新对象,而是返回指向现有对象的指针。
这背后的想法是,这些对象将是完全相同的,因此当使用
is
操作符来比较这两个对象时,结果将是真实的
为了能够使用is
操作符并得到True
作为答案,我需要确保这些变量指向同一个对象。因此,为了返回指向现有对象的指针,我需要在对象的初始化过程中间进行干预。我无法在构造函数本身检查给定的参数并相应地修改对象的内部变量,因为这太晚了:如果我只在\uuuuu init\uuuuuu
函数中检查给定的参数,那么这两个对象将被分配到内存的不同部分(使用is
运算符时,它们可能相等,但不会返回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()
但我一直在从构造函数获取参数。我看到
\uuuu new\uuuu
方法只得到四个参数:元类实例本身、类的名称、基和属性。
如何将参数从构造函数发送到元类?
为了实现我的目标,我能做些什么?你不需要元类 事实上,
\uuuuuu init\uuuuu
在Python中不是对象的“构造函数”,而是通常称为“初始化器”。\uuuu new\uuuuu
更接近于“构造函数”的角色在其他语言中,它不仅对元类可用-所有类都有一个\uuuuuu new\uuuu
方法。如果没有显式实现,则直接调用对象
实际上,它是对象。\uuuuuu new\uuuu
在Python中创建了一个新对象。从纯Python代码中,没有其他可能的方法来创建对象:它将始终通过那里。这意味着如果您在自己的类上实现\uuuu new\uuuuu
方法,您可以选择不创建新实例,而是重新创建urn同一类(或任何其他对象)的另一个预先存在的实例
您只需记住:如果\uuuuuu new\uuuuu
返回同一类的实例,那么默认行为是对同一实例调用\uuuuuuu init\uuuuu
。否则,不调用\uuuuuu init\uuuuu
还值得注意的是,近年来,一些使用元类在Python中创建“单例”的方法变得流行起来——这实际上是一种过激的方法,s重写\uuuuuu new\uuuu
对于创建单例也更为可取
在您的情况下,您只需要拥有一个字典,其中包含要跟踪的参数作为密钥,并检查是否在运行\uuuu new\uuuu
时创建新实例或“回收”实例。字典可能是类属性,也可能是模块级别的全局变量-这是您选择的:
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
如果除此之外,\uuuuu init\uuuuu
中还有任何代码,请将其移动到\uuuu new\uuuuu
您可以有一个具有这种行为的基类,并有一个类层次结构,而无需为每个类重新实现\uuuuu new\uuuu
至于元类,当实际创建使用该元类创建的类的新实例时,不会调用它的任何方法。只有通过在使用该元类创建的类上装饰或创建新的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu新方法,自动插入此行为才有用。由于此行为更容易跟踪,maintain、 总的来说,只要使用普通继承就可以与其他类结合,根本不需要元类。您试图在错误的位置进行检查。它需要在查询中。\uuuuuu新的\uuuuuu
,而不是在超级计算机中。\uuu新的\uuuuuuuu
。您根本不需要元类。我不明白您为什么需要元类。元类<代码>\uuuuuuuuuuuuuuuuu
是在定义类查询时执行的,而此时您似乎不想做任何事情。如果您想在查询实例化时进行检查,只需在查询中进行检查。\uuuuuuuuuuuuuuuuuuuuuuuuuuu
。因此,在这种情况下我根本不需要使用元类
?我认为我需要这样做,因为这样做是为了mble是一个(半)单例,从某种意义上说,它有时返回相同的对象,而不是创建一个新的对象,我发现实现这一点的最佳方法是使用元类()。您能详细说明一下吗,这样我就可以理解有什么不同,我能做什么不同?使用元类远远不是创建单例的最佳方法。(事实上,我在阅读本评论之前的回答中就已经提到了这一点)