" name="sm-site-verification"/>
侧边栏壁纸
博主头像
PySuper 博主等级

千里之行,始于足下

  • 累计撰写 206 篇文章
  • 累计创建 14 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Python 元类

PySuper
2024-06-12 / 0 评论 / 2 点赞 / 124 阅读 / 0 字
温馨提示:
本文最后更新于2024-06-19,若内容或图片失效,请留言反馈。 所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

Python中一切皆对象,元类 --> 类 --> 对象。

Python 中的元类(metaclass)是一种用于创建类的“类”。换句话说,元类定义了如何构造类本身

每个类都是一个对象,而元类就是用来创建这些类对象的类

理解元类的概念需要先对 Python 中的类和对象有一个清晰的认识

基本概念

1. 类和对象

  • 在 Python 中,类(class)用于创建对象(object),类定义了对象的属性和方法

  • 对象是类的实例

2. 元类

  • 元类用于创建类本身。换句话说,元类就是类的类

  • 默认情况下,Python 中所有的类都是由 type 元类创建的

type使用

使用type创建类时,接受三个参数:

  • 类名:'MyClass'

  • 基类元组:(object,)

  • 类属性字典:class_attrs,案例中为一个初始化方法和一个自定义方法

# 定义类属性和方法
class_attrs = {
    'greet': lambda self: f"Hello, my name is {self.name}",
    '__init__': lambda self, name: setattr(self, 'name', name)
}

# 动态创建类
MyClass = type('MyClass', (object,), class_attrs)

# 创建类的实例
instance = MyClass('Alice')

# 调用方法
print(instance.greet())  # 输出: Hello, my name is Alice

自定义元类

  1. 继承 type元类

  2. 重载一些方法,如 __new____init__,来修改类的创建过程

控制类的创建

  • 可以在类创建时进行一些自定义操作,比如添加属性或方法,修改现有的属性或方法等

  • 例如,可以在元类中自动为类添加某些方法或属性,以简化重复性工作

# 定义一个自定义元类
class MyMeta(type):
    def __new__(cls, name, bases, dct):
        dct['y'] = 10  # TODO:在类中添加一个属性
        return super().__new__(cls, name, bases, dct)

# 使用自定义元类创建类
class MyClass(metaclass=MyMeta):
    pass

# 实例化 MyClass
obj = MyClass()

print(obj.y)  # 输出:10

动态创建类

# 定义类属性和方法
class_attrs = {
    'greet': lambda self: f"Hello, my name is {self.name}",
    '__init__': lambda self, name: setattr(self, 'name', name)
}

# 动态创建类
MyClass = type('MyClass', (object,), class_attrs)

# 创建类的实例
instance = MyClass('Alice')

# 调用方法
print(instance.greet())  # 输出: Hello, my name is Alice

实现单例模式

使用元类来控制类的实例化过程,确保一个类只有一个实例

class SingletonMeta(type):
	_instances = {}

	def __call__(cls, args, *kwargs):
		if cls not in cls._instances:
			cls._instances[cls] = super().__call__(*args, **kwargs)
		return cls._instances[cls]

class SingletonClass(metaclass=SingletonMeta):
	pass

obj1 = SingletonClass()
obj2 = SingletonClass()

print(obj1 is obj2)  # 输出:True

可通过继承,实现多个类的元类自定义

# 这里的 MyType 是我们自定义的元类,元类的构建需要继承 type 类,使用__new__方法去定义元类的构造
# 其实原理就是基于继承和重写(继承父类 type 的__new__和__init__进行构造和初始化)

class MyType(type):

    def __new__(mcs, *args, **kwargs):
        # 创建"类"时调用, 可以在创建"类"时进行扩展
        print(f"我是元类构造方法,mcs的id为: {id(mcs)}")
        new_cls = super().__new__(mcs, *args, **kwargs)  # 这里调用的是父类的构造方法
        print(f"新创建的 new_class 的id为: {id(new_cls)}")
        return new_cls

    def __init__(cls, *args, **kwargs):
        # 创建"类"时调用, 可以在创建"类"时进行扩展
        print(f"我是初始化方法, cls的id为: {id(cls)}")
        super().__init__(*args, **kwargs)  # 这里用父类的方法的初始化方法


# Foo类实际一个类但同时也是MyType创建的对象: 创建类时调用元类的__new__方法来创建,__init__方法来初始化
class Foo(object, metaclass=MyType):
    pass
"""基于元类实现单例模式"""


class SingletonMeta(type):
    """自定义单例元类:继承type并重写type的构造方法,初始化方法,__call__方法"""

    def __init__(cls, *args, **kwargs):
        # 本质还是使用父类的初始化方法进行初始化,不过我们可以自定义一些操作,这里我们定义了一个类变量
        cls.__instance = None
        super().__init__(*args, **kwargs)

    def __new__(mcs, *args, **kwargs):
        # 本质还是使用父类的构造方法来构造,在构造前我们可以自定义一些操作,这里我们打印一句话
        print("先执行我来构造类")
        return super().__new__(mcs, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = super().__call__(*args, **kwargs)
        return cls.__instance


class President(metaclass=SingletonMeta):
    """先执行我们自定义的元类中的__new__和__init__构造和初始化这个类"""
    username = None

    def get_name(self):
        """Returns the name of the"""
        print(self.username)

    def set_name(self):
        """Set the name of the administration"""
        print(self.username)


# President类是 SingletonMeta 元类实例化出来的一个"对象",由上文可知"对象+()"即可执行 SingletonMeta 中定义的__call__方法
A = President()
B = President()

print(id(A))

print(id(B))

自动注册类

在元类中实现类的自动注册机制,这在需要自动管理多个类时非常有用

registry = {}


class RegisterMeta(type):

    def __new__(cls, name, bases, dct):
        new_class = super().__new__(cls, name, bases, dct)
        registry[name] = new_class
        return new_class


class BaseClass(metaclass=RegisterMeta):
    pass


class SubClass1(BaseClass):
    pass


class SubClass2(BaseClass):
    pass


print(registry)  # 输出:{'BaseClass': <class '__main__.BaseClass'>, 'SubClass1': <class '__main__.SubClass1'>, 'SubClass2': <class '__main__.SubClass2'>}

验证类定义

在类创建时验证类的属性和方法是否符合某些规则或约定

class ValidatingMeta(type):

    def __new__(cls, name, bases, dct):
        if 'required_method' not in dct:
            raise TypeError("Missing required method 'required_method'")

        return super().__new__(cls, name, bases, dct)


class MyClass(metaclass=ValidatingMeta):

    def required_method(self):
        pass  # 必须定义这个方法,否则会抛出异常

new、init、call

new & init

  1. new是在实例创建之前被调用的,用于创建实例,然后返回实例对象

  2. new至少有一个cls参数代表当前类,该参数在实例化时由Python解释器自动识别

  3. new必须要有返回值,返回的是实例化出来的实例(如果__new__没有有返回当前实例,__init__方法是不会被调用的)

class A:
    def __new__(cls, *args, **kwargs):
        print("A.__new__")
        return super().__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print("A.__init__")
        self.username = "zheng"
        self.password = "zheng"


a = A()
print(a.username, a.password)

# 输出
A.__new__
A.__init__
zheng zheng

基于__new__方法实现单例

class A:
    instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.instance:
            cls.instance = super().__new__(cls)
        return cls.instance

    def __init__(self, *args, **kwargs):
        print(f"{args[0]}.__init__")
        self.password = kwargs.get('password')


a = A("zheng", {"password": "123"})
b = A("xing", {"password": "456"})

print(">>>> ", a is b)
print(a.password)
print(b.password)

# 输出
zheng.__init__
xing.__init__
>>>>  True
None
None

call

call方法内部的执行流程是怎样的,为什么实例化一个对象时先执行__new__方法再执行__init__方法?

  • 元类的__call__会先调用type内的__new__方法创造一个空对象

  • 元类的__call__函数会接着调用type内的__init__方法初始化

  • 元类的__call__会返回一个初始化好的对象

class A(object):
	def __call__(self, *args, **kwargs):
        print("何时调用call方法")

# 类名+(): 实例化出来的一个对象,实际执行的是__new__和__init__方法,此时不调用类中定义的call方法
a = A()		# A中没有__new__和__init__就执行父类的__new__和__init__方法

# 对象+(): 此时调用才是A类中定义的__call__方法
a()			

元类中使用

"""测试元类"""


class MyType(type):
    """自定义Type类"""

    def __new__(mcs, *args, **kwargs):
        print(f"元类的构造方法, mcs的id为: {id(mcs)}")
        new_cls = super().__new__(mcs, *args, **kwargs)
        print(f"新构建类的new_cls的id为: {id(new_cls)}")
        return new_cls

    def __init__(cls, *args, **kwargs):
        print(f"元类的初始化方法, cls的id为: {id(cls)}")
        super().__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print(f"自定义__call__方法,啥都不干使用父类的__call__方法,注意看__call__方法的执行流, cls的id为{id(cls)}")
        return super().__call__(*args, **kwargs)  # 使用type中的_call__


class Foo(object, metaclass=MyType):
    """重写父类的new init方法"""

    def __init__(self, name):
        print(f"Foo类的初始化方法, self的id: {id(self)}")
        self.name = name

    def __new__(cls, *args, **kwargs):
        print(f"Foo类的构造方法,cls的id: {id(cls)}")
        new_obj = object.__new__(cls)
        print(f"新创建的对象new_obj的id为: {id(new_obj)}")
        return new_obj

    def __call__(self, *args, **kwargs):
        print("使用Foo实例化出来的对象的调用")

    def get(self):
        print("GET {}")

v1 = Foo('SB')
print(v1)
print(v1.name)
v1()

print(f"Foo类的id为: {id(Foo)}")
# 自定义元类
class MyType(type):
    # 可以把 类 当作对象来看,type就是类,来创建 类 这个对象的类
    def __new__(cls, *args, **kwargs):
        print("MyType new")
        return super().__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print("MyType init")
        super().__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        # 首先执行父类的 __call__方法,call方法中执行 __new__方法、__init__方法,返回一个实例对象
        print("MyType call")

        # 调用自身的 __new__方法
        new_obj = self.__new__(self)

        # 调用自身的 __init__方法
        self.__init__(new_obj, *args, **kwargs)

        return new_obj


# 使用自定义的type来创建类
# TODO: BB类 其实是 MyType类的 实例对象
class BB(object, metaclass=MyType):
    username = "zheng"

    def __new__(cls, *args, **kwargs):
        print("new BB")
        return super().__new__(cls)

    def __init__(self, username):
        print("init BB")
        self.username = username

    def __call__(self, *args, **kwargs):
        print("BB call")

    def create_user(self):
        print(self.username)


# 我们说 类是 type类的 实例对象,那么 BB(),就会执行 实例化类的 __call__方法,也就是type类的 __call__方法
B = BB("zheng")  # 调用 父类的 __call__方法
print(B.username)
B()  # B 是 BB类 的实例化对象,那么 B()调用的就是 实例化类-BB的 __call__方法

上述代码执行流程:

  1. MyType__new____init__ 方法在创建 BB 类时被调用。

  2. MyType__call__ 方法在实例化 BB 类时被调用。

  3. MyType.__call__ 方法中,BB 类的 __new__ 方法和 __init__ 方法依次被调用来创建实例。

  4. 打印实例的 username 属性。

  5. 调用实例的 __call__ 方法。

2
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区