首页 > 编程语言 >python 面向对象之元类

python 面向对象之元类

时间:2022-11-08 17:34:55浏览次数:45  
标签:__ obj python 元类 面向对象 print type class 之元类

python 面向对象之元类

type方法的应用

1.查看数据类型

s1 = 'hello world'  # str()
l1 = [11, 22, 33, 44]  # list()
d1 = {'name': 'jason', 'pwd': 123}  # dict()
t1 = (11, 22, 33, 44)  # tuple()
print(type(s1))  # <class 'str'>
print(type(l1))  # <class 'list'>
print(type(d1))  # <class 'dict'>
print(type(t1))  # <class 'tuple'>

2.查看对象是哪个类产生的

class Student:
    pass
obj = Student()
print(type(obj))  # <class '__main__.Student'>

3.类其实也是一种对象,类是由谁产生的呢

class Student:
    pass
obj = Student()
print(type(obj))  # <class '__main__.Student'>
print(type(Student))  # <class 'type'>
class A:pass
class B:pass
print(type(A), type(B))  # <class 'type'> <class 'type'>

我们发现我们的类都是由type产生的。

而type就是一切类的生产者(包括它自己)被称为元类

print(type(type))  # <class 'type'>

创建类的底层机制

我们平常都是用class关键字来创建类的:

class 类名(父类):
	类体代码

而我们打开产生类的type它的源码,可以看见type除了传入对象来判断产生它的类,还有另一种用法

![image-20221108155811062](C:\Users\ASUS\Pictures\md文件图片\python 面向对象之元类\image-20221108155811062.png)

即传入类名,父类,类体名称空间产生一个新类。

class_body_code = """
name = 'leethon'
"""
class_dict = {}
# exec会执行代码并将一些名称传入class_dict名称空间
exec(class_body_code, {}, class_dict)
cls = type('Student', (object,), class_dict)  # 传入类名,传入父类,传入名称空间
obj = cls()
print(obj.__class__)  # <class '__main__.Student'>
print(obj.name)  # leethon

这个过程很麻烦,而且和class关键字所实现的还有差距,所以这里只做了解。

即我们的类实际上都是由元类产生的,底层就是用了一个type方法。

通过元类控制类的产生

因为类是由元类产生的,所以可以理解为类实际上就是元类进行实例化产生的对象,

联系对比:

  • 对象--通过类名()的方式产生--触发了类体中__init__得到了独有的属性
  • 类---通过元类产生--触发了元类中的__init__得到了每个类所独有的类属性

所以我们可以通过派生元类中的__init__方法达到控制类产生的目的。

那么派生自然要用到继承和super关键字,将元类type作为父类得到的类也是元类,因为它有产生类的方法。

class MyMetaClass(type):
    def __init__(cls, what, bases=None, dict=None):
        if not what.istitle():  # 如果类名不大写开头
            raise TypeError(f'类定义必须要大写,你看看你写的{what}')  # 报错终止程序
        # 如果符合开头大写的命名风格就继续执行
        super().__init__(what, bases, dict)  # 继承父类中的方法

class A(metaclass=MyMetaClass):  # 修改默认元类变为我们的元类,也修改了产生本类的方式
    pass
class bbb(metaclass=MyMetaClass):  # 在这里报错,TypeError: 类定义必须要大写,你看看你写的bbb
    pass

如果我们选择修改产生类的元类(通过修改metaclass关键字参数的方式),那么这个元类可以是我们基于type派生的,我们通过派生type的双下init方法就可以控制类的产生过程了。

通过元类控制类产生对象

看标题有些相似,但要注意区分,上一小节指控制类的产生过程,这一节要说明对象的产生过程。

  • 我们在上一篇博客中提到,类的魔法方法__call__是在对象被加括号调用时自动触发的。

  • 那么将类看成对象,产生它的元类中的__call__也就会在类名加括号调用时自动触发。

也就是我们平常习以为常的obj = 类名()的方式产生对象的过程会自动触发元类中的__call__

理解了上面这一点后,我们就可以尝试从元类的__call__做手脚,来控制对象的产生了。

我们可以直接通过派生的方式修改,也可以直接重写的一个call

一般来说类的元类都是type,它的__call__,主要做了以下几件事:

class MyMetaClass(type):
    def __call__(cls, *args, **kwargs):
        # 1.就是产生一个空对象
        obj = cls.__new__(cls)
        # 2.调用类的init传入对象和参数
        cls.__init__(obj, *args, **kwargs)
        # 3.返回创建好的对象
        return obj
# 以上的__call__只是对type中的简单模仿,建议还是直接用super派生

class A(metaclass=MyMetaClass):  # 修改默认元类变为我们的元类,也修改了产生本类的方式
    pass


obj = A()  # 触发了MyMetaClass的__call__
print(obj)  # <__main__.A object at 0x0000025DBB436610>
让产生对象时只能传入关键字参数
# 实现方式1
class MyMetaClass(type):
    def __call__(cls, *args, **kwargs):
        if args:
            raise TypeError('你只能传入关键字参数')  # 在这一步控制了只能传入关键字参数
        return super().__call__(*args, **kwargs)  # 自动传入调用的对象,方法是父类的方法
		# 并将值返回出去,与原本call的结构一致

class A(metaclass=MyMetaClass):  # 修改默认元类变为我们的元类,也修改了产生本类的方式
    def __init__(self, name):
        self.name = name


obj = A('leethon')
print(obj.name)

# 实现方式2(不建议)
class MyMetaClass(type):
    def __call__(cls, *args, **kwargs):
        # 1.就是产生一个空对象
        obj = cls.__new__(cls)
        # 2.调用类的init传入对象和参数
        if args:
            raise TypeError('你只能传入关键字参数')  # 在这一步控制了只能传入关键字参数
        cls.__init__(obj, *args, **kwargs)
        # 3.返回创建好的对象
        return obj

标签:__,obj,python,元类,面向对象,print,type,class,之元类
From: https://www.cnblogs.com/Leethon-lizhilog/p/16870479.html

相关文章

  • Python基础之面向对象:8、面向对象之元类
    目录面向对象之元类一、什么是元类二、元类推导流程三、创建类的方式方式一:方式二:四、元类定制类的产生行为五、元类定制对象的产生行为六、元类之双下new面向对象之元类......
  • python import
    fromtest2import*import导入和fromimport导入,在内存上没有差别,都需要从头到尾全部编译一遍,并加载到内存中。这里如果在test2中定义__all__=['func1',....],这里限制了......
  • IOU计算-纯python
    iou计算代码,纯pythonfrom:https://blog.csdn.net/leviopku/article/details/81629492#!/usr/bin/envpython#encoding:utf-8defcompute_iou(rec1,rec2):......
  • Python学习笔记3
    Python学习笔记3       Python中的True和Falsepath=os.path.join('a','b')print(path)#a\bmessages=[]ifmessages==False:  print('[]就是False')m......
  • oop面向对象
    面向对象OOP面向过程&面向对象面向过程思想步骤清晰简单,第一步做什么,第二步做什么...面对过程适合处理一些较为简单的问题面向对象思想物以类聚,分类的思维模......
  • day30面向对象(4)
    目录面向对象的魔法对象魔法方法笔试题元类简介创建类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new设计模式简介面向对象的魔法对象魔法方法......
  • 面向对象4魔法方法及元类
    目录面向对象的魔法方法魔法方法笔试题元类简介创建类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new设计模式简介面向对象的魔法方法魔法方法......
  • esp32 micropython引脚电容值实现模拟按键
    frommachineimportTouchPad,Pin#引入touchpad模块fromtimeimportsleepimportutimetouch_up=TouchPad(Pin(12))#12是上touch_down=TouchPad(Pin(13)......
  • Python基础30
    今日内容概要面向对象之魔法方法基于魔法方法的笔试题元类简介创建类的两种方式元类定制类的产生行为元类定制对象的产生行为魔法方法之双下new方法设计模式简介......
  • python 入门 2 数字类型
    在编程时,经常会使用到数字来记录的情况,比如游戏分数、可视化。存储web等。python会根据数字的用法,以不同的方式来处理。1.整数python中的整数  加(+)减(-)乘(*)除(/)运算: ......