在定义类时,如果有一个您要共同执行的过程,则使用它。
Google的编码标准表示最好不要使用它,因为它会使它的可读性降低,因此我认为它是可以被强制学习到此程度的功能。
强大的功能
避免使用这些功能。
定义:
Python是一种非常灵活的语言,具有许多不错的(变形) 元类 ,字节码访问,快速编译,动态继承,对象育儿,导入hacks,反射,内部系统更改等。有各种功能。
1.元类
假设您要在定义类时显示Hello,world!。在这种情况下,请在元类中创建__init__。
class Meta(type): def __init__(self, name, bases, name_space): print('Hello, world!') class Cls(metaclass=Meta): pass >>> class Meta(type): ... def __init__(self, name, bases, name_space): ... print('Hello, world!') ... >>> class Cls(metaclass=Meta): ... pass ... Hello, world! >>> >>>
为什么这样做?
a和b是等效的。一旦您了解到a和b是等效的,就可以感觉到您了解元类。
# a. class Meta(type): def __init__(self, name, bases, name_space): print('Hello, world!') class Cls(metaclass=Meta): pass
# b. def init(self, name, bases, name_space): print('Hello, world!') Meta = type('Meta', (type, ), {'__init__': init}) Cls = Meta('Cls', (object, ), {})
Cls是从Meta实例化的类。看来以这种方式实例化一个类的类称为元类。在面向对象的编程中,元类是一个类,其实例是类。
当有一个参数时,类型函数用于确定类型,而在有三个参数时,类型函数用于创建一个类。参数如下。
Meta = type('Meta', (type, ), {'__init__': init}) Meta = type(‘类名称,要继承的类,类变量和方法’)
您可以看到,这里的要点仅是创建类所需的三件事:“类名”,“继承类”以及“类变量和方法”。
这里重要的是“类定义”和“通过从类型类实例化来创建类对象”是相同的。
# a, b 等于 # a. 类别定义 class Cls(object): pass # b. 实例化 Cls = type('Cls', (object, ) {})
因此,如果可以在类型类中重写__init__,则应该可以添加定义类时要执行的任何操作。
# Step1. 元类定义 class Meta(type): def __init__(meta, name, bases, name_space): print('Hello, world!') # Step2. 类别定义 Cls = Meta('Cls', (object, ), {})
当您执行它时,将显示Hello,wolrd!。
>>> Cls = Meta('Cls', (object, ), {}) Hello, world! >>>
但是Cls = Meta('Cls', (object, ), {})
,很难为类定义编写代码。因此,Python为元类提供了专用的语法。就是这样
class Meta(type): def __init__(self, name, bases, name_space): print('Hello, world!') class Cls(metaclass=Meta): pass
2.类装饰器
假设您要在定义类时显示Hello,world!。在这种情况下,请使用类装饰器。
def decorator(cls): print('Hello, world!') return cls @decorator class Cls(object): pass
>>> def decorator(cls): ... print('Hello, world!') ... return cls ... >>> @decorator ... class Cls(object): ... pass ... Hello, world! >>>
类装饰器比元类更容易编写。
3.正确使用
在Python 3.7中添加的数据类引入了修饰符,而不是元类。
PEP 557-数据类数据类
中不使用基类或元类。使用Data类的用户可以自由使用继承和元类,而无需从Data Class接收。装饰类是完全“普通”的Python类。使用数据类装饰器创建的类必须与普通类完全一样地使用。
数据类不使用任何基类或元类。这些类的用户可以自由使用继承和元类,而不会受到数据类的干扰。修饰的类是真正的“普通” Python类。数据类的修饰器不应干扰任何用法。
PEP 3129-
最初是从Python 2.4讨论类装饰器函数装饰器时,由于它的元类而被认为是晦涩且不必要的。在发布了Python 2.4的一些版本并且对函数装饰器有所了解,以及对更多实际使用案例的多年实践经验之后,BDFL和Python社区将重新评估类装饰器,并将其包含在Python 3.0中。推荐的。
当最初对函数装饰器是否包含在Python 2.4中进行辩论时,由于元类,类装饰器被认为是晦涩且不必要的[1]。在使用Python 2.4.x系列发行版几年的经验以及对函数装饰器及其使用的日益熟悉之后用途,BDFL和社区重新评估了类装饰器,并建议将其包含在Python 3.0中。
基于组合而不是继承的思想,如果可以使用类装饰器,请使用类装饰器,如果不继承,则仅在严重时才使用元类。
4.实际例子
1.按原样使用namedtuple
使用namedtuple创建一个创建不可变对象的类。它直接继承了namedtuple创建的类。
import collections class Region(collections.namedtuple('ImmutableRegion', ('x1', 'y1', 'x2', 'y2', 'is_rectangle', 'is_line', 'is_dot'))): def __new__(cls, x1, y1, x2, y2): # point 1. 不是使用init,而是使用new。。 width_0 = (x1 - x2 == 0) height_0 = (y1 - y2 == 0) is_rectangle = (not width_0 and not height_0) # 0 0 is_line = (width_0 != height_0) # 0 1 or 1 0; is_dot = (width_0 and height_0) # 1 1 args = (x1, y1, x2, y2, is_rectangle, is_line, is_dot) # point 2. 即使所需对象已对齐 # 生成对象。 self = super().__new__(cls, *args) # point 3. 生返回生成的对象 return return self Region(0, 0, 1, 1)
2.通过类装饰器使用namedtuple
看起来像是1的语法suger。虽然超级感到遗憾。我一直在寻找解决方法,但是似乎无法直接编写super()。
@immutable( 'x1', 'y1', 'x2', 'y2', 'is_rectangle', 'is_line', 'is_dot') class Region: # point 2. 不是使用init new。 # 在产生对象之前调用。 def __new__(cls, x1, y1, x2, y2): width_0 = (x1 - x2 == 0) height_0 = (y1 - y2 == 0) is_rectangle = (not width_0 and not height_0) # 0 0 is_line = (width_0 != height_0) # 0 1 or 1 0; xor is_dot = (width_0 and height_0) # 1 1 args = (x1, y1, x2, y2, is_rectangle, is_line, is_dot) # point 3. 即使所需对象已对齐 # 生成对象 self = super(Region, cls).__new__(cls, *args) # point 4. 返回生成的对象 return self """ #请注意 #如果使用了装饰物的话,就不能用省略记法来写 self = super().__new__(cls, *args)
"""
元类本身的应用范围似乎很广。我只能理解“在定义类时有一个我想执行的通用过程时才使用它”。
元类具有无限的潜在效用价值。已经尝试过的想法包括枚举,日志记录,接口检查,自动委派,自动属性生成,代理,框架和自动资源锁定/同步。
3.3.3.6。元类示例-Python语言参考