本文共 8271 字,大约阅读时间需要 27 分钟。
class Foo: # Foo=元类() pass
cmd = """x=1print('exec函数运行了')def func(self): pass"""class_dic = {}# 执行cmd中的代码,然后把产生的名字丢入class_dic字典中exec(cmd, {}, class_dic)
exec函数运行了
print(class_dic)
{'x': 1, 'func':}
class People: # People=type(...) country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name)
print(type(People))
class_name = 'People' # 类名class_bases = (object, ) # 基类# 类的名称空间class_dic = {}class_body = """country='China'def __init__(self,name,age): self.name=name self.age=agedef eat(self): print('%s is eating' %self.name)"""exec( class_body, {}, class_dic,)
print(class_name)
People
print(class_bases)
(,)
print(class_dic) # 类的名称空间
{'country': 'China', '__init__':, 'eat': }
People1 = type(class_name, class_bases, class_dic)print(People1)
obj1 = People1(1, 2)obj1.eat()
1 is eating
print(People)
obj = People1(1, 2)obj.eat()
1 is eating
class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __init__(self, class_name, class_bases, class_dic): print('self:', self) # 现在是People print('class_name:', class_name) print('class_bases:', class_bases) print('class_dic:', class_dic) super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类type的功能
分析用class自定义类的运行原理(而非元类的的运行原理):
class People(object, metaclass=Mymeta): # People=Mymeta(类名,基类们,类的名称空间) country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name)
self:class_name: Peopleclass_bases: ( ,)class_dic: {'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': , 'eat': }
class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __init__(self, class_name, class_bases, class_dic): if class_dic.get('__doc__') is None or len( class_dic.get('__doc__').strip()) == 0: raise TypeError('类中必须有文档注释,并且文档注释不能为空') if not class_name.istitle(): raise TypeError('类名首字母必须大写') super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类的功能
try: class People(object, metaclass=Mymeta ): #People = Mymeta('People',(object,),{....}) # """这是People类""" country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name)except Exception as e: print(e)
类中必须有文档注释,并且文档注释不能为空
class Foo: def __call__(self, *args, **kwargs): print(args) print(kwargs) print('__call__实现了,实例化对象可以加括号调用了')obj = Foo()obj('nick', age=18)
('nick',){'age': 18}__call__实现了,实例化对象可以加括号调用了
我们之前说类实例化第一个调用的是__init__,但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 __new__ 方法。
__new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
注意:new() 函数只能用于从object继承的新式类。
class A: passclass B(A): def __new__(cls): print("__new__方法被执行") return cls.__new__(cls) def __init__(self): print("__init__方法被执行")b = B()
class Mymeta(type): def __call__(self, *args, **kwargs): print(self) # self是People print(args) # args = ('nick',) print(kwargs) # kwargs = {'age':18} # return 123 # 1. 先造出一个People的空对象,申请内存空间 # __new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。 obj = self.__new__(self) # 虽然和下面同样是People,但是People没有,找到的__new__是父类的 # 2. 为该对空对象初始化独有的属性 self.__init__(obj, *args, **kwargs) # 3. 返回一个初始化好的对象 return obj
class People(object, metaclass=Mymeta): country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name)# 在调用Mymeta的__call__的时候,首先会找自己(如下函数)的,自己的没有才会找父类的# def __new__(cls, *args, **kwargs):# # print(cls) # cls是People# # cls.__new__(cls) # 错误,无限死循环,自己找自己的,会无限递归# obj = super(People, cls).__new__(cls) # 使用父类的,则是去父类中找__new__# return obj
类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的__call__方法控制
分析:调用Pepole的目的
obj = People('nick', age=18)
('nick',){'age': 18}
print(obj.__dict__)
{'name': 'nick', 'age': 18}
结合python继承的实现原理+元类重新看属性的查找应该是什么样子呢???
在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象OldboyTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object
class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 n = 444 def __call__(self, *args, **kwargs): #self=obj = self.__new__(self) self.__init__(obj, *args, **kwargs) return objclass Bar(object): n = 333class Foo(Bar): n = 222class OldboyTeacher(Foo, metaclass=Mymeta): n = 111 school = 'oldboy' def __init__(self, name, age): self.name = name self.age = age def say(self): print('%s says welcome to the oldboy to learn Python' % self.name)print( OldboyTeacher.n) # 自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为OldboyTeacher->Foo->Bar->object->Mymeta->type
111
print(OldboyTeacher.n)
111
查找顺序:
依据上述总结,我们来分析下元类Mymeta中__call__里的self.__new__的查找
class Mymeta(type): n = 444 def __call__(self, *args, **kwargs): #self=obj = self.__new__(self) print(self.__new__ is object.__new__) #Trueclass Bar(object): n = 333 # def __new__(cls, *args, **kwargs): # print('Bar.__new__')class Foo(Bar): n = 222 # def __new__(cls, *args, **kwargs): # print('Foo.__new__')class OldboyTeacher(Foo, metaclass=Mymeta): n = 111 school = 'oldboy' def __init__(self, name, age): self.name = name self.age = age def say(self): print('%s says welcome to the oldboy to learn Python' % self.name) # def __new__(cls, *args, **kwargs): # print('OldboyTeacher.__new__')OldboyTeacher('nick', 18) # 触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
总结,Mymeta下的__call__里的self.__new__在OldboyTeacher、Foo、Bar里都没有找到__new__的情况下,会去找object里的__new__,而object下默认就有一个__new__,所以即便是之前的类均未实现__new__,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找__new__
需求:使用元类修改属性为隐藏属性
class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): # 加上逻辑,控制类Foo的创建 super(Mymeta, self).__init__(class_name, class_bases, class_dic) def __call__(self, *args, **kwargs): # 加上逻辑,控制Foo的调用过程,即Foo对象的产生过程 obj = self.__new__(self) self.__init__(obj, *args, **kwargs) # 修改属性为隐藏属性 obj.__dict__ = { '_%s__%s' % (self.__name__, k): v for k, v in obj.__dict__.items() } return obj
class Foo(object, metaclass=Mymeta): # Foo = Mymeta(...) def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sexobj = Foo('nick', 18, 'male')
print(obj.__dict__)
{'_Foo__name': 'egon', '_Foo__age': 18, '_Foo__sex': 'male'}
转载地址:http://pfgyz.baihongyu.com/