Python知识
- 元类
- class机制分析
- 自定义元类
- __new__方法
- __call__方法
元类
Python中有这么一句话,一切皆为对象,那么看下面的代码:
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
obj = People('egon', 18)
obj是通过类People产生的对象,一切皆为对象,那么产生对象obj的类People也是对象,既然所有的对象都是调用类得到的,那么必然类People也是调用了一个类得到的,这个类称为元类。
元类就是实例化产生类的类。
关系:
元类–>(实例化)–>类People–>(实例化)–>对象obj
那元类到底是什么类?平时查看对象的类型都是用type方法查看,同里查看类的类也是使用type
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
obj = People('egon', 18)
print(type(obj))
pirnt(type(People))
'''
<class '__main__.People'>
<class 'type'>
'''
type是内置的元类,用class关键字定义的所有类以及内置的类(例如:int)都是由内置的元类type来实例化产生
class机制分析
类的三大特征:类名,类的基类,执行类体代码拿到类的名称空间
class机制创造类的四个步骤
- 类名
class name ='People'
- 类的基类
class_bases =(object)
- 执行类体代码拿到类的名称空间
class_name = 'People'
class_bases = (object,)
class_dic = {}
class_body = '''
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
'''
exec(class_body, {}, class_dic)
print(class_dic)
'''
{'__init__': <function __init__ at 0x0000020B415AF040>,
'say': <function say at 0x0000020B41A59430>}
'''
- 调用元类
People = type(class_name, class_bases, class_dic)
print(type(People))
'''
<class 'type'>
'''
此时,一个类就创造出来了,并且没有使用class机制。
obj = People('egon',18)
print(obj.__dict__)
'''
{'name': 'egon', 'age': 18}
'''
弄懂这个class机制为的是什么?
前三步都是可以在class机制下修改,而第四步调用元类就不同了,一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类。比如说,就想让类的命名方式是首字母大写,不大写就报错,如果使用内置的元类进行调用,无法进行控制,就需要自定义元类。
自定义元类
如何自定义元类?
调用Mymeta发生三件事:
- 先造一个空对象==》People
- 调用Mymeta这个类内的__init__方法,完成初始化对象的操作
- 返回初始化好的对象
metaclass关键字参数为一个类指定元类
如果直接运行会直接报错:
class Mymeta(type): # 只有继承了type类的类才是元类
def __init__(self):
print('run')
class People(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
'''
TypeError: __init__() takes 1 positional argument but 4 were given
'''
在上述的class机制中,第四步调用元类,People = type(class_name, class_bases, class_dic)
就会调用Mymeta中的init方法,init会传入四个参数,默认传入对象本身class_name,class_bases,class_dic
,所以就显示传入了四个值。
解决方法:
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dic):
if not class_name.istitle():
raise NameError('类名首字母为大写')
class people(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
'''
File "D:\python, line 37, in __init__
raise NameError('类名首字母为大写')
NameError: 类名首字母为大写
'''
__new__方法
在上述提到产生新的对象的三个步骤中,只是对第二步的init方法进行了控制,而__new__方法是在第一步,返回了一个对象给到init方法,init才会完成对象的初始化操作。
何以证明?
没有预先设置__new__方法时,底层会有默认的__new__功能:
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dic):
if not class_name.istitle():
raise NameError('类名首字母为大写')
print('run2')
class People(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
'''
run2
'''
预先设置了__new__方法,但是并没有返回一个对象
class Mymeta(type): # 只有继承了type类的类才是元类
def __init__(self, class_name, class_bases, class_dic):
if not class_name.istitle():
raise NameError('类名首字母为大写')
print('run2')
def __new__(cls, *args, **kwargs):
print('run1')
class People(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
'''
run1
'''
预先设置了__new__方法,也有返回一个对象
lass Mymeta(type):
def __init__(self, class_name, class_bases, class_dic):
if not class_name.istitle():
raise NameError('类名首字母为大写')
print('run2')
def __new__(cls, *args, **kwargs):
print('run1')
return super().__new__(cls, *args, **kwargs) #从type中调用了__new__方法
#return type.__new__(cls, *args, **kwargs) #第二种返回对象的方法
class People(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
'''
run1
run2
'''
__call__方法
通过上述的结果得知,在调用Mymeta时,Mymeta会将参数先传给__new__方法,再传给__init__方法,但是为什么是这种顺序?并且调用Mymeta类的时候的第三步是返回一个已经初始化好的对象,而在第二步__init__方法,并没有返回任何的对象,那么这个初始化好的对象是谁返回的?
__call__的作用:
如果想让对象加括号调用,就可以加入一个call方法
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
def __call__(self, *args, **kwargs):
print(self, args, kwargs)
obj = People('egon', 18)
obj(1, 2, 5, 6, a=3, b=7)
'''
<__main__.People object at 0x000002CAEF800FD0> (1, 2, 5, 6) {'a': 3, 'b': 7}
'''
回头一想,在obj=People('egon',18)
中,People也是对象,也是加了括号调用,所以在People的元类也有__call__方法。
对象()——>类内的__call__
类()——>自定义元类内的__call__
自定义元类()——>内置元类__call__
class Mymeta(type): # 只有继承了type类的类才是元类
def __init__(self, class_name, class_bases, class_dic):
if not class_name.istitle():
raise NameError('类名首字母为大写')
print('run2')
def __new__(cls, *args, **kwargs):
print('run1')
return super().__new__(cls, *args, **kwargs)
def __call__(self, *args, **kwargs):
# 1、Mymeta.__call__函数内调用People内的__new__
people_obj = self.__new__(self)
# 2、Mymeta.__call__函数内调用People内的__init__
self.__init__(people_obj, *args, **kwargs)
# 3、Mymeta.__call__函数内返回一个初始化好的对象
return people_obj
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('<%s:%s>' % (self.name, self.age))
def __new__(cls, *args, **kwargs):
#产生真正的对象
return object.__new__(cls)
obj = People('egon', 18)
print(obj.__dict__)