目录
- 元类type介绍
- 使用元类创建类
- 直接使用type
- 继承type
- 类和对象的创建过程
- 元类有什么用
- 使用元类实现一个单例模式
元类type介绍
我们知道在python中一切皆对象,即使是类,也是对象,那么类这个对象是的类是谁呢?那就是元类。
通过 type()
和 obj.__class__
可查看对象的元类:
# 元类
class A:
pass
if __name__ == "__main__":
a = A()
# 1.查看自定义对象的元类
print(type(a)) # <class '__main__.A'>
# 2.查看类A的元类
print(type(A)) # <class 'type'>
# 3.python一些内置类型的元类
print(type(str)) # <class 'type'>
print(type(int)) # <class 'type'>
print(type(float)) # <class 'type'>
# 4.甚至type类的元类还是自己
print(type(type)) # <class 'type'>
从上面结果可以看出:
- 普通对象的元类可直接查看是创建对象的类
- 自己创建的一些类的元类是type
- python内置的一些基本类型的元类是type
- type自己的元类也是type
所以可以说:所有的类终归都是由 type 实例化得来的,它就是最元始的,同时它自己也是一个类,所以也可以称之为元类(Metaclass)。
实例、类、元类之间的关系如图所示:
使用元类创建类
既然前面说了所有的类终究都是由元类创建的,那么下面看看,除了我们经常使用class
关键字来快速创建类,如何使用元类来创建类。
通常有以下两种方法
直接使用type
type源码如下
class type(object):
"""
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
"""
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
pass
创建type对象一般有3个参数:
- name:新类的名称;
- bases:以元组的形式,声明父类;
- dict:以字典的形式声明类的属性;
示例如下:
# 元类
if __name__ == "__main__":
# 通过type创建类A
A = type("A", (), {})
# 创建类A的实例a
a = A()
print(type(a)) # <class '__main__.A'>
print(type(A)) # <class 'type'>
print(A.__bases__) # (<class 'object'>,)
从结果可以看出我们使用type成功创建一个类A。
虽然没有给 bases 赋值,但是 Python 中所有的类都是以 object 为基类,所以查看类 Foo1 的父类,会得到这样一个结果。PS:__bases__查看父类。
通过type创建带属性的类:
注意属性是类属性
if __name__ == "__main__":
User = type("User", (), {"name":"user"})
my_obj = User()
print(my_obj)
# my_obj实例有属性
print(my_obj.name)
通过type创建带方法的类
def say(self):
return "i am user"
# return self.name
if __name__ == "__main__":
User = type("User", (), {"name":"user", "say":say})
my_obj = User()
print(my_obj)
print(my_obj.say())
通过type创建带父类的类
def say(self):
return "i am user"
# return self.name
class BaseClass:
def answer(self):
return "i am baseclass"
if __name__ == "__main__":
User = type("User", (BaseClass, ), {"name":"user", "say":say})
my_obj = User()
print(my_obj)
print(my_obj.answer())
继承type
如果一个类继承了type类,那么这个类也是元类,使用metaclass
可以指定某个类的元类是哪个。来这种方式使用的较多。
下面是一个简单的示例:
class MetaClass(type):
pass
class User(metaclass=MetaClass):
pass
if __name__ == "__main__":
my_obj = User()
print(my_obj)
类 MetaClass
继承了 type
,就可以称为元类,而 User
类与之前定义的类的不同之处就在于,类名后括号内添加了参数 metaclass
来指定元类(即指定使用哪个元类来创建当前类,默认是type),这样就利用了元类 MetaClass
创建了类 User
。
类和对象的创建过程
上面知道了如何去定义一个元类,下面看看,元类是如何创建一个类的。
看一个下面的例子:
class MetaClass(type):
def __new__(cls, *args, **kwargs):
print("new of MetaClass")
return super(MetaClass, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print("init of MetaClass")
super(MetaClass, self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
print("call of MetaClass")
super(MetaClass, self).__call__( *args, **kwargs)
class User(metaclass=MetaClass):
pass
直接运行文件:打印如下:
new of MetaClass
init of MetaClass
可以看到与对象的实例化过程很像。
当使用class关键字定义一个类时,其实是先去寻找这个类的元类,找到元类后,先调用元类的__new__
方法实例化一个类对象,然后调用__init__
去初始化这个类对象。
另外:
创建类时,会先检查当前类是否有元类,没有再找父类是否有,没有再去模块中找,再没有再使用type
创建类。
然后我使用User类来创建一个对象:
class MetaClass(type):
def __new__(cls, *args, **kwargs):
print("new of MetaClass")
return super(MetaClass, cls).__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print("init of MetaClass")
super(MetaClass, self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
print("call of MetaClass")
super(MetaClass, self).__call__(self, *args, **kwargs)
class User(metaclass=MetaClass):
pass
if __name__ == "__main__":
print("--- 开始创建User对象")
my_obj = User()
运行打印结果如下:
new of MetaClass
init of MetaClass
--- 开始创建User对象
call of MetaClass
在使用元类的__new__
方法和__init__
去创建完类后,实例化这个类时,会调用元类的__call__
方法,这个方法会返回实例化后的对象。
其实在实例化对象时,这个元类的__call__
方法会自己调类的__new__
方法和__init__
去创建并初始化对象,然后返回。
看下面的示例:
class MetaClass(type):
def __new__(cls, *args, **kwargs):
print("new of MetaClass")
return super().__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
print("init of MetaClass")
super().__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
print("call of MetaClass")
return super().__call__(*args, **kwargs)
class User(metaclass=MetaClass):
def __new__(cls, *args, **kwargs):
print("new in User")
return super().__new__(cls)
def __init__(self, name, age):
print("init in User")
self.name = name
self.age = age
if __name__ == "__main__":
print("--- 开始创建User对象")
my_obj = User("a", 10)
print(my_obj)
打印结果如下:
new of MetaClass
init of MetaClass
--- 开始创建User对象
call of MetaClass
new in User
init in User
<__main__.User object at 0x7f9b58056d30>
可以很清楚的看到从创建类到类的实例化的过程,可总结如下:
- 创建类:调用原元类的
__new__
方法和__init__
方法创建了一个类; - 创建对象:调用了原类的
__call__
方法; - 创建对象:
__call__
方法调类的__new__
方法和__init__
去创建并初始化对象,然后返回。
元类有什么用
一般我们不会自己定义元类,但是会再一些框架只能怪经常见到。
其主要作用就是在定义一些类时有一些限制或特殊要求。
比如我想限制一个测试类必须以Test开头:
class TestType(type):
def __new__(cls, name, bases, attrs):
if not name.startswith("Test"):
raise AttributeError("类名请以Test开头")
return super().__new__(cls, name, bases, attrs)
class TestUser(metaclass=TestType):
pass
if __name__ == "__main__":
print("--- 开始创建对象")
test_obj = TestUser()
如果我定义了类不以Test开头就会报错
class TestType(type):
def __new__(cls, name, bases, attrs):
if not name.startswith("Test"):
raise AttributeError("类名请以Test开头")
return super().__new__(cls, name, bases, attrs)
class User(metaclass=TestType):
pass
if __name__ == "__main__":
print("--- 开始创建对象")
test_obj = User()
# AttributeError: 类名请以Test开头
使用元类实现一个单例模式
所谓的单例模式,就是指一个类不论我实例化多少次,都指返回同一个对象。主要用在一些公用资源、文件对象等。比如我们的数据库连接池。
示例如下:
class Meta(type):
__instance = None
def __call__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = type.__call__(cls, *args, **kwargs)
return cls.__instance
class DbPool(metaclass=Meta):
pass
if __name__ == "__main__":
x = DbPool()
y = DbPool()
print(x)
print(y)
print(x==y)
# <__main__.DbPool object at 0x7fc210182e20>
# <__main__.DbPool object at 0x7fc210182e20>
# True
上面主要是让我们的DbPool类使用我们自定义的元类Meta。然后重写了Meta例的__call__
方法。为什么呢?回一下前面讲的,在实例化对象时,会调用元类的__call__
方法返回实例化后的对象,所以为了返回同一个对象,可以在__call__
方法里返回同一个即可。
参考:
https://www.cnblogs.com/XiaoYang-sir/p/16524775.html
https://blog.csdn.net/weixin_43988680/article/details/123903473