zoukankan      html  css  js  c++  java
  • 元类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,"
    ================")
     5         print(class_bases,"
    ================")
     6         print(class_dict,"
    ================")
     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('
    '))==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__': '
        DemoClass 的注释
        ', '__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('
    '))==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('
    '))==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

  • 相关阅读:
    [Quote] Android Graphics Architecture
    New NFC log
    [Quote] 3.6 Namespaces
    NFC DAL(Driver Abstraction Layer) and OSAL(Operating System Abstraction Layer)
    Finance&Invest&Economics URL Links
    Concepts
    Tracking NFC Flow
    NFC log
    [IoE] About AllJoyn™
    [Quote] How does getSystemService() work exactly?
  • 原文地址:https://www.cnblogs.com/zach0812/p/11316483.html
Copyright © 2011-2022 走看看