Python面向对象之八:元类
一、概述
1 、元类的定义
在python中,对象是由类实例化而来,那么按照万事万物皆对象的理念,类也是对象,那它是怎么来的呢?
答案是:元类,元类就是创建类的类。
在Python当中万物皆对象,我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类,元类可以简称为类的类,元类的主要目的是为了控制类的创建行为。
2、查看元类的类型
class Person:
pass
class Mother(Person):
pass
mother = Mother()
print(type(mother)) #<class '__main__.Mother'>
print(type(Mother)) #<class 'type'>
print(type(Person)) #<class 'type'>
分析:
1、对象的类型是:<class ‘main.Mother’>,说明它是由Mother类实例化而来;
2、类的类型是:<class ‘type’>,说明它是由type这个元类而实例化得来的。
3、关于type
1、type是Python的一个内建元类,用来直接控制生成类,在python当中任何class定义的类其实都是type类实例化的结果。
2、只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类,自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程。
3、Python中所有的东西,注意,我是指所有的东西都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。
二、类的创建
1、通过class关键字创建
class Person(object):
country = 'China'
def __init__(self,name,age):
self.name = name
self.age = age
def tell(self):
print(f'{self.name}的年龄是:{self.age}')
2、通过type关键字创建
country = 'China'
def __init__(self,name,age):
self.name = name
self.age = age
def tell(self):
print(f'{self.name}的年龄是:{self.age}')
Person = type('Person',(object,),{'country':country,
'__init__':__init__,
'tell':tell})
print(type(Person)) #<class 'type'>
per = Person('晴朗', 18)
print(type(per)) #<class '__main__.Person'>
分析:Person类实际上就是一个类对象,于是我们将产生类的类称之为元类,默认的元类是type。
3、自定义元类
一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类:
class MyMetaClass(type):
pass
class Person(object, metaclass=MyMetaClass):
country = 'China'
def __init__(self,name,age):
self.name = name
self.age = age
def tell(self):
print(f'{self.name}的年龄是:{self.age}')
print(type(Person)) #<class '__main__.MyMetaClass'>
per = Person('晴朗', 18)
print(type(per)) #<class '__main__.Person'>
print(per.__dict__) #{'name': '晴朗', 'age': 18}
分析:自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即:
class MyMetaClass(type):
pass
country = 'China'
def __init__(self,name,age):
self.name = name
self.age = age
def tell(self):
print(f'{self.name}的年龄是:{self.age}')
Person = MyMetaClass('Person',(object,),{'country':country,
'__init__':__init__,
'tell':tell})
print(type(Person)) #<class 'type'>
per = Person('晴朗', 18)
print(type(per)) #<class '__main__.Person'>
print(per.__dict__) #{'name': '晴朗', 'age': 18}
三、元类的使用
1、控制类的创建
class Mymeta(type): # 只有继承了type类的类才是自定义的元类
def __init__(self, class_name, class_bases, class_dic):
print(self)
print(class_name)
print(class_bases)
print(class_dic)
# 产生的新类必须至少有一个父类
if len(class_bases) == 0:
raise BaseException("至少继承一个父类")
# 产生的新类必须有注释文档
doc = class_dic.get('__doc__')
if not (doc and len(doc.strip()) > 0):
raise BaseException("必须要有文件注释,并且注释内容不为空")
class Person(object, metaclass=Mymeta):
'''
这里是注释文档
'''
pass
2、控制类的实例化
2.1、_ call _方法
class Foo:
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
obj = Foo()
res = obj(1, 2, 3, x=1, y=2) #只有写了这行代码,__call__才会运行
分析:调用一个对象去执行,就是触发对象所在类中的__call__方法的执行。
2.2 _ new _方法
class Student(object):
def __new__(cls, *args, **kwargs):
print('---------new--------------')
obj = super().__new__(cls)
return obj
def __init__(self, name, age):
print('---------init--------------')
self.name = name
self.age = age
student = Student('wtt', 30)
#运行结果:
# ---------new--------------
# ---------init--------------
分析:创建一个对象,首先会运行__new__方法创建一个空对象,然后再运行__init__方法装备名称空间。
2.3控制类的实例化
1、实例化一个对象会做三件事:产生一个空对象obj、调用__init__方法初始化对象obj、返回初始化好的obj,对应的__call__方法也会做这三件事:
分析:__call__方法相当于一个模板,我们可以在该基础上改写__call__的逻辑从而控制调用类的过程
2、利用改写__call__方法将类模板中用于实例化的属性全部变成私有属性:
class Person(type):
def __call__(self, *args, **kwargs):
# 1、先创建一个空对象
obj = object.__new__(self)
# 2、调用类内的__init__函数,然后将空对象连同括号内的参数的参数一同传给__init__
self.__init__(obj, *args, **kwargs)
obj.__dict__ = {"_%s__%s" % (self.__name__, k): v for k, v in obj.__dict__.items()}
# 3、将初始化好的对象返回
return obj
class Teacther(object, metaclass=Person):
def __init__(self, name, age):
self.name = name
self.age = age
def sya(self):
print(f'{self.name}说了句话!')
t1 = Teacther('晴朗', 20)
print(t1.__dict__)
#{'_Teacther__name': '晴朗', '_Teacther__age': 20}
四、元类的冲突
1、假如有两个不同的元类,要生成一个继承这两个类的子类,会产生什么情况呢?
class MetaA(type):
pass
class MetaB(type):
pass
class A(object, metaclass=MetaA):
pass
class B(object, metaclass=MetaB):
pass
class C(A, B):
pass
#TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
分析:这时会报错,派生类的元类必须是其所有基元类的子类,而类C来自于两个继承不同基元类的子类,所以会元类冲突。
2、修改一下以上代码,保证派生类的元类是其所有基元类的子类
class MetaA(type):
pass
class MetaB(type):
pass
class MetaC(MetaA, MetaB): #保证派生类的元类是其所有基元类的子类
pass
class A(object, metaclass=MetaA):
pass
class B(object, metaclass=MetaB):
pass
class C(object, metaclass=MetaC):
pass