zoukankan      html  css  js  c++  java
  • 元类介绍

    元类

    什么是元类呢,有句话是Python中一切皆对象。

    class StanfordTeacher(object):
        school = 'Stanford'
        def __init__(self,name,age):
            self.name = name
            self.age = age
            
        def say(self):
            print(f'{self.name says welcome to the Stanford to learn Python}')
    

    所有的对象都是实例化或者说是调用类而得到的(调用类的过程就是类的实例化),比如t1是调用类StanfordTeacher得到的

    t1 = StanfordTeacher('Jerry',18)
    print(type(t1))	# 查看对象t1的类是<class '__main__.StanfordTeacher'>
    

    如果说一切皆为对象,那么类StanfordTeacher本质上也是一个对象,既然所有的对象都是调用类得到的,那么StanfordTeacher必然也是调用了一个类得到的,这个类称为元类

    根据推导,我们可以说 在产生StanfordTeacher的过程中一定发生了:StanfordTeacher = 元类(...)

    print(type(StanfordTeacher))
    # 结果为<class 'type'> 证明是调用了type这个元类而产生的StanfordTeacher,也就是说默认的元类为type
    

    exec的用法

    exec:是一个Python的内置函数,可以将字符串的代码添加到名称空间中

    exec需要三个参数:

    """	参数一:包含一系列Python代码的字符串
    	参数二:全局作用域(字典形式),如果不指定,默认为global()
    	参数三:局部作用域(字典形式),如果不指定,默认为locals()
    可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
    """
    g = {'x': 1, 'y': 2}
    l = {}
    exec("""
    global x, z
    x = 100
    z = 200
    m = 300
    """, g, l)
    print(g)	#{'x':100, 'y':2, 'z':200,......}
    print(l)	#{'m': 300}
    

    练习

    x = 10  # 定义全局变量
    expr = """
    z = 30
    y = 5
    global x    				# 声明全局变量x
    x = 18
    m =10
    
    sum = x + y + z
    print(sum)  # 求和
    """
    func1 = {'x': 1, 'y': 2}    # 自定义全局名称空间
    func2 = {'x': 3, 'y': 4}    # 自定义局部名称空间
    
    
    def func():
        y = 20
        x = 11
        # exec(expr)    		# 此处进行了字符串expr中的计算---> 结果为53
    
        exec(expr, func1, func2)    # 此处又进行了字符串expr中的计算
        print(func1,'111')  	# {'x':18,'y':2}111
        print(func2,'222')  	# {'x':3,'y':5,'z':30,'m':10,'sum':53}222
    	# 注意:全局名称空间中的值x不影响局部,且sum求和的值在上面已经进行了计算
    func()
    

    元类

    说了这么多,类的类就是元类,所有通过关键字class定义的类,都是相当于通过调用type()得到的对象(因为一切皆对象)

    • 元类有什么作用呢

      • 定义类,让我们更好的理解类的产生和类的底层原理
      • 元类可以控制类的创建过程
    • 怎么创建元类

      • 通过class定义类
      class Chiness:
          country = 'China'
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def speak(self):
              print(f'{self.name} speak Chinese')
      
      p_obj = Chiness('tank', 18)
      print(p_obj)
      
      <class '__main__.Chinese'>
      <__main__.Chinese object at 0x000001E97164CA48>
      
      • 通过元类type来定义类的方法
      class_name = 'Chinese'  # 类名
      class_dic = {}  # 局部名称空间(类的名称空间)
      code = """
      country = 'China'
      
      def __init__(self, name, age):
          self.name = name
          self.age = age
          
      def speak(self):
          print(f'{self.name} speak Chinese')
      """
      exec(code, {}, class_dic)   # 通过exc将字符串加入名称空间
      print(class_dic)
      Chinese = type(class_name, (object,), class_dic)
      print(Chinese)
      obj = Chinese('tank', 18)   # 这里可以将obj换为Chinese和class方法一样
      print(obj)
      
      {'country': 'China', '__init__': <function __init__ at 0x00000213808D1558>, 'speak': <function speak at 0x00000213808D14C8>}
      <class '__main__.Chinese'>	# 可以看到与上面class创建的类一样
      <__main__.Chinese object at 0x00000213824EC9C8>
      
      • 怎么使用
      # 自定义一个元类
      class MyMetaClass(type):
          # 定义元类自己的属性,并重用父类type中的属性
          def __init__(self, class_name, class_bases, class_dict):
              print(self)
              if not class_name.istitle():    # 判断类名首字母是否大写
                  raise NameError('类名首字母必须大写哦宝贝')
              if not class_dict.get('__doc__'): # 判断类的名称空间中文档属性是否为空(就是类中是否写了注释)
                  raise TypeError('必须写注释哦小宝贝')
              # 重用父类的属性
              super().__init__(class_name, class_bases, class_dict)
      
          """
          元类中的__call__就是创建类的过程
          __call__在通过对象()时调用,此处的对象为元类MyMetaClass
          (User,(object,), User.__dict__)产生的对象User,在User()实例化时调用的
          """
          def __call__(self, *args, **kwargs):
              print(self)
              # 默认继承object类中的__new__方法,并将User传入得到一个对象,若不用此方法会出现递归
              obj = object.__new__(self)
              print(obj)
              self.__init__(obj, *args, **kwargs) # 类调用类内部方法时,相当于是一个普通函数
              return obj
      
      class User(object, metaclass=MyMetaClass):
          """元类实验"""
          def __init__(self):
              print('tank_dsb')
      
      obj = User()
      print(obj)
      print(User)
      
      <class '__main__.User'>
      <class '__main__.User'>
      <__main__.User object at 0x00000279B014CC48>
      tank_dsb
      <__main__.User object at 0x00000279B014CC48>
      <class '__main__.User'>
      
  • 相关阅读:
    JavaScript数组升降序排列、最大值、最小值等
    css3箭头
    隐藏显示
    最后一个 last-of-type
    jquery函数封装
    为什么要使用rem
    Git的使用--如何将本地项目上传到Github
    jQuery判断是否选中
    数组索引赋值
    HTML中input和button设置同样高度却不能等高的原因
  • 原文地址:https://www.cnblogs.com/YGZICO/p/12050809.html
Copyright © 2011-2022 走看看