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
类,然后在子类中实现一些特殊方法:
__new__(cls, name, bases, attrs)
: 这个方法会在创建类时调用,它会接收三个参数,分别是类的名称、父类集合、以及属性字典。我们通常会在这个方法中动态地修改类的结构和行为,并最终返回一个新的类对象。__init__(self, name, bases, attrs)
: 这个方法也会在创建类时调用,它接收的参数和__new__
方法是一样的,但是它并不会返回任何东西。通常我们会在这个方法中做一些初始化工作。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__()
方法用于删除类的属性。
首先,在
MyClass
类中增加了一个静态属性y
。接着,在实例化时,不仅将属性
x
的值设置为 5,还将属性z
的值设置为 1。然后,获取属性
X
的值并打印输出,输出为 25,即5
的平方。接着,删除属性
X
并尝试获取已经删除的属性X
的值,因为该属性已经被删除,故会触发异常处理,输出 'X does not exist'。最后,分别使用
MyClass.__dict__
和my_instance.__dict__
打印类和实例的命名空间,并观察它们的差异。
评论