子类化实现单例思想元类的类

时间:2016-12-15 02:13:00

标签: python inheritance namespaces 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的元类。

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

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

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

输出将是:

called
1
1

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

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

输出将是:

called
called
2
1

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

  1. OnlyOneTwo created属性与OnlyOne'相同吗?因为我通过id()获得了不同的结果,这取决于我先定义的那个。如果它是OnlyOneTwo首先是不同的,但如果OnlyOne首先是created则相同。

  2. 如果我要None运行,a = OnlyOneTwo(2) print(OnlyOne.created)如何仍然是12-15 11:20:37.845 10853-10853/org.androidtown.realchangdeokgunge D/AndroidRuntime: Shutting down VM 12-15 11:20:37.855 10853-10853/org.androidtown.realchangdeokgunge W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x4166bd58) 12-15 11:20:37.855 10853-10853/org.androidtown.realchangdeokgunge E/AndroidRuntime: FATAL EXCEPTION: main Process: org.androidtown.realchangdeokgunge, PID: 10853 java.lang.NullPointerException at org.androidtown.realchangdeokgunge.GPSActivity.getMyLocation(GPSActivity.java:118) at org.androidtown.realchangdeokgunge.GPSActivity.access$000(GPSActivity.java:50) at org.androidtown.realchangdeokgunge.GPSActivity$1.onClick(GPSActivity.java:135) at android.view.View.performClick(View.java:4457) at android.view.View$PerformClick.run(View.java:18501) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5068) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608) at dalvik.system.NativeStart.main(Native Method)

1 个答案:

答案 0 :(得分:1)

我会试一试。我认为症状是由cls.created = ...中的作业__call__引起的。

首次创建类对象时OnlyOneTwo.created 指向 OnlyOne.created。它们都具有相同的id,与None相同。

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

如果您首先创建OnlyOne的实例,则会将实例分配给OnlyOne.created(它不再指向 None),但{{1}仍然指向OnlyOneTwo.created - 它们仍然是相同的,所以当调用OnlyOne.created时,条件的else子句被执行。

OnlyOneTwo

当您首先创建>>> 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.createdOnlyOne.created仍然指向 OnlyOne.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 的实例时,if条件为True且实例已分配给OnlyOne

OnlyOne.created

我经常发现自己正在重读Binding of NamesResolution of Names - 同时A Word About Names and ObjectsPython Scopes and Namespaces

我觉得我还没有真正解释这个机制 - 我真的不明白孩子类属性 指向 < / strong>基类属性 - 它与>>> a = OnlyOne('a') called >>> id(OnlyOne.created), id(OnlyOneTwo.created) (54352752, 54837544) >>> id(a) 54352752 不同。

也许:

  • 最初,子类实际上没有属性,指向机制是如何实现继承,因为它不是 em >拥有属性,在其父级中搜索属性。
  • 即使父类属性使用实例化更改,子类仍然没有拥有属性,并且必须搜索它并找到< em> new thing。
  • 如果实例化子类,则元类中的赋值将属性赋予子类 - 现在它它并且它不必寻找它。

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

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

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