python元类

博客围绕Python元类展开,介绍元类是产生类的类,默认元类为type。阐述不依靠class关键字创建类的方法,利用exec()执行字符串代码并获取名称空间。还探讨了自定义元类创建类的过程,可拦截__new__方法并增加属性字段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录:

  1. 什么是元类
  2. 不依靠class关键字创建类
  3. 自定义元类创建类的过程

什么是元类

  • 一切皆对象。而对象都是由类实例化得到的
class OldboyTeacher(object):
    school = 'oldboy'
    def __init__(self,name):
        self.name = name
    def run(self):
        print('%s is running'%self.name)
t1 = OldboyTeacher('jason')
# 对象t1是由类OldboyTeacher实例化得到
  • 如果类也是对象,又是谁实例化得到的?
# 分别查看对象t1和OldboyTeacher的类型
print(type(t1))
print(type(OldboyTeacher))

# 结果为:
<class '__main__.OldboyTeacher'>
<class 'type'>

结论1:元类就是产生类的类,默认情况下type就是所有类的元类

回到目录

不依靠class关键字创建类

根据第一个结论我们能理出两条对应关系

  1. 调用元类得到自定义的类
  2. 调用自定义的类得到自定义的类的对象

现在我们来看第一对关系,调用元类来得到自定义的类,都需要哪些参数(OldboyTeacher=type(…),括号内传什么?)

我们自定义一个类的时候都有哪些关键的组成部分:

  1. 类名
  2. 类的父类
  3. 类的名称空间

就以第一阶段的OldboyTeacher类为例,class关键字创建自定义类的步骤

1.获取类名(OldboyTeacher)

2.获取类的父类(object,)

3.执行类体代码获取产生的名称空间(如何获取???)

4.调用元类得到自定义类OldboyTeacher = type(class_name,class_bases,{...})

补充
如何执行一段字符串内部的代码并将产生的名称空间交给对应的参数?
使用exec()

class_body = """
school = 'oldboy'
def __init__(self,name):
      self.name = name
def run(self):
      print('%s is running'%self.name)
"""
class_dic = {}
class_global = {}

exec(class_body,class_global,class_dic)
# class_global一般情况下都为空,除非在字符串代码内部用global关键字声明,才会将产生的名字丢到class_global全局名称空间中
print(class_dic)

{'school': 'oldboy', '__init__': <function __init__ at 0x000000B5D2771EA0>, 'run': <function run at 0x000000B5DB5B7400>}

有了这个exec方法后,我们就可以不依赖于class关键字创建自定义类

# 类名
class_name = 'OldgirlTeacher'
# 类的父类
class_bases = (object,)  # 注意必须是元祖,逗号不能忘
# 名称空间
class_body = """
school = 'oldgirl'

def __init__(self,name):
    self.name = name

def run(self):
    print(self.name)
"""
class_dic = {}
exec(class_body,{},class_dic)

#调用元类创建自定义类
OldgirlTeacher = type(class_name,class_bases,class_dic)
print(OldgirlTeacher)

# 结果为:<class '__main__.OldgirlTeacher'>

# 并且它可以访问自身的属性和方法,并实例化产生对象
print(OldgirlTeacher.school)
print(OldgirlTeacher.run)

# 结果为:
"""
oldgirl
<function run at 0x000000229B157378>
"""

obj = OldgirlTeacher('jason')
print(obj.school)
obj.run()

回到目录

自定义元类创建类的过程

  1. 如何自定义元类
class Mymeta(type):  # 必须是继承了type的类才是自定义元类
    pass

class oldboyTeacher(metaclass=Mymeta):  # 通过metaclass可以指定类的元类
    school = 'oldboy'

    def __init__(self,name):
        self.name = name

    def run(self):
        print('%s is running'%self.name)
  1. _call_
    思考:一个类的对象加括号调用会执行该对象父类中的__call__方法,那么类也是对象,它在加括号实例化对象的时候,是不是也应该走它父类的__call_方法?
class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

class OldboyTeacher(object,metaclass=Mymeta): #使用元类需要制定metaclass
    school = 'oldboy'
    def __init__(self,name):
        self.name = name
    def run(self):
        print('%s is running'%self.name)
obj = OldboyTeacher('jason')

"""
打印结果:
<class '__main__.OldboyTeacher'>
('jason',)
{}
"""

思考:类加括号实例化对象的时候,有哪几个步骤?

  1. 创建一个该类的空对象

  2. 实例化该空对象

  3. 将实例化完成的空对象返回给调用者

# 也就是说__call__里面需要做三件事
class Mymeta(type):
    def __call__(self, *args, **kwargs):
        # 1.产生空对象
        # 2.初始化对象
        # 3.返回该对象
        # 那我先做最后一件事,返回一个123,发现
        return 123

obj = OldboyTeacher('jason')
print(obj)  
# 结果就是123   

手动去做

class Mymeta(type):  
  def __call__(self, *args, **kwargs):
        # 1.产生一个空对象
        obj = self.__new__(self)
        # 2.实例化该对象
        self.__init__(obj,*args,**kwargs)
        # 3.返回该对象
        return obj
# 关于这个__new__,我们是不是不知道是个啥,我这里直接告诉你,它就是用来创建空对象的

思考:这是类加括号产生对象的过程,那么元类加括号产生类的过程是不是也应该是这个三步

  1. 产生一个空对象(指类)

  2. 实例化该空对象(实例化类)

  3. 将实例化完成的类对象返回

那依据上面的推导,self.__new__就是关键了,可以在自定义元类里面定义一个__new__方法来派生

class Mymeta(type):
    def __new__(cls, *args, **kwargs):
        print(cls)
        print(args)
        print(kwargs)


class OldboyTeacher(object,metaclass=Mymeta):
    school = 'oldboy'

    def __init__(self, name):
        self.name = name

    def run(self):
        print('%s is running' % self.name)

"""
<class '__main__.Mymeta'>
('OldboyTeacher', (object,), {'__module__': '__main__', '__qualname__': 'OldboyTeacher', 'school': 'oldboy', '__init__': <function OldboyTeacher.__init__ at 0x000000323CEB9510>, 'run': <function OldboyTeacher.run at 0x000000323CEE7158>})
{}
"""

__new__里面的*args参数接收到了三个位置参数,并且很容易辨认它们对应的就是类名,类的父类,类体代码执行后的名称空间

那么我们可不可以将__new__()的形参换一种写法

class Mymeta(type):
    def __new__(cls, class_name,class_bases,class_dic):
        print(class_name)
        print(class_bases)
        print(class_dic)
     # 这里需要记住的是,必须在最后调用元类type中的__new__方法来产生该空对象
        return type.__new__(cls,class_name,class_bases,class_dic)
    
class OldboyTeacher(metaclass=Mymeta):
    school = 'oldboy'
    def __init__(self,name):
        self.name = name
    def run(self):
        print('%s is running'%self.name)

最终可以得出一个结论:如果需要自定义类的创建可以拦截__new__,并在在class_attrs增加属性字段

# 表名  主键  一对字段
class MemetaClass(type):
    def __new__(cls, class_name,class_bases,class_attrs):
        if class_name == 'Userinfo':
            raise TypeError('我不想鸟你')
        if 'school' in class_attrs:
            class_attrs['school'] = '澳门最大线上赌场开业啦'
        class_attrs['table_name'] = 'userinfo'
        class_attrs['primary_key'] = 'id'
        return type.__new__(cls,class_name,class_bases,class_attrs)
class User(object,metaclass=MemetaClass):
    school = 'oldboy'
print(User.__dict__)

回到目录

内容来源于:https://www.cnblogs.com/Dominic-Ji/p/10520256.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值