【问题标题】:Subclassing a class that implements singleton thought metaclass子类化实现单例思想元类的类
【发布时间】:2017-04-30 12:48:29
【问题描述】:

我有这个代码:

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 的元类。

在这里,我尝试创建一个元类,在设置时将类限制为单个实例。

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

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

输出将是:

called
1
1

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

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

输出将是:

called
called
2
1

有人可以帮我追溯吗?我有点困惑,但这是我最初的问题/想法:

  1. OnlyOneTwocreated 属性与OnlyOne 的属性相同吗?因为我通过id() 得到不同的结果,具体取决于我首先定义的结果。如果是OnlyOneTwo,则不同,但如果是OnlyOne,则相同。

  2. 如果我要运行a = OnlyOneTwo(2) print(OnlyOne.created),为什么created 仍然是None

【问题讨论】:

  • 可以先添加print('__new__\tcls:{}\n\tname:{}\n\tbase:{}\n\tattr:{}'.format(cls,name,base,attr)) 作为__new__ 的第一行。

标签: python inheritance namespaces metaclass


【解决方案1】:

我会试一试。我认为症状是由于分配,cls.created = ... in __call__

第一次创建类对象时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

我经常发现自己重读 Binding of NamesResolution of Names - 还有 A Word About Names and ObjectsPython Scopes and Namespaces


我觉得我还没有真正解释过机制——我不太明白child类属性如何指向基类属性 - 它与 a = b = None 不同。

也许:

  • 最初子类实际上并没有这个属性,指向机制是继承的实现方式,因为它没有 em> 属性,在其父级中搜索该属性。
  • 即使父类属性改变实例化,子类仍然没有属性,必须搜索它并找到新事物
  • 如果首先实例化子类,则元类中的赋值属性赋予子类 - 现在它拥有它并且不必搜索它。

重新阅读文档中Standard Type Hierarchy 的自定义类部分触发了大脑转储。

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


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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    相关资源
    最近更新 更多