首页 > 其他分享 >元类

元类

时间:2024-01-12 20:44:06浏览次数:17  
标签:__ name 元类 init call print class

元类

(一)什么是元类

  • 创建出类的类就是元类
"""元类"""
# 什么是元类
# 元类:就是产出类的类-----创建出类的类----type
# 在python中一切皆对象
class Person(object):
    def __init__(self,name):
        self.name=name
# 实例化类得到对象
p=Person(name='syh')
# 查看实例化类得到的对象的数据类型
print(type(p))# <class '__main__.Person'>
# 查看类的数据类型
print(type(Person))# <class 'type'>
# 查看产生字典的数据类型
print(type(dict))# <class 'type'>
# 查看数据类型后,都存在type这个类,所有type这个类就称之为元类

(二)产生类的两种方式

  • 创建类的两种方式
    • class关键字创建类
    • 通过type创建类

(1)class关键字创建类

#class关键字创建类
class Person(object):
    person='人类'
    def __init__(self,name):
        self.name=name
# 查看创建当前类的类
print(type(Person))# <class 'type'>
# 查看当前类的名称空间
print(Person.__dict__)# {'__module__': '__main__', 'person': '人类', '__init__': <function Person.__init__ at 0x00000210D65A2440>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

(2)通过type创建类

  • type创建类
    • type('类名',(父类1,父类2,....),{属性名:属性值})
# 通过type创建类
# type('类名',(父类1,父类2,....),{数据属性名:数据属性值})
Person=type('Person',(object,),{})
# 查看创建的类的类
print(type(Person))# <class 'type'>
# 查看类的名称空间
print(Person.__dict__)# {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
"""可以在字典中传入 属性:值"""
# 字典中不仅可以添加数据属性,还可以添加函数属性
def read():
    ...
Person=type('Person',(object,),{'name':'syh','run':read})
# 查看创建的类的类
print(type(Person))# <class 'type'>
# 查看类的名称空间
print(Person.__dict__)# {'name': 'syh', 'run': <function read at 0x0000025D14C12320>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# 细心地你可能会发现,当我们使用关键字创建类内的方法的时候,查看他的内存空间是带着类名字的
# 而我们当前创建的这个类,初始化进去的只是一个普普通通的函数,也就是非绑定方法

(三)为什么要使用元类

  • 元类可以控制类的创建,也就是说 我们可以高度定制类的具体行为
    • 简单来说:我们控制了生产的过程,可以在过程中添加内容

(四)元类的基本使用

(1)需求

  • 要求所有类的名字必须是大写

(2)思考

  • 我们应该在哪里定制这些代码
    • 对象的产生过程,是通过类内部的__init__方法实现的
    • 那么,在元类的内部也有一个__init__的方法

(3)基本使用

class MyType(type):
    def __init__(cls,class_name,class_bases,class_dict):
        print(class_name,class_bases,class_dict)
        super().__init__(class_name,class_bases,class_dict)
# 元类的使用区别于我们继承中使用父类的那种形式
# 元类的使用采用metaclass关键字声明

# 创建一个类
class MyClass(object,metaclass=MyType):
    ...
# 初始化类
MyClass()
# MyClass
# (<class 'object'>,)
# {'__module__': '__main__', '__qualname__': 'MyClass'}

(4)进阶使用

  • 限制类的名字必须是首字母大写
class MyType(type):
    def __init__(cls,class_name,class_bases,class_dict):
        print(class_name,class_bases,class_dict)
        if not class_name.istitle():
            raise Exception(f"类名:{class_name}必须首字母大写")
        super().__init__(class_name,class_bases,class_dict)

# 元类的使用区别于我们继承中使用父类的那种形式
# 元类的使用采用metaclass关键字声明

# 创建一个类
# class MyClass(object,metaclass=MyType):
#     ...
# # 初始化类
# MyClass()


# # 创建一个类
# class Myclass(object,metaclass=MyType):
#     ...
# # 初始化类
# Myclass()# Myclass (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'Myclass'}


# 创建一个类
class myclass(object,metaclass=MyType):
    ...
# 初初始化类
myclass()# Exception: 类名:myclass必须首字母大写
  • 限制类只能按关键字传入如参数
class MyMeta(type):
    def __init__(cls, class_name, class_bases, class_dict):
        print(f'这是 MyMeta  中的init方法被触发了')
        if not class_name.istitle():
            raise f'{class_name}必须开头字母大写'
        # print(class_name)  # MyClass
        # print(class_bases)  # (<class 'object'>,)
        # print(class_dict)  # {'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x000002A251540360>}
        super().__init__(class_name, class_bases, class_dict)

    def __call__(self, *args, **kwargs):
        print(f'这是 MyMeta  中的 __call__ 方法被触发了')
        print(f'这是 MyMeta  中的 {args}')
        if args:
            raise Exception(f'参数只能按照关键字传参')
        print(f'这是 MyMeta  中的 {kwargs}')
        obj = super().__call__(*args, **kwargs)
        print(f'这是 MyMeta  中的 {obj}')
        return obj


# 创建一个新的类:继承元类区别于继承父类
# class MyClass(类名)
class Myclass(object, metaclass=MyMeta):
    def __init__(self, name):
        print(f'这是 MyClass  中的init方法被触发了')
        self.name = name

    def __call__(self, *args, **kwargs):
        print(f'这是 MyClass  中的 __call__ 方法被触发了')
        print(args)
        print(kwargs)


# 对象() ---> 触发了实例化得到自己的这个类里面的 __call__ 方法
p = Myclass(name='dream')
p1 = Myclass('hope')
print(p)
print(p1)
# 需求:定制初始化类的时候传参数必须是按照关键字传参,不能按位置传参

# 【1】写一个类 继承元类 type
# 【2】重写 type 的 __call__
# 【3】拦截到  __call__  带的位置参数和关键字参数
# 【4】限制你的不定长未知参数为空即可
# 【5】继承给 想使用的类

(五)元类的进阶使用

(1)引入

  • 类内部定义一个__init__方法,对象加括号会自动执行产生该对象的类里面的__call__方法,并返回对应的返回值
  • 类加括号调用,也会触发元类中的__call__方法,获得返回值,这个返回值就是实例化类得到的对象
class MyType(type):
    def __init__(cls,class_name,class_bases,class_dict):
        print(f"MyType中的init方法")
        super().__init__(class_name,class_bases,class_dict)
    def __call__(self, *args, **kwargs):
        print(f"MyType中的call方法")
        # 创建对象并初始化参数
        obj=super().__call__(*args,**kwargs)
        return obj
class MyClass(metaclass=MyType):
    def __init__(self, name):
        print(f' MyClass 中的 init 方法 我被触发了')
        self.name = name

    def __call__(self, *args, **kwargs):
        print(f' MyClass 中的 call 方法 我被触发了')
        return 'call 方法返回的值'

obj=MyClass(name='syh')
# MyType中的init方法
# MyType中的call方法
#  MyClass 中的 init 方法 我被触发了
print(obj)# <__main__.MyClass object at 0x0000019EE59E7D60>
obj()
# MyType中的init方法
# MyType中的call方法
#  MyClass 中的 init 方法 我被触发了
#  MyClass 中的 call 方法 我被触发了
print(obj())# call 方法返回的值

(2)定制对象的产生过程

class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        print(f'MyType 中的 init 方法 我被触发了')
        super().__init__(class_name, class_bases, class_dict)

    def __call__(self, *args, **kwargs):
        print(f'MyType 中的 call 方法 我被触发了')
        print(f'args: {args}, kwargs: {kwargs}')
        # 定制对象的产生条件:只能通过关键字传参创建对象,不允许通过位置传参
        if args:
            raise TypeError(f'{self.__name__} 产生对象只能通过关键字传参创建')
        # 创建对象并初始化参数
        obj = super().__call__(*args, **kwargs)
        # print(obj)
        # 将产生的对象返回出去
        return obj


class MyClass(metaclass=MyType):
    def __init__(self, name):
        print(f' MyClass 中的 init 方法 我被触发了')
        self.name = name

    def __call__(self, *args, **kwargs):
        print(f' MyClass 中的 call 方法 我被触发了')
        return 'call 方法返回的值'


# 正确的
obj = MyClass(name='张三')
print(obj)
print(obj())

# MyType 中的 init 方法 我被触发了
# MyType 中的 call 方法 我被触发了
# args: (), kwargs: {'name': '张三'}
#  MyClass 中的 init 方法 我被触发了
# <__main__.MyClass object at 0x1287b3dc0>
#  MyClass 中的 call 方法 我被触发了
# call 方法返回的值

# 错误的,会被拦截并报错
obj = MyClass('张三')
print(obj)
print(obj())

#     raise TypeError(f'{self.__name__} 产生对象只能通过关键字传参创建')
# TypeError: MyClass 产生对象只能通过关键字传参创建

总结:

# 总结 :
# 【】如果你想定制类的产生过程 ---> 编写元类中的 __init__ 方法  (必须类名首字母大写)
# 【】如果你想定制对象的产生过程 ---> 编写元类中的 __call__ 方法 (类初始化必须按关键字传参数)

* __new__方法

# __new__ 方法 :如果在元类里面就是产生空对象
# __init__ 方法 :往里面添加血和肉

class MyMeta(type):
    # 填充血肉
    def __init__(cls, class_name, class_bases, class_dict):
        print(f'MyType 中的 init 方法 我被触发了')
        super().__init__(class_name, class_bases, class_dict)

    # 生成骨架
    def __new__(cls, *args, **kwargs):
        print(f'MyType 中的 new 方法 我被触发了')
        print(args)
        print(kwargs)
        obj = type.__new__(cls, *args, **kwargs)
        return obj

    def __call__(self, *args, **kwargs):
        print(f'MyType 中的 call 方法 我被触发了')
        print(f'args: {args}, kwargs: {kwargs}')
        # 创建对象并初始化参数
        obj = super().__call__(*args, **kwargs)
        # print(obj)
        # 将产生的对象返回出去
        return obj


class MyClass(object, metaclass=MyMeta):
    def __init__(self, name):
        print(f' MyClass 中的 init 方法 我被触发了')
        self.name = name

    def __call__(self, *args, **kwargs):
        print(f' MyClass 中的 call 方法 我被触发了')
        return 'call 方法返回的值'

    def __new__(cls, *args, **kwargs):
        print(f' MyClass 中的 new 方法 我被触发了')
        print(args, kwargs)
        # return super().__new__(cls)
        obj = object.__new__(cls)
        # obj.__init__(*args, **kwargs)
        return obj


p = MyClass(name='dream')
p()
# MyType 中的 new 方法 我被触发了
# ('MyClass', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x000001A7BED656C0>, '__call__': <function MyClass.__call__ at 0x000001A7BED65D80>, '__new__': <function MyClass.__new__ at 0x000001A7BED65EA0>})
# {}
# MyType 中的 init 方法 我被触发了
# MyType 中的 call 方法 我被触发了
# args: (), kwargs: {'name': 'dream'}
#  MyClass 中的 new 方法 我被触发了
# () {'name': 'dream'}
#  MyClass 中的 init 方法 我被触发了
#  MyClass 中的 call 方法 我被触发了

标签:__,name,元类,init,call,print,class
From: https://www.cnblogs.com/suyihang/p/17961577

相关文章

  • python之元类
    【什么是元类】元类:就是用来实例化产生类的类关系:元类-----实例化-----》类(People)-----实例化-----》对象(obj)一切都来源于一句话:一切皆为对象【演示】   【如何自定义元类来控制类的产生】   【__call__方法】  【自定义元类控制类的调用=====》......
  • 常用魔法方法和元类
    常用魔法方法和元类1.常用魔法方法__init__ :初始化类时触发__del__ :删除类时触发__new__ :构造类时触发__str__ :str函数或者print函数触发__repr__ :repr或者交互式解释器触发__doc__ :打印类内的注释内容__enter__ :打开文档触发__exit__ :关闭文档触发__getattr__:访......
  • Python中的元类和元编程是什么
    在Python中,元类和元编程是一些高级概念,能够帮助我们更深入地理解和扩展语言的特性。通过元类和元编程,我们可以在运行时动态地创建类、修改类和实例化对象,从而使我们能够更加灵活地编写代码。1.元类是什么?在Python中,类是对象的模板,用于创建对象。而元类则是用于创建类的类。也就是说......
  • [C++ 从入门到精通] 15.友元函数、友元类、友元成员函数
    文章预览:一.前言二.友元函数三.友元类四.友元成员函数一.前言众所周知,C++控制对类对象私有成员的访问。通常,公有类方法(public)提供唯一的访问途径,但是有时候这种限制太严格,以至于不适合特定的编程问题。在这种情况下,C++提供了另外一种形式的访问权限:友元,友元有3种:友元函数;友......
  • C++友元函数和友元类
    在C++中,一个类中可以有public、protected、private三种属性的成员,通过对象可以访问public成员,只有本类中的函数可以访问本类的private成员。现在,我们来介绍一种例外情况——友元(friend)。借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的private......
  • C++_15_友元函数和友元类 - 重写版
    友元函数和友元类友元函数:通过friend关键字,将不属于当前类的函数在当前类中加以声明,使其成为友元函数,同时该函数能够访问private属性的成员变量。友元类:有有元函数,自然也能有友元类,通过friend关键字,将类A在类B中声明,那么类A会成为类B的友元类注意:1、友......
  • 通过元类来控制类的产生
    写一个类来继承type以后这种类都叫元类自定义元类来控制类的产生:可以控制类名,可以控制类的继承父类,控制类的名称空间如果你要自定义元类,就必须继承type练习一:加限制控制类名必须用sd开头classMy_class(type):#def__init__(self,*args,**kwargs): #......
  • 10月18日元类、单例模式
    目录1.元类复习1.什么是元类:2.所有类的元类是谁?3.如何自定义元类呢?4.__init__和__new__和__call__这三者的关系:2.单例模式1.元类复习1.什么是元类:因为一切皆对象这个思想,所以类也是对象,元类构造类,类是由元类实例化得到的2.所有类的元类是谁?是type,它是所有......
  • 10月17日元类回顾
    目录元类回顾1.什么是元类?2.class关键字底层原理3.exec方法自定义元类元类回顾1.什么是元类?​ 能够实例化产生类的类,就叫元类​ 所有类的元类是type​ 自己定义一个类就需要让这个类继承type2.class关键字底层原理​ 底层的原理:调用type这个类里面的初始化方式来生成一个类......
  • 10月16日什么是元类
    目录什么是元类什么是元类就是定义类的类classPerson:pass这个代码里我定义了一个名叫Person的类,而定义这个类的类就叫元类classPerson:passp=Person()#上面我定义了一个类,然后定义这个类的类就是元类#如何找元类?#我要是找对象的类如何去找?要用到typepri......