最近刚好在准备面试,然后复习知识点。看了好多元类相关的文章,但还是心存疑惑🤔。
还是写点东西吧!
元类,官方的定义是,类的类型。而类型的顶点,便是type
- metaclass - The class of a class. Class definitions create a class name, a class dictionary, and a list of base classes. The metaclass is responsible for taking those three arguments and creating the class. Most object oriented programming languages provide a default implementation. What makes Python special is that it is possible to create custom metaclasses. Most users never need this tool, but when the need arises, metaclasses can provide powerful, elegant solutions. They have been used for logging attribute access, adding thread-safety, tracking object creation, implementing singletons, and many other tasks.
上面的内容大致写的是:元类,是类的类型。然后定义了name(类的名称),dict(可用于传入一些内置参数),bases(包含所有父类的列表)。然后猿类可以根据些参数创建一个类。然后就是一些元类的用途,用于记录属性的获取、确保线程安全、跟踪对象的创建、实现单例模式,等等。
但只是这么说,我还是没理解他是个啥,然后整了些小实验:效果如下:
object.base
type.base
<class ‘object’>
type.class
<class ‘type’>
object.class
<class ‘type’>
先看父子关系,我们可以看到:
object没有父类,而type的父类是object。
因此,我们可以得出object是父类的顶点。
再看类型,同理:
object的类型是type,type的类型也是type。
因此,type是类型的顶点。但是,为什么object的类型也是type呢。所以我们的结论是,type是基于object实现的,而基于object实现的type实现的type被定义为了类型的顶点。
下面我们来看看元类的实际用途:
1、动态且快速地创建一个类
>>> t = type("test",(),{"a":0})
>>> t.a
0
>>> t
<class '__main__.test'>
2、追踪对象的创建(当然,在追踪的同时,我们也可以做一些事情,比如对属性的大小写做限制)。
class UpperAttrMetaclass(type):def __new__(cls, name, bases, dct):uppercase_attrs = {}for attr_name, attr_value in dct.items():if not attr_name.startswith('__'):uppercase_attrs[attr_name.upper()] = attr_valueelse:uppercase_attrs[attr_name] = attr_value# 使用修改后的属性创建新的类return super().__new__(cls, name, bases, uppercase_attrs)# 使用 UpperAttrMetaclass 元类创建一个新类
class MyClass(metaclass=UpperAttrMetaclass):my_attr = "hello"# 测试 MyClass 中的属性名是否已转换为大写
obj = MyClass()
print(obj.MY_ATTR) # 输出: hello
经过测试发现,new__函数的的cls对象变量创建会经过以下逻辑:
1、当前类有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为当前类的main函数类对象)
2、如果Python没有找到__metaclass,它会继续在父类中寻找__metaclass__属性,并尝试做和前面同样的操作
3、如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作
4、如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。
这也就是为什么,我们在用type不传参时,与不传metaclass参数时会得到类似的报错信息。判断type类的__call__逻辑是三个参数时,调用__new__函数创建实例。这也就符合了,我们上方的逻辑,type所有类型的顶点,所有的实例都是基于type的实例创建的。
3、实现单例模式(本质上利用了闭包思想)
class Singleton(type):_instances = {}def __call__(cls, *args, **kwargs):if cls not in cls._instances:cls._instances[cls] = super().__call__(*args, **kwargs)return cls._instances[cls]class MyClass(metaclass=Singleton):passobj1 = MyClass()
obj2 = MyClass()
print(obj1 is obj2) # 输出: True
先写到这里,后面有空再写。。。