唯一需要元类的东西实际上很简单:
完全创建 intances 和 all 属性。
它所要做的就是将它们插入到命名空间中。啊,它还必须包装类 __new__ 方法以将新实例插入到 instances 列表中。
all 想要的行为部分很有趣,可以使用描述符协议和属性访问控制来实现,因此我们必须制作几个特殊的类,它们会在请求时返回适当的对象之后 ”。”。
“All”是将被实例化为“all”的类 - 它只需要一个 __get__ 方法来返回另一个特殊对象,该对象来自已绑定到父类的 AllAttr 类。
“AllAttr”是一个特殊对象,在任何属性访问时,都对所有者类“instance”属性的成员执行您的要求。
而“CallAllList”是一个特殊的可调用列表子类,依次调用其所有成员。如果所有者类的所需属性本身是可调用的,则 AllAttr 使用它。
class CallAllList(list):
def __call__(self, *args, **kwargs):
return [instance(*args, **kwargs) for instance in self]
class AllAttr(object):
def __init__(self, owner):
self._owner = owner
def __getattr__(self, attr):
method = getattr(self._owner, attr, None)
cls = CallAllList if callable(method) else list
return cls(getattr(obj, attr) for obj in self._owner.instances)
def __setattr__(self, attr, value):
if attr == "_owner":
return super(AllAttr, self).__setattr__(attr, value)
for obj in self._owner.instances:
setattr(obj, attr, value)
class All(object):
def __get__(self, instance, owner):
return AllAttr(owner)
def __repr__(self):
return "Representation of all instances of '{}'".format(self.__class__.__name__)
class MetaAll(type):
def __new__(metacls, name, bases, namespace):
namespace["all"] = All()
namespace["instances"] = []
cls = super(MetaAll, metacls).__new__(metacls, name, bases, namespace)
original_new = getattr(cls, "__new__")
def __new__(cls, *args, **kwargs):
instance = original_new(cls, *args, **kwargs)
cls.instances.append(instance)
return instance
cls.__new__ = __new__
return cls
class Foo(metaclass=MetaAll):
pass
上面的代码是为了兼容 Python 3 和 Python 2 而编写的,因为鉴于您的“打印”示例,您似乎仍在使用 Python2。
唯一不能与这两种形式兼容的是元类 using 声明本身 - 如果您使用 Python 2,只需在 Foo 类的主体内声明 __metaclass__ = MetaAll 即可。但您不应该真正使用 Python2,只需更改为尽快使用 Python 3。
更新
碰巧 Python 2 有“未绑定方法”图,__new__ 的特殊大小写不像在 Python 3 中那样工作:你不能只将名为 __new__ 的函数归因于类。为了从超类中获得正确的__new__ 方法,最简单的方法是创建一个一次性类,以便对其进行线性搜索。否则,必须重新实现 MRO 算法才能获得正确的 __new__ 方法。
因此,对于 Python 2,元类应该是这样的:
class MetaAll(type):
def __new__(metacls, name, bases, namespace):
namespace["all"] = All()
namespace["instances"] = []
if "__new__" in namespace:
original_new = namespace["__new__"]
def __new__(cls, *args, **kwargs):
instance = original_new(cls, *args, **kwargs)
cls.instances.append(instance)
return instance
else:
# We create a disposable class just to get the '__mro__'
stub_cls = super(MetaAll, metacls).__new__(metacls, name, bases, {})
for parent in stub_cls.__mro__[1:]:
if "__new__" in parent.__dict__:
original_new = parent.__dict__["__new__"]
break
def __new__(cls, *args, **kwargs):
instance = original_new(cls, *args, **kwargs)
cls.instances.append(instance)
return instance
namespace["__new__"] = __new__
final_cls = super(MetaAll, metacls).__new__(metacls, name, bases, namespace)
return final_cls
class Foo(object):
__metaclass__ = MetaAll
(现在,再一次,这件事很古老。只要满足于 Python 3.6)