元类type

内置的元类:type ,它是所有类对象的类!

一个类对象没有声明它的元类,那么它的默认元类就是type .

 1 class DemoClass():
 2     def __init__(self,name):
 3         self.name = name
 4 
 5 if __name__ == "__main__":
 6     demo  = DemoClass("tom")
 7     print(type(demo))
 8     print(type(DemoClass))
 9 '''
10 输出:
11 <class '__main__.DemoClass'>
12 <class 'type'>  #类对象的类型都是type
13 '''

我们能够通过继承type 来自定义元类,然后其他类对象创建的时候就可以用metaclass 来指定自定义元类。

类对象的创建流程:(用自定义元类创建类对象)

 1 class Mymeta(type):  #自定义元类
 2     def __init__(self,class_name,class_bases,class_dict):
 3         super().__init__(class_name,class_bases,class_dict) #重用父类的init
 4         print(class_name,"\n================")
 5         print(class_bases,"\n================")
 6         print(class_dict,"\n================")
 7 
 8         if class_name.islower():
 9             raise TypeError("类对象名{}请修改为驼峰式".format(class_name))
10 
11         if '__doc__' not in class_dict or len(class_dict['__doc__'].strip('\n'))==0:
12             raise TypeError("类中要有注释,且注释不能为空")
13 #类对象的创建
14 #DemoClass = Mymeta(name,(object,),{})
15 class DemoClass(metaclass=Mymeta):  #这时类对象DemoClass 的类就不是type而是Mymeta了
16     '''
17     DemoClass 的注释
18     '''
19     def __init__(self,name):
20         self.name = name
21 
22 '''
23     输出:
24     DemoClass 
25     ================
26     () 
27     ================
28     {'__module__': '__main__', '__init__': <function DemoClass.__init__ at 0x00000144C9943B70>, '__doc__': '\n    DemoClass 的注释\n    ', '__qualname__': 'DemoClass'} 
29     ================
30 '''

我们知道 类对象+() 会触发元类中的__call__()方法,

 1 class Mymeta(type):  #自定义元类
 2     def __init__(self,class_name,class_bases,class_dict):
 3         super().__init__(class_name,class_bases,class_dict) #重用父类的init
 4 
 5         if class_name.islower():
 6             raise TypeError("类对象名{}请修改为驼峰式".format(class_name))
 7 
 8         if '__doc__' not in class_dict or len(class_dict['__doc__'].strip('\n'))==0:
 9             raise TypeError("类中要有注释,且注释不能为空")
10     def __call__(self, *args, **kwargs):
11         print(args,kwargs)
12 
13 
14 #类对象的创建
15 #DemoClass = Mymeta(name,(object,),{})
16 class DemoClass(metaclass=Mymeta):  #这时类对象DemoClass 的类就不是type而是Mymeta了
17     '''
18     DemoClass 的注释
19     '''
20     def __init__(self,name):
21         self.name = name
22 if __name__ == "__main__":
23     DemoClass("tom") #DemoClass 其实是个类对象,然后它加() 会调用__call__
24 
25 '''
26     输出:
27     ('tom',) {}
28 '''

默认的类对象 实例化的过程会做三件事:

1、产生一个空对象obj

2、调用__init__方法初始化对象obj

3、返回初始化好的obj

所以,我们也自己加上这三件事:

 1 class Mymeta(type):  #自定义元类
 2     def __init__(self,class_name,class_bases,class_dict):
 3         super().__init__(class_name,class_bases,class_dict) #重用父类的init
 4         print("1")
 5         if class_name.islower():
 6             raise TypeError("类对象名{}请修改为驼峰式".format(class_name))
 7 
 8         if '__doc__' not in class_dict or len(class_dict['__doc__'].strip('\n'))==0:
 9             raise TypeError("类中要有注释,且注释不能为空")
10     #产生类对象之后,加括号会调__call__
11     def __call__(self, *args, **kwargs):
12         print(id(self))
13         # 1,产生空对象
14         obj = self.__new__(self)  #self是类对象DemoClass 不是demo ,obj 才是demo
15         # 2,调用类对象的init
16 
17         print("2")
18         self.__init__(obj,*args,**kwargs)
19         print("4")
20         print(args,kwargs)
21         #,3 返回obj
22         return obj
23 
24 #类对象的创建
25 #DemoClass = Mymeta(name,(object,),{})
26 class DemoClass(metaclass=Mymeta):  #这时类对象DemoClass 的类就不是type而是Mymeta了
27     '''
28     DemoClass 的注释
29     '''
30 
31     def __init__(self,name):  #__init__() 是类对象的属性,所以在元类__call__中,self才能调它.
32         print("3")
33         self.name = name
34 if __name__ == "__main__":
35     demo =  DemoClass("tom") #DemoClass 其实是个类对象,然后它加() 会调用__call__
36     print(id(DemoClass))
37 '''
38     输出:
39     1
40     1817911787480
41     2
42     3
43     4
44     ('tom',) {}
45     1817911787480
46 '''

所以,demo = DemoClass("tom") 这个过程不是自动的调init() 而是先调__call__() 然后,在__call__中会回头调的init() 。

属性查找:

 1 class Mymeta(type):  #只有继承type 才是自定义元类,否则是普通的类
 2     n = 100
 3     def __call__(self, *args, **kwargs):
 4         obj = self.__new__(self)
 5         self.__init__(obj,*args,**kwargs)
 6         return obj
 7 
 8 class Demo1(object):  #继承object
 9     n  =200
10 
11 class Demo2(Demo1):
12     n = 300
13 
14 class Demo3(Demo2,metaclass=Mymeta):
15     n = 400
16 
17     def __init__(self,name):
18         self.name = name
19 print(Demo3.n)
20 #查找n 的顺序:
21 #先类对象层 :Demo3 ->Demo2-->Demo1-->object
22 #再元类层: Mymeta -->type 
23 
24 
25 # 注:object 也是个类对象
26 print(type(object))
27 #<class 'type'>

下面是产生obj的方法  类对象的方法__new__ 的查找:

 1 class Mymeta(type):  #只有继承type 才是自定义元类,否则是普通的类
 2     n = 100
 3     def __call__(self, *args, **kwargs):
 4         obj = self.__new__(self)
 5         print(self.__new__ is object.__new__)
 6 
 7 class Demo1(object):  #继承object
 8     n  =200
 9     # def __new__(cls, *args, **kwargs):
10     #     print("Demo1_new")
11 class Demo2(Demo1):
12     n = 300
13     # def __new__(cls, *args, **kwargs):
14     #     print("Demo1_new")
15 class Demo3(Demo2,metaclass=Mymeta):
16     n = 400
17 
18     def __init__(self,name):
19         self.name = name
20 
21 Demo3("tom")
22 '''
23     输出:True
24 '''

结论:Mymeta下的__call__里的self.__new__在Demo3、Demo2、Demo1里都没有找到__new__的情况下,会去找object里的__new__,而object下默认就有一个__new__,所以即便是之前的类均未实现__new__,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找__new__

在元类的__call__中也可以用object.__new__(self)去造对象

但还是推荐在__call__中使用self.__new__(self)去创造空对象,因为这种方式会检索三个类Demo3->Demo2->Demo1,而object.__new__则是直接跨过了他们三个

 补:

 1 class Mymeta(type):  #只有继承type 才是自定义元类,否则是普通的类
 2     n = 100
 3     def __new__(cls, *args, **kwargs):
 4         obj = type.__new__(cls,*args,**kwargs) #必须按照这样传值 cls 为类对象
 5         print(obj.__dict__)
 6         #return obj #只有返回obj 才会调下面的init 
 7         return 123
 8     def __init__(self,class_name,class_bases,class_dict):
 9         print("run....")
10 #Demo = Mymeta()
11 class Demo(object,metaclass=Mymeta):
12     n  = 200
13 
14     def __init__(self,name):
15         self.name = name
16 #产生类对象Demo 的过程就是在调Mymeta ,而Mymeta 也是type类的一个对象,
17 #那么Mymeta 之所以可以调用,一定是因为元类type中有一个__call__方法
# class type:
#     def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
#         obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象
#         self.__init__(obj,*args,**kwargs) 
#         return obj
 1 class Mymeta(type):  #只有继承type 才是自定义元类,否则是普通的类
 2     n = 100
 3     def __new__(cls, *args, **kwargs):
 4         obj = type.__new__(cls,*args,**kwargs) #cls 是Mymeta这个类对象
 5         print(obj.__dict__)
 6         return obj
 7         
 8     def __init__(self,class_name,class_bases,class_dict):
 9         print("run....")
10 
11 #把自定义的数据属性都变成大写
12 class Demo(object,metaclass=Mymeta):
13     n  = 200
14     def __init__(self,name):
15         self.name = name
16 
17 demo  = Demo("tom")  #这会找到type 中的__call__方法
18 '''
19 type 中的方法应该是:
20 # class type:
21 #     def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
22 #         obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象
23 #         self.__init__(obj,*args,**kwargs) 
24 #         return obj
25 '''
26 print(demo.n)

 练习1:将类属性变为全大写:

 1 class Mymeta(type):  #只有继承type 才是自定义元类,否则是普通的类
 2     def __new__(cls, class_name,class_bases,class_dict):  #type中的__call__ 会调它
 3         update_attrs = {}
 4         for k,v in class_dict.items():
 5             if not callable(v) and not k.startswith('__'):
 6                 update_attrs[k.upper()]=v
 7             else:
 8                 update_attrs[k] =v
 9         return type.__new__(cls,class_name,class_bases,update_attrs)#生成的Demo对象
10 
11 #把类中的数据属性都变成大写
12 class Demo(object,metaclass=Mymeta):
13     name = "tom"
14     age = 18
15     def test(self):
16         pass
17 
18 print(Demo.__dict__)
19 '''
20 输出:{'__weakref__': <attribute '__weakref__' of 'Demo' objects>, '__module__': '__main__', 'AGE': 18, 'test': <function Demo.test at 0x000002C156163B70>, '__doc__': None, 'NAME': 'tom', '__dict__': <attribute '__dict__' of 'Demo' objects>}
21 '''

练习二:类中无需__init__方法:

 1 class Mymeta(type):  #只有继承type 才是自定义元类,否则是普通的类
 2     def __call__(self, *args, **kwargs):
 3         if args:
 4             raise TypeError("必须以键值对传参")
 5         obj = object.__new__(self)
 6 
 7         for k,v in kwargs.items():
 8             obj.__dict__[k.upper()] = v
 9         return obj
10 
11 class DemoClass(metaclass=Mymeta):
12     salary = 1000
13     def test(self):
14         pass
15 
16 
17 p = DemoClass(name = "tom",age = 18)
18 print(p.__dict__)
19 print(DemoClass.__dict__)
20 '''
21 {'AGE': 18, 'NAME': 'tom'}
22 {'salary': 1000, '__module__': '__main__', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'DemoClass' objects>, '__dict__': <attribute '__dict__' of 'DemoClass' objects>, 'test': <function DemoClass.test at 0x000002B2B3A03BF8>}
23 '''
 1 class Mymeta(type):  #只有继承type 才是自定义元类,否则是普通的类
 2     def __call__(self, *args, **kwargs):
 3         print(args,kwargs)
 4         if args:
 5             raise TypeError("必须以键值对传参")
 6         obj = object.__new__(self)
 7 
 8         for k,v in kwargs.items():
 9             obj.__dict__[k.upper()] = v
10         return obj
11 
12 class DemoClass(object,metaclass=Mymeta):
13     salary = 1000
14     def test(self):
15         pass
16 p = DemoClass(18,name = "tom",age = 18,)
17 print(p.__dict__)
18 print(DemoClass.__dict__)
19 '''
20 Traceback (most recent call last):
21 (18,) {'name': 'tom', 'age': 18}
22   File "C:/Users/Administrator/Desktop/test/m1/testtest.py", line 16, in <module>
23     p = DemoClass(18,name = "tom",age = 18,)
24   File "C:/Users/Administrator/Desktop/test/m1/testtest.py", line 5, in __call__
25     raise TypeError("必须以键值对传参")
26 TypeError: 必须以键值对传参
27 '''

三:属性设置为隐藏属性:

 1 class Mymeta(type):
 2     def __call__(self, *args, **kwargs):
 3         obj = self.__new__(self)
 4         self.__init__(obj,*args,**kwargs)
 5         obj.__dict__ ={"_{}__{}".format(self.__name__,k):v for k,v in obj.__dict__.items() }
 6         return obj
 7 
 8 class DemoClass(object,metaclass=Mymeta):
 9     def __init__(self,name,age,sex):
10         self.name = name
11         self.age= age
12         self.sex = sex
13 
14 
15 obj=DemoClass('tom',18,'male')
16 print(obj.__dict__)
17 setattr(obj,"salary",15054)
18 print(obj.__dict__)
19 '''
20 {'_DemoClass__age': 18, '_DemoClass__sex': 'male', '_DemoClass__name': 'tom'}
21 {'_DemoClass__age': 18, '_DemoClass__sex': 'male', '_DemoClass__name': 'tom', 'salary': 15054}
22 '''

参考博客:https://www.cnblogs.com/linhaifeng/articles/8029564.html

猜你喜欢

转载自www.cnblogs.com/zach0812/p/11316483.html