Python基础之面向对象:8、面向对象之元类

[复制链接]
发表于 2022-11-8 17:33:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
目录

面向对象之元类

一、什么是元类


  • Python中一切皆为对象,对象是有类实例化生成;
  • 类也是对象(类对象),生成类对象的类可称之为元类;
  • 所以,元类就是来创建类对象的,可称之为类工厂;
  • type是python内建元类,type是最上层的元类,也可称为一切类对象的元类
二、元类推导流程
  1. """推导步骤1:如何查看数据的数据类型"""
  2. # s1 = 'hello world'  # str()
  3. # l1 = [11, 22, 33, 44]  # list()
  4. # d1 = {'name': 'jason', 'pwd': 123}  # dict()
  5. # t1 = (11, 22, 33, 44)  # tuple()
  6. # print(type(s1))  # <class 'str'>
  7. # print(type(l1))  # <class 'list'>
  8. # print(type(d1))  # <class 'dict'>
  9. # print(type(t1))  # <class 'tuple'>
  10. """推导步骤2:其实type方法是用来查看产生对象的类名"""
  11. # class Student:
  12. #     pass
  13. # obj = Student()
  14. # print(type(obj))  # <class '__main__.Student'>
  15. """推导步骤3:python中一切皆对象 我们好奇type查看类名显示的是什么"""
  16. class Student:
  17.     pass
  18. obj = Student()
  19. print(type(obj))  # <class '__main__.Student'>
  20. print(type(Student))  # <class 'type'>
  21. class A:pass
  22. class B:pass
  23. print(type(A), type(B))
  24. """
  25. 结论:我们定义的类其实都是由type类产生的>>>:元类(产生类的类)
  26. """
复制代码
三、创建类的方式

方式一:

创建方法:
​        直接使用class关键字创建
  1. class Foo:
  2.     school_name = 'kangkang
  3.     def func1(self):
  4.         pass
  5.     print(Teacher)
  6.     print(Teacher.__dict__)
复制代码
方式二:

创建方法:
​        使用元类type
​        type(类名, 类的父类, 类的名称空间)
  1. cls = type('Student', (object,), {'name':'jason'})
  2. print(cls)
  3. print(cls.__dict__)
  4. """
  5. 1.手动写键值对
  6.     针对绑定方法不好定义
  7. 2.内置方法exec
  8.     能够运行字符串类型的代码并产生名称空间
  9. """
复制代码
四、元类定制类的产生行为

​        通过上述推导,我们得出了我们所创建的类,其实就是type帮我们所生成的,那么我们是否可以通过继承type(元类)的方式,来研究type底层代码原理,来改修,新增条件,来定制类的生成
例如:
​        生成类时,类名的首字母必须大写,否则报错
推导流程:
  1. """
  2. 推导
  3.         1、对象是由类名加括号产生的          __init__
  4.         2、类是由元类加括号产生的                __init__
  5.         3、查看type底层源码,找到__init__
  6.         __init__(cls, what, bases=None, dict=None)
  7.         cls:元类本身
  8.         what:需要产生的类名
  9.         bases:产生类的父类
  10.         dict:类体名称空间
  11.         4、通过上述我们就可以发现what可以控制类名的产生
  12.        
  13. """
  14. # 1、首先生成一个类,继承元类(type)
  15.     class MyMetaClass(type):
  16.         pass
  17.    
  18. # 2、在类中,创建def __init__(self, what, bases=None, dict=None):
  19. # 3、对限制what产生类名的条件
  20.         class MyMetaClass(type):
  21.         
  22.     def __init__(self, what, bases=None, dict=None):
  23.         
  24.         # 设置条件,未满足时,主动抛出异常并提示
  25.         if not what.istitle():
  26.             raise TypeError('类的首字母要大写')
  27.             
  28.         # 条件满足后调用父类进行产生
  29.         super().__init__(what, bases, dict)
  30. # 4、指定类的元类:利用关键字metaclass指定类的元类
  31. class myclass(metaclass=MyMetaClass):
  32.     desc = '元类其实很有趣 就是有点绕'
  33. class Student(metaclass=MyMetaClass):
  34.     info = '我是学生 我很听话'
  35. print(Student)
  36. print(Student.__dict__)
  37. # 5、这个时候我们在生成MyMetaClass的子类时,就必须要遵循它的条件,否则将会报错
复制代码
五、元类定制对象的产生行为

要求:
​        生成的对象必须使用关键字进行传参,否则将无法生成对象
推导流程:
  1. '''
  2. 推导:
  3.         1、我们在上述学习了类的魔法方法,发现:
  4.         2、对象加括号会执行产生该对象类里的__call__
  5.         3、类加括号会执行产生该类的类里的__call__
  6.         4、观察__call__(self, *args, **kwargs):
  7.                 self: 调用者本身
  8.                 *args:接收位置实参
  9.                 **kwargs:接收关键字实参
  10.         5、得出结论,我们需要对args进行约束就可达到条件
  11. '''
  12. class MyMetaClass(type):
  13.     def __call__(self, *args, **kwargs):
  14.         
  15.         # 1.产生一个空对象(骨架)
  16.         # 2.调用__init__给对象添加独有的数据(血肉)
  17.         # 3.返回创建好的对象
  18.         # print(args)  # ('jason', 18, 'male')
  19.         # print(kwargs)  # {}
  20.         
  21.                   # 设置条件
  22.         if args:
  23.         # 当条件未满足时,主动抛出异常,并提升
  24.             raise TypeError("需要进行关键字传参")
  25.             
  26.         # 条件满足后执行,使用super,重新调用父类
  27.         return super().__call__(*args, **kwargs)
  28.            
  29. class Student(metaclass=MyMetaClass):
  30.     def __init__(self, name, age, gender):
  31.         # print('__init__')
  32.         self.name = name
  33.         self.age = age
  34.         self.gender = gender
  35. # obj = Student('jason', 18, 'male')
  36. obj = Student(name='jason',age= 18,gender= 'male')
  37. print(obj.__dict__)
复制代码
六、元类之双下new

​        当我们在使用类产生对象时,类体代码种名字产生的顺序是:
​        __ call __ >>>: __ new __ >>>:__ init __
​        双下call将类名传给双下new,然后new将对象的名字传给双下init而后产生类名
​        得出结论:对象是由new进行产生的
  1. class MyMetaClass(type):
  2.     def __call__(self, *args, **kwargs):
  3.         
  4.         # 1.产生一个空对象(骨架)
  5.         obj = self.__new__(self)
  6.         
  7.         # 2.调用__init__给对象添加独有的数据(血肉)
  8.         self.__init__(obj,*args, **kwargs)
  9.         
  10.         # 3.返回创建好的对象
  11.         return obj
  12. class Student(metaclass=MyMetaClass):
  13.     def __init__(self, name):
  14.         self.name = name
  15. obj = Student('jason')
  16. print(obj.name)
  17. """
  18. __new__可以产生空对象
  19. """
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表