深圳网站设计公司的,小地方网站建设公司好,wordpress怎么添加自动推送代码,网站首页背景代码Python 面向对象之元类
【一】一切皆对象
【1】元类
元类#xff08;metaclass#xff09;是Python中用于创建类的类。在Python中#xff0c;类是对象#xff0c;而元类就是类的类它们控制类的创建过程#xff0c;允许你定制类的行为Python中内置的默认元类是type我们用…Python 面向对象之元类
【一】一切皆对象
【1】元类
元类metaclass是Python中用于创建类的类。在Python中类是对象而元类就是类的类它们控制类的创建过程允许你定制类的行为Python中内置的默认元类是type我们用class关键字定义的所有类以及内置的类都是有元类type实例化产生
【2】class机制
class是Python的关键字目的用来创建类它在底层实现类的定义本质上有四个步骤 获取类名获取基类获取类的名称空间调用元类type实例化产生所定义的新类
# 使用class关键字创建
class PeaShooter(object):owner 戴夫def __init__(self, name):self.name namedef introduce(self):print(fIm {self.name})print(PeaShooter.__dict__)
# {__module__: __main__, owner: 戴夫, __init__: function PeaShooter.__init__ at 0x000002534E353AC0, introduce: function PeaShooter.introduce at 0x000002534E639EA0, __dict__: attribute __dict__ of PeaShooter objects, __weakref__: attribute __weakref__ of PeaShooter objects, __doc__: None}# 使用type函数创建
# 类名
class_name PeaShooter
# 基类
class_bases (object,)
# 类的名称空间
class_dict {}
class_body
owner 戴夫
def __init__(self, name):self.name name
def introduce(self):print(fTm {self.name}) # 类的属性和方法存储在class_dict里面. 第二个参数{}控制代码的执行环境
exec(class_body, {}, class_dict)
# 使用type函数创建
PeaSHooter type(class_name, class_bases, class_dict)print(PeaSHooter.__dict__)
# {owner: 戴夫, __init__: function __init__ at 0x0000017E39653E20, introduce: function introduce at 0x0000017E39783AC0, __module__: __main__, __dict__: attribute __dict__ of PeaShooter objects, __weakref__: attribute __weakref__ of PeaShooter objects, __doc__: None}【3】如何自定义元类
元类的作用是控制类的创建 也就意味着我们可以定制自己的类的具体行为 class机制默认的元类是typemetaclasstype 也就是说我们修改metaclass参数就可以自定义元类 元类是一切类的基石所以我们自定义的元类就必须继承type 目的在于使用元类的大部分功能仅定制我们需要的那部分功能
# 自定义元类
class MyMeta(type):pass# 普通的类元类默认是type
class PeaShooter(object, metaclasstype):pass# 普通自定义的类元类是Shooter
class PeaShooter(object, metaclassMymeta):pass
【二】自定义元类控制自定义类
【1】实例化对象的本质
我们知道类的实例化会触发魔法方法__new__、__init__ __new__申请空间创建并返回一个空对象__init__接收new创建的空对象并初始化这个空对象 我们知道实例的调用会触发魔法方法__call__ __call__当对象被调用时触发
【2】控制自定义类的类名、继承关系、名称空间
1讲解
# 类名
class_name PeaShooter
# 类的基类
class_bases (object, )
# 类的名称空间
class_dict {}# 使用元类创建类
PeaShooter1 type(class_name, class_bases, class_dict)# 使用自定义元类创建类
class MyMeta(type):pass
PeaShooter2 MyMeta(class_name, class_bases, class_dict)type是Python的内置元类而使用type创建类就是通过元类type进行类的实例化过程即元类的实例化会自动触发魔法方法__init__、__new__使用元类type或者自定义元类MyMeta需要传入参数类名class_name、基类class_bases、名称空间class_dict所以可以通过自定义的元类中的魔法方法__init__、__new__来控制类名、基类、名称空间这些参数让我们来看看代码的执行执行结果
class MyMeta(type):def __init__(cls, what, bases, dict):print(自定义元类的魔法方法init)print(f参数cls{cls})print(f参数what{what})print(f参数bases{bases})print(f参数dict{dict})super().__init__(what, bases, dict)def __new__(cls, *args, **kwargs):print(自定义元类的魔法方法new)print(f参数cls{cls})print(f参数args{args})print(f参数kwargs{kwargs})return super().__new__(cls, *args, **kwargs)# 等价于 PeaShooter MyMeta(PeaShooter, (object, ), {})
class PeaShooter(object, metaclassMyMeta):pass
# 自定义元类的魔法方法new
# 参数clsclass __main__.MyMeta
# 参数args(PeaShooter, (class object,), {__module__: __main__, __qualname__: PeaShooter})
# 参数kwargs{}
# 自定义元类的魔法方法init
# 参数clsclass __main__.PeaShooter
# 参数whatPeaShooter
# 参数bases(class object,)
# 参数dict{__module__: __main__, __qualname__: PeaShooter}2应用 要求自定义元类来控制创建新类 类名必须含有射手(Shooter) 类名首字母大写 如果没有基类默认为object 必须要有注释文档 小坑不可以直接使用istitle()判断首字母大写因为这个函数会要求其他字母都是小写
class MyMeta(type):def __init__(cls, class_name:str, class_bases, class_dict):if Shooter not in class_name:raise NameError(必须含有Shooter)elif not class_name[0].istitle():print(class_name, type(class_name))raise NameError(必须首字母大写)elif not cls.__doc__:raise ValueError(类必须有注释文档)if not class_bases:class_bases (object, )super().__init__(class_name, class_bases, class_dict)def __new__(cls, *args, **kwargs):return super().__new__(cls, *args, **kwargs)class iceShoot(metaclassMyMeta):pass
# NameError: 必须含有Shooterclass iceShooter(metaclassMyMeta):pass
# NameError: 必须首字母大写class IceShooter(metaclassMyMeta):pass
# ValueError: 类必须有注释文档class IceShooter(metaclassMyMeta):这是寒冰射手pass
print(IceShooter.__mro__)
# (class __main__.IceShooter, class object)【3】控制自定义类实例化的括号内容
1讲解
class MyMeta(type):def __call__(self, *args, **kwargs):print(这是自定义元类的魔法方法call)print(f参数self{self})print(f参数args{args})print(f参数kwargs{kwargs})return super().__call__(*args, **kwargs)class A(metaclassMyMeta):def __init__(self, *args, **kwargs):print(这是根据自定义元类创建新类的魔法方法init)print(f参数self{self})print(f参数args{args})print(f参数kwargs{kwargs})def __call__(self, *args, **kwargs):print(这是根据自定义元类创建新类的魔法方法call)print(f参数self{self})print(f参数args{args})print(f参数kwargs{kwargs})
a A(bruce, age18)
a(tom, age16)
# 这是自定义元类的魔法方法call
# 参数selfclass __main__.A
# 参数args(bruce,)
# 参数kwargs{age: 18}
# 这是根据自定义元类创建新类的魔法方法init
# 参数self__main__.A object at 0x00000200C1787DF0
# 参数args(bruce,)
# 参数kwargs{age: 18}
# 这是根据自定义元类创建新类的魔法方法call
# 参数self__main__.A object at 0x00000200C1787DF0
# 参数args(tom,)
# 参数kwargs{age: 16}我们来看看这个例子a A(bruce, age18) 自定义的类A是根据自定义的元类MyMeta创建出来的所以这里会执行自定义元类MyMeta的魔法方法__call__然后将参数传递给自定义类的魔法方法___init__中这两部内容是一样的并且必须要有返回值即将生成的类返回给实例a综上所述我们可以通过自定义元类MyMeta的魔法方法__call__来控制自定义类的括号内容 a(tom, age16) 实例a使用括号调用了自定义类中的__call__方法这是显而易见的不需要解释
2应用
要求自定义元类来控制创建新类 实例化的参数必须通过关键字参数传参不能通过位置关键字传参
class MyMeta(type):def __call__(cls, *args, **kwargs):if len(args):raise TypeError(实例化必须通过关键字传参)return super().__call__(*args, **kwargs)class Student(metaclassMyMeta):def __init__(self, name, age):self.name nameself.age agestudent_one Student(bruce, 18)
# TypeError: 实例化必须通过关键字传参student_two Student(nametom, age22)
print(student_two.name, student_two.age)
# tom 22【三】总结