python 元类

在 Python 中,元类(metaclass)是一种特殊的类,用于创建其他类。它们可以用于控制、定制和修改类的行为,以及实现一些高级的编程技巧。元类是 Python 面向对象编程中非常重要的概念之一,理解元类可以帮助程序员更好地掌握和应用 Python 的面向对象编程思想。

在 Python 中,默认的元类是 type 类,它是所有新式类的默认元类。当我们定义一个类时,Python 解释器会自动使用 type 类来创建这个类。不过,我们也可以自定义元类,通过继承 type 类并重写其一些特殊方法来实现。例如,我们可以定义一个元类,来限制某个类的属性或方法的访问权限,或者自动给某个类增加一些属性和方法等等。

元类的使用需要比较高级的编程技巧和经验,并且不是在所有情况下都需要使用。但理解元类对于深入掌握 Python 编程语言和面向对象编程思想是非常有帮助的。

type的类型是什么?

  • type的类型也是type

什么是元类?

  • 用来创建类的类,叫做元类

  • Python中的任何新式类以及Python 3中的任何类都是type元类的一个实例。函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类

注意区分元类和继承的基类:

  • type: 是元类,所有的类都是通过type所创建出来的

  • object:顶层的基类,所有类的继承顶层父类都是object

class OneClass:
    pass


obj1 = OneClass()
print(obj1.__class__) # 返回表示 obj1 所属类的类型对象
print(type(obj1))

# python中一切皆对象
# python中的类的类型是type(元类)
print(OneClass.__class__)
print(type(OneClass))

# 输出
<class '__main__.OneClass'>
<class '__main__.OneClass'>
<class 'type'>
<class 'type'>

这段代码定义了一个名为 OneClass 的空类,然后通过该类创建了一个对象 obj1。接着,代码分别使用 obj1.class 和 type(obj1) 打印出 obj1 的类型信息,结果都是表示 OneClass 类的类型对象。

紧接着,代码又分别使用 OneClass.class 和 type(OneClass) 打印出 OneClass 类的类型信息,结果都是 type 类型,也就是元类。

解释一下:

  • OneClass 是一个 Python 类,是由 type 类创建的,所以它的类型是 type 类型。

  • obj1 是 OneClass 类的一个实例对象,它的类型是 OneClass 类型,也就是表示 OneClass 的类型对象。

  • 通过 obj1.class 和 type(obj1) 都可以获得 obj1 的类型信息。

  • 通过 OneClass.class 和 type(OneClass) 都可以获得 OneClass 类的类型信息,即元类 type。

type() 函数的使用场景

获取对象的类型信息:

可以使用 type() 函数来获取一个对象的类型信息,例如:

a = 10
print(type(a))  # <class 'int'>
class OneClass(object):
    attr1 = 'abc'
    attr2 = 100


print(type(OneClass))  # <class 'type'>

动态创建类

type() 函数还可以用于动态地创建一个类对象。在 Python 中,类也是一种对象,我们可以使用 type() 来动态创建类,例如:

# 动态创建一个类
TwoClass = type('TwoClass', (object,), {'attr1': 'abc', 'attr2': 100,
                                        'my_attribute': 42,
                                        'my_method': lambda self: self.my_attribute})
print(TwoClass.__dict__)
obj = TwoClass()
print(obj.my_method())  # 输出 42

# {'attr1': 'abc', 'attr2': 100, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'TwoClass' objects>, '__weakref__': <attribute '__weakref__' of 'TwoClass' objects>, '__doc__': None}
42

使用 type() 函数创建一个类需要三个参数,分别为:

  • 类名:表示要创建的类的名称,是一个字符串。

  • 父类集合:表示要创建的类继承自哪些父类。通常,我们将父类放在一个元组中,即 (Parent1, Parent2, ...)。

  • 属性字典:表示要创建的类的属性和方法集合。这个字典包含了类的所有属性和方法,其中每个键值对的键表示属性或方法的名称,值则表示属性或方法的值。

自定义元类

在 Python 中,类也是对象,因此我们可以通过使用元类(metaclass)来创建类。元类是用于创建类的类,它定义了类的构造方法、属性等特性。当程序员编写一个新类时,实际上是创建了该类的一个实例,该实例是由 Python 的内置元类 type() 创建的。即默认的元类是 type()。

使用元类可以动态地创建类,并在创建类时自定义其行为。例如,我们可以通过元类来对类的定义进行拦截和修改,或者实现自定义的属性描述符、类装饰器等高级功能。

要自定义一个元类,可以继承 type 类,然后在子类中实现一些特殊方法:

  1. __new__(cls, name, bases, attrs): 这个方法会在创建类时调用,它会接收三个参数,分别是类的名称、父类集合、以及属性字典。我们通常会在这个方法中动态地修改类的结构和行为,并最终返回一个新的类对象。

  2. __init__(self, name, bases, attrs): 这个方法也会在创建类时调用,它接收的参数和 __new__ 方法是一样的,但是它并不会返回任何东西。通常我们会在这个方法中做一些初始化工作。

  3. MyMetaClass 是自定义的元类,Python 在创建 MyClass 时会调用 MyMetaClass__new__() 方法来创建类。在 __new__() 方法中,我们可以对类进行拦截和修改,以实现自定义功能。

在类中,__new__() 方法会在实例化对象时被调用,它的作用是创建并返回一个新的对象实例。通常情况下,__new__() 方法不需要被定义或重写,因为 Python 会默认提供一个实现。但是,如果你想控制对象创建的过程,你可以重写这个方法。

元类是用于创建类的类,它们通常用于实现高级的类行为和元编程。在元类中,__new__() 方法会在创建类时被调用,它的作用是创建并返回一个新的类对象。

from typing import Any, Dict


class MyMetaClass(type):
    # 用于创建类,将属性名转为大写
    def __new__(cls, name, bases, attrs: Dict[str, Any]):
        uppercase_attr = {}
        for attr_name, attr_value in attrs.items():
            if not callable(attr_value) and not attr_name.startswith('__'):
                uppercase_attr[attr_name.upper()] = attr_value
            else:
                uppercase_attr[attr_name] = attr_value

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


class MyClass(metaclass=MyMetaClass):
    y = 10

    # 获取不存在的属性时,返回提示
    def __getattr__(self, name):
        raise AttributeError(f'{name} does not exist')

    # 将属性值进行平方\开根和倒数操作操作
    def __setattr__(self, name, value):
        if value == 0:
            super().__setattr__(name.upper(), 0)
        else:
            super().__setattr__(name.upper(), ((value ** 2) ** 0.5) ** -1)


    def __delattr__(self, name):
        super().__delattr__(name)


# 创建 MyClass 的实例
my_instance = MyClass()

# 设置属性 x 的值为 5
my_instance.x = 5
# 设置属性 z 的值为 1
my_instance.z = 1

# 获取属性 X 的值
print(my_instance.X)  # 输出 25,即 5 的平方

# 删除属性 X
del my_instance.X

# 尝试获取已经删除的属性 X 的值
try:
    print(my_instance.X)
except AttributeError as e:
    print(str(e))  # 输出 'X does not exist'

print(MyClass.__dict__)
print(my_instance.__dict__)



# 输出
0.2
X does not exist
{'__module__': '__main__', 'Y': 10, '__getattr__': <function MyClass.__getattr__ at 0x00000208A4B76438>, '__setattr__': <function MyClass.__setattr__ at 0x00000208A4CC7708>, '__delattr__': <function MyClass.__delattr__ at 0x00000208A4CC7B88>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
{'Z': 1.0}

这段代码是在上一段代码的基础上进行了一些修改,主要分为以下几个部分:

MyMetaClass 中,定义了一个 __new__() 方法,该方法在创建新类时被调用,它首先对类属性进行遍历,将除特殊方法和函数以外的属性名全部转换成大写形式,并加入到新的字典 uppercase_attr 中,最后返回具有大写属性名的类。

MyClass 中定义了特殊方法 __getattr__(), __setattr__()__delattr__(), 用于访问、设置和删除类中的属性。其中,__getattr__() 方法用于在获取不存在的属性时返回提示;__setattr__() 方法用于将属性值进行平方操作;__delattr__() 方法用于删除类的属性。

  1. 首先,在 MyClass 类中增加了一个静态属性 y

  2. 接着,在实例化时,不仅将属性 x 的值设置为 5,还将属性 z 的值设置为 1。

  3. 然后,获取属性 X 的值并打印输出,输出为 25,即 5 的平方。

  4. 接着,删除属性 X 并尝试获取已经删除的属性 X 的值,因为该属性已经被删除,故会触发异常处理,输出 'X does not exist'。

  5. 最后,分别使用 MyClass.__dict__my_instance.__dict__ 打印类和实例的命名空间,并观察它们的差异。