输出结果:
True代码中比较巧妙的一点是:
_instance = {}使用不可变的类地址作为键,其实例作为值,每次创造实例时,首先查看该类是否存在实例,存在的话直接返回该实例即可,否则新建一个实例并存放在字典中。
使用 new 方法在创造实例时进行干预,达到实现单例模式的目的。
class Single(object): _instance = None def __new__(cls, *args, **kw): if cls._instance is None: cls._instance = object.__new__(cls, *args, **kw) return cls._instance def __init__(self): pass single1 = Single() single2 = Single() print(id(single1) == id(single2))globals():返回一个字典,变量名是key,指向是值。因此,程序中的变量名不可以重复 __builtin__
x = globals() type(x) # dict x['__builtin__'] # <module 'builtins' (built-in) > # 查看模块里面有什么方法 # 模块.__dict__ x['__builtin__'].__dict__程序一运行,红色部分会被加载,要用到的会现在这里面找,没有再去__builtin__里面找。
type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢?好吧,我猜这是为了和str保持一致性,str是用来创建字符串对象的类,而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。
class Test(): num = 100 t = Test() print(t.__class__) print(t.__class__.__class__) # <class '__main__.Test'> # typetype就是Python的内建元类,当然了,你也可以创建自己的元类。
在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的。
较为完整的使用type创建类的方式:
class A(object): num = 100 def print_b(self): print(self.num) @staticmethod def print_static(): print("----haha-----") @classmethod def print_class(cls): print(cls.num) B = type("B", (A,), {"print_b": print_b, "print_static": print_static, "print_class": print_class}) b = B() b.print_b() b.print_static() b.print_class() # 结果 # 100 # ----haha----- # 100python解释器碰到类的时候,就用类定义里面的值,传给type创建类对象了。
元类的主要目的就是为了当创建类时能够自动地改变类。
假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定__metaclass__。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。
幸运的是,__metaclass__实际上可以被任意调用,它并不需要是一个正式的类。所以,我们这里就先以一个简单的函数作为例子开始。
python3中
#-*- coding:utf-8 -*- def upper_attr(class_name, class_parents, class_attr): #遍历属性字典,把不是__开头的属性名字变为大写 new_attr = {} for name,value in class_attr.items(): if not name.startswith("__"): new_attr[name.upper()] = value #调用type来创建一个类 return type(class_name, class_parents, new_attr) class Foo(object, metaclass=upper_attr): bar = 'bip' print(hasattr(Foo, 'bar')) print(hasattr(Foo, 'BAR')) f = Foo() print(f.BAR)现在让我们再做一次,这一次用一个真正的class来当做元类。
#coding=utf-8 class UpperAttrMetaClass(type): # __new__ 是在__init__之前被调用的特殊方法 # __new__是用来创建对象并返回之的方法 # 而__init__只是用来将传入的参数初始化给对象 # 你很少用到__new__,除非你希望能够控制对象的创建 # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__ # 如果你希望的话,你也可以在__init__中做些事情 # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用 def __new__(cls, class_name, class_parents, class_attr): # 遍历属性字典,把不是__开头的属性名字变为大写 new_attr = {} for name, value in class_attr.items(): if not name.startswith("__"): new_attr[name.upper()] = value # 方法1:通过'type'来做类对象的创建 return type(class_name, class_parents, new_attr) # 方法2:复用type.__new__方法 # 这就是基本的OOP编程,没什么魔法 # return type.__new__(cls, class_name, class_parents, new_attr) # python3的用法 class Foo(object, metaclass=UpperAttrMetaClass): bar = 'bip' # python2的用法 # class Foo(object): # __metaclass__ = UpperAttrMetaClass # bar = 'bip' print(hasattr(Foo, 'bar')) # 输出: False print(hasattr(Foo, 'BAR')) # 输出:True f = Foo() print(f.BAR) # 输出:'bip'就是这样,除此之外,关于元类真的没有别的可说的了。但就元类本身而言,它们其实是很简单的:
拦截类的创建修改类返回修改之后的类同样,我们在类的创建时进行干预,从而达到实现单例的目的。
在实现单例之前,需要了解使用 type 创造类的方法,代码如下:
def func(self): print("do sth") Klass = type("Klass", (), {"func": func}) c = Klass() c.func()以上,我们使用 type 创造了一个类出来。这里的知识是 mataclass 实现单例的基础。
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class Cls4(metaclass=Singleton): pass cls1 = Cls4() cls2 = Cls4() print(id(cls1) == id(cls2))这里,我们将 metaclass 指向 Singleton 类,让 Singleton 中的 type 来创造新的 Cls4 实例。
