元类

作者: 山猪打不过家猪 | 来源:发表于2022-10-13 12:28 被阅读0次

1. 元类创建的过程

class Mymeta(type):

    def __new__(cls, *args, **kwargs):
        print('1.type 调用了 Mymeta 的 new 方法--生成一个空对象,即 People 类')
        "这里调用的是 type 的 new 方法,传入参数需要注意全部传会 type 元类"
        return super().__new__(cls, *args, **kwargs)


    def __init__(self, class_name, class_bases, class_dic):
        print('2.初始化这个对象--- People 类,给 People 类添加额外的功能')
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父类的功能
        # 自定义的类的功能
        if not class_name.istitle():
            raise TypeError('类名%s请修改为首字母大写' % class_name)

        if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
            raise TypeError('类中必须有文档注释,并且文档注释不能为空')


    # 传入 Mymeta的参数:People, 以及传入People的参数
    def __call__(self, *args, **kwargs):
        """
        self---<class '__main__.People'>
        :param args: (1,)
        :param kwargs: {'y': 2}
        :return: 返回最终初始化好的代码
        """
        print('3.调用了 Mymeta 的 call 方法')
        # 调用 People 类里的 __new__方法,生成空对象
        People_obj = self.__new__(self, *args, **kwargs)
        # 调用 People 类里的 __init__方法,初始化空对象,注意:第一个传入的参数是生成好的空对象
        self.__init__(People_obj, *args, **kwargs)
        # 给 People 类生成的对象 obj 添加额外的功能
        print("9.给 People 类生成的对象 obj 添加额外的功能")
        People_obj.__dict__["新增一个属性"] = None
        # 返回初始化好的对象
        return People_obj


class People(metaclass=Mymeta):
    """People 类的注释"""
    # 产生 People 类真正的对象
    def __new__(cls, *args, **kwargs):
        # 在这里就可以定制功能
        print('4.生成 People 类的空对象')
        print('5.传入的位置参数', args)
        print('6.传入的位置参数', kwargs)
        # 调用所继承的父类的__new__方法,这里就是 object 类,一定要传入 cls(当前这个类)
        "这里要区别于自定义元类的 new 方法,自定义元类调用的是 type 的 new 方法,传入参数是不一样的"
        return super().__new__(cls)

    def __init__(self, x, y=None):
        print("7.初始化 People 类的对象")
        self.x = x
        self.y = y
        print("8.初始化 People 类的对象结束")

# 调用People 类生成对象---> People()= Mymeta.__call__()
obj = People(1, y=2)
print('10.最终的对象字典:', obj.__dict__)
image.png
元类创建过程

2. 元类的应用

  • python原生的list无add方法,为了实现C#的add方式我们使用元类
class ListMeta(type):
    def __new__(cls, name, bases, attrs):
        # 在类属性当中添加了add函数
        # 通过匿名函数映射到append函数上
        attrs['add'] = lambda self, value: self.append(value)
        return super().__new__(cls, name, bases, attrs)
    
    
class MyList(list, metaclass=ListMeta):
    pass

lt = MyList()
lt.add(3)
lt.add(4)
print(lt)
>>>
[3, 4]
元类的使用场景:涉及类属性变更和类创建的时候

一个工厂模式的,我们要求所有的类,都是通过这个工厂创建,但是现在这种情况,可以跳过工厂来创建,工厂中可能有一些必须执行的方法,例如统计生产了多少产品, 没生产一个产品,发送一点短信

class Last_of_us:
    def play(self):
        print('the Last Of Us is really funny')
        
        
class Uncharted:
    def play(self):
        print('the Uncharted is really funny')
        

class PSGame:
    def play(self):
        print('PS has many games')
        
        
class GameFactory:
    games = {'last_of_us': Last_of_us, 'uncharted': Uncharted}
    def __new__(cls, name):
        if name in cls.games:
            return cls.games[name]()
        else:
            return PSGame()
        
uncharted = GameFactory('uncharted').play()
last_of_us = GameFactory('last_of_us').play()
Last_of_us().play()
>>>
the Uncharted is really funny
the Last Of Us is really funny
the Last Of Us is really funny
  • 改进
class check_factory(type):

    def __call__(self, *args, **kwargs):
        if len(args) == 0 or args[0] != 'factory':
            raise TypeError("Can't instantiate directly")

class Last_of_us(metaclass=check_factory):
    def play(self):
        print('the Last Of Us is really funny')

class Uncharted:
    def play(self):
        print('the Uncharted is really funny')

class PSGame:
    def play(self):
        print('PS has many games')

class GameFactory:
    games = {'last_of_us': Last_of_us, 'uncharted': Uncharted}

    def __new__(cls, name):
        if name in cls.games:
            return cls.games[name]()
        else:
            return PSGame()

uncharted = GameFactory('uncharted').play()
last_of_us = GameFactory('last_of_us')
Last_of_us
控制子类生成

假设你是个库的作者,你要求用户继承你的类必须实现特定的方法,比如下面这个:

# 库提供的父类
class Father():
    def foo(self):
        return self.bar()

# 用户写的子类
class Child(Father):
    def bar(self):
        return True

如果想强制让子类实现foo方法


class Meta(type):
    def __new__(cls, name, bases, dct, **kwargs):
        if name != 'Father' and 'foo' not in dct:
            raise TypeError('Class must contain foo() method.')
        return super().__new__(cls, name, bases, dct, **kwargs)

# 添加了元类
class Father(metaclass=Meta):

    def foo(self):
        return self.foo()

class Child(Father):
    def bar(self):
        return True

c1= Child()
>>>
TypeError: Class must contain foo() method.
动态给子类添加方法

比如说,我只想让名叫 Apple 的子类具有 sayHi() 方法:

class Meta(type):
    def __new__(cls, name, bases, dct, **kwargs):
        if name == 'Apple':
            dct.update({
                    'sayHi': lambda: 'Hi I am Apple'
                })
        return super().__new__(cls, name, bases, dct, **kwargs)


class Food(metaclass=Meta):
    pass

class Apple(Food):
    pass

class Pear(Food):
    pass

Apple.sayHi()
Pear.sayHi()
>>>
AttributeError: type object 'Pear' has no attribute 'sayHi'

除了判断类的名称外,你可以编写更加复杂的判据,来实现业务的要求。

相关文章

  • 3.isa

    isa指向 ->实例isa -> 类|类isa -> 元类 | 元类isa -> 根元类|根元类isa -> 根元...

  • Python元类

    元类 元类就是用来创建类的,元类就是类的类。 实例 关于type()函数的使用戳这里 元类为一个函数 元类为一个类

  • isa 和 superclass

    实例对象的 isa 指向类对象,类对象的 isa 指向元类对象,元类的isa 指向基类的元类对象,基类的元类对象的...

  • 友元类和嵌套类

    友元类 友元函数用于类的扩展接口中,类并非只能拥有友元函数,也可以将类作为友元,该类称为友元类。 友元类的所有方法...

  • 29.Python之面向对象的元类

    Python之面向对象的元类 什么是元类?类的类就是元类,元类创建对象。使用class定义的类,用来产生程序员自己...

  • 面向对象 六

    目录 1.元类 1.1 类创建过程 1.2 元类控制类的创建 1.3 元类控制类的创建过程案例 1.4 自定义元...

  • Python 元类

    什么是元类?元类就是创建类的类,type是最原始的一个元类。(实例是实例化的类,而类是元类的实例)python中一...

  • isa

    NSObject的父类为nil NSObject的isa为根元类 元类的isa都为根元类 根元类的isa为本身 根...

  • iOS开发中方法查找流程图

    实例对象的isa指针指向类对象,类对象的isa指针指向元类,元类的isa指针指向根元类,根元类的isa指针指向自己...

  • 类对象、元类

    任何实例的isa指针指向其类对象,类对象的isa指针指向其元类对象,元类对象的isa指针指向根元类对象,根元类对象...

网友评论

      本文标题:元类

      本文链接:https://www.haomeiwen.com/subject/hstczrtx.html