Subclassing class 实现单例思想 metaclass

Subclassing a class that implements singleton thought metaclass

我有这个代码:

class Singleton(type):

  def __call__(cls,*args,**kwargs):
    if cls.created is None :
      print('called')
      cls.created = super().__call__(*args,**kwargs)
      return cls.created
    else:
      return cls.created
  def __new__(cls,name,base,attr,**kwargs):
    return super().__new__(cls,name,base,attr,**kwargs)


class OnlyOne(metaclass=Singleton):
  created = None
  def __init__(self,val):
      self.val = val

class OnlyOneTwo(OnlyOne):
  pass


k = OnlyOne(1)
a = OnlyOneTwo(2)



print(a.val)
print(k.val)
print('a.created: {0} - b.created: {1}'.format(id(a.created),id(k.created)))

我是 Python 3 的新手,所以我决定做一些小实验并尝试使用 Python 的 metaclasses。

在这里,我尝试制作一个元class,在设置时将 class 严格限制为单个实例。

我不确定这是否有效,但每当我尝试这样做时:

k = OnlyOne(1)
a = OnlyOneTwo(2)

输出将是:

called
1
1

这意味着 OnlyOneTwo 没有设置但是当我尝试这样做时:

a = OnlyOneTwo(2)
k = OnlyOne(1)

输出将是:

called
called
2
1

有人可以帮我追溯吗?我有点困惑,但这是我的初始 questions/thoughts:

  1. OnlyOneTwocreated属性和OnlyOne的一样吗?因为我通过 id() 得到不同的结果,具体取决于我首先定义的结果。如果先OnlyOneTwo就不一样,如果先OnlyOne就一样了

  2. 我怎么会created还是None运行a = OnlyOneTwo(2) print(OnlyOne.created)

我会试一试。我认为症状是由于 __call__.

中的分配 cls.created = ...

当 class objects 首次创建时 OnlyOneTwo.created 指向 OnlyOne.created。它们都具有相同的 id,这与 None.

相同
>>> id(None)
506773144
>>> id(OnlyOne.created), id(OnlyOneTwo.created)
(506773144, 506773144)

如果先创建OnlyOne的实例,实例会被分配给OnlyOne.created(它不再指向 None)但是 OnlyOneTwo.created 仍然指向 OnlyOne.created - 它们仍然是同一件事,因此当调用 OnlyOneTwo 时,执行条件的 else 子句。

>>> a = OnlyOne('a')
called
>>> id(OnlyOne.created), id(OnlyOneTwo.created)
(54522152, 54522152)
>>> z = OnlyOneTwo('z')
>>> id(OnlyOne.created), id(OnlyOneTwo.created)
(54522152, 54522152)
>>> id(a)
54522152

当您首先创建 OnlyOneTwo 的实例时,该实例被分配给 OnlyOneTwo.created,它不再 指向 OnlyOne.createdOnlyOne.created 仍然 指向 None.

>>> id(None)
506773144
>>> id(OnlyOne.created), id(OnlyOneTwo.created)
(506773144, 506773144)
>>> z = OnlyOneTwo('z')
called
>>> id(OnlyOne.created), id(OnlyOneTwo.created)
(506773144, 54837544)
>>> id(z)
54837544

现在,当您创建 OnlyOne 的实例时,if 条件为 True 并且实例被分配给 OnlyOne.created

>>> a = OnlyOne('a')
called
>>> id(OnlyOne.created), id(OnlyOneTwo.created)
(54352752, 54837544)
>>> id(a)
54352752

我经常发现自己re-readingBinding of Names and Resolution of Names - also A Word About Names and Objects and Python Scopes and Namespaces


我感觉我没有真正解释过这个机制——我不是很理解child class属性指向基本class属性——它不同于a = b = None

也许:

  • 最初 child class 实际上 没有 属性, 指向 机制是继承是如何实现的,因为它 没有 属性,所以在其 parent(s).
  • 中搜索该属性
  • 即使 parent class 属性 改变了 实例化, child class 仍然没有 具有 属性并必须搜索它,它找到了 new 东西。
  • 如果 child class 首先实例化元 class 中的赋值 属性赋予 child class - 现在它 它不需要搜索它。

Re-reading 文档中 Standard Type Hierarchy 的自定义 类 部分触发了那个大脑转储。

我觉得我以前来过这里,甚至可能在 SO。希望我这次记住了,不用再想了。


如果你想修复你的单身人士,如果你搜索它们,有很多选择。使用 metaclass 似乎实例保存在 metaclass 中,而不是 class 本身。 Creating a singleton in Python,SO Q&A,是一个好的开始。