type和isintance
-
type()解决类或者实例是什么,不考虑继承
type(type) <class 'type'> type(object) <class 'type'>
-
isintance考虑继承情况下的类或者实例是什么
isinstance(type, type) True isinstance(type, object) True isinstance(object, object) True isinstance(object, type) True
元类实现单例
class Singleton(type):
_instance = {}
def __new__(cls, *args, **kwargs):
# args ('AAA', (), {'__module__': '__main__', '__qualname__': 'AAA'})
# kwargs {}
obj = super().__new__(cls, *args, **kwargs) # 元类实例即具体类
print("__new__", cls)
return obj
def __init__(cls, *args, **kwargs):
# args ('AAA', (), {'__module__': '__main__', '__qualname__': 'AAA'})
# kwargs {}
super().__init__(*args, **kwargs)
print("__init__", cls)
def __call__(cls, *args, **kwargs): # cls: <class '__main__.AAA'>
if cls not in cls._instance:
instance = super().__call__(*args, **kwargs) # 子类实例 instance: <__main__.AAA object at 0x0459BBD0>
cls._instance[cls] = instance
print("__call__")
return cls._instance[cls]
class AAA(metaclass=Singleton):
pass
class BBB(metaclass=Singleton):
pass
bool(id(AAA()) == id(AAA())) # True
bool(id(BBB()) == id(BBB())) # True
注意点
-
类AAA和类BBB继承元类Singleton,通过字典维护实现单例,因为字典是哈希表数据结构所以key值唯一,也可以避免并发下单例模式失效。
-
元类中__new__ _init_方法参数args值为(‘AAA’, (), {'_module_': ‘_main_’, ‘_qualname_’: ‘AAA’})。即对应
# 类名 基类(object,) 名称空间{} type(class_name, class_bases, class_dict)
-
AAA和BBB还未实例化时,元类就已经执行了。子类实例化后再执行__call__方法
__new__ <class '__main__.AAA'> __init__ <class '__main__.AAA'> __new__ <class '__main__.BBB'> __init__ <class '__main__.BBB'> # 子类实例化后打印日志 __call__ <class '__main__.AAA'> __call__ <class '__main__.AAA'> __call__ <class '__main__.BBB'> __call__ <class '__main__.BBB'>
元类执行顺序
- __new__返回Singleton类对象,即子类
- __init__初始化子类
- __call__子类实例化时AAA(),实际就是调用元类的call方法Singleton()()。使用super调用type的内部call方法实现
-
元类需要继承type类,可以使用type的call等其他内置方法。具体为啥不理解,待后续补充
个人理解:普通类创建内部实现类似于type(class_name,class_bases(父类),class_dict(类属性)。元类就像对type创建类做了一层封装,这样就可以操控类的创建过程实现一定的定制化,所以元类需要继承type -
注意new方法中必须传cls参数
obj = super().__new__(cls, *args, **kwargs)
为什么需要元类
它允许你在运行时动态的创建类。Python 中的类也是对象,因此我们可以通过元类来定义一些通用的类方法、属性或者操作符重载,以便同时在多个类中使用。