术语元编程是指程序理解或操纵自身的潜力。Python支持一种称为元类的类元编程形式。元类是一个深奥的OOP概念,几乎隐藏在所有Python代码之后。无论您是否知道,它们都在被使用。在大多数情况下,您不需要意识到这一点。大多数Python程序员很少(如果有的话)必须考虑元类。然而,在需要时,Python提供了并非所有面向对象语言都支持的东西:您可以向下钻取和自定义元类。自定义元类的使用引起了一些争议,正如ZenofPython作者TimPeters所引用的:元类具有比99%的用户应该担心的更深的魔力。如果你想知道你是否需要它们,你不需要(真正需要它们的人肯定知道他们需要它们,并且不需要解释为什么)。”-TimPeters有些用户(Pythonista-众所周知的Python爱好者)认为你永远不应该使用自定义元类。这可能有点远,但很有可能不需要自定义元类。如果它没有明显的问题需要解决,那么如果问题以更简单的方式解决,它可能会更清晰,更具可读性。然而,理解Python元类是值得的,因为通过元类,Python类的内部可以是更好理解。也许有一天会出现一种情况,你只需要一个自定义元类来解决问题。旧式类与新式类在Python的世界中,类可以是两种变体之一。没有官方术语尚未解决,因此它们被非正式地称为对于旧式类,类和类型不是一回事。旧式类的实例总是由称为实例的单个内置类型实现。如果obj是一个实例旧式class,然后obj.__class__指定类,但type(obj)始终是实例。以下示例取自Python2.7:>>>classFoo:...pass...>>>x=Foo()>>>x.__class__>>>type(x)新类型新类型统一了类和类型的概念。如果obj是新式类的实例,则type(obj)与obj.__class__相同:>>>classFoo:...pass>>>obj=Foo()>>>obj.__class__>>>type(obj)>>>obj.__class__istype(obj)True>>>n=5>>>d={'x':1,'y':2}>>>classFoo:...pass...>>>x=Foo()>>>forobjin(n,d,x):...print(type(obj)是obj.__class__)...TrueTrueTrue类型类型和类别类在Python3中,所有类都是新式类。因此,在Python3中,可互换地引用对象的类型及其类是合理的。注意:在Python2中,类默认为旧式。在Python2.2之前,根本不支持新式类。从Python2.2开始,可以创建它们,但必须显式声明为new-style。请记住,在Python中,一切都是对象。类也是对象。因此,一个类必须有一个类型。什么是课程类型?考虑以下内容:>>>classFoo:...pass...>>>x=Foo()>>>type(x)>>>type(Foo)如您所料,typex是Foo类。但是Foo,类本身的类型就是类型。通常,任何新式类的类型都是类型。你熟悉的内置类的类型也是类型:>>>fortinint,float,dict,list,tuple:...print(type(t))...就此而言,typetype也是type(是的,确实如此):>>>type(type)typeis一个元类,其中的类是实例。正如普通对象是类的实例一样,Python中的任何新式类以及Python3中的任何类都是类型元类的实例。在上面的例子中:x是类Foo的一个实例。Foo是类型元类的一个实例。type也是type元类的一个实例,所以它也是它自己的一个实例。动态定义的类type()内置函数在传递参数时返回对象的类型。对于新式类,通常与对象的__class__属性相同:>>>type(3)>>>type(['foo','bar','baz'])>>>t=(1,2,3,4,5)>>>type(t)>>>classFoo:...pass...>>>type(foo())也可以用三个参数调用type(,,):指定类名。这成为类的__name__属性。指定要继承的基类的元组。这成为类的__bases__属性。指定包含类主体定义的命名空间字典。这成为类的__dict__属性。以这种方式调用type()会创建类型元类的一个新实例。换句话说,它动态地创建了一个新类。在以下每个示例中,最上面的代码段使用type()动态定义了一个类,而下面的代码段使用class语句以通常的方式定义了类。在每种情况下,这两个代码片段在功能上都是等效的。示例1在第一个示例中,传递给type()的和参数均为空。未指定从任何父类的继承,并且最初没有在名称空间字典中放置任何内容。这是最简单的类定义:>>>Foo=type('Foo',(),{})>>>x=Foo()>>>x<__main__.Fooobjectat0x04CFAD50>>>>classFoo:...pass...>>>x=Foo()>>>x<__main__.Fooobjectat0x0370AD50>Example2这里是一个元组,只有一个元素Foo,指定哪个Bar继承自father。属性attr最初放在命名空间字典中:>>>Bar=type('Bar',(Foo,),dict(attr=100))>>>x=Bar()>>>x.attr100>>>x.__class__>>x.__class__.__bases__(,)>>>classBar(Foo):...attr=100...>>>x=Bar()>>>x.attr100>>>x.__class__>>>x.__class__.__bases__(,)示例3这次,而是空的。通过参数将这两个对象放入命名空间字典中。第一个是名为attr的属性,第二个是名为attr_val的函数,它成为已定义类的方法:>>>Foo=type(...'Foo',...(),...{...'attr':100,...'attr_val':lambdax:x.attr...}...)>>>x=Foo()>>>x.attr100>>>x.attr_val()100>>>classFoo:...attr=100...defattr_val(self):...returnself.attr...>>>x=Foo()>>>x.attr100>>>x.attr_val()100例4lambda在Python中只能定义非常简单的函数。在下面的示例中,外部定义了一个稍微复杂的函数,然后attr_val将其按名称分配给命名空间字典中的f:>>>deff(obj):...print('attr=',obj.attr)...>>>Foo=type(...'Foo',...(),...{...'attr':100,...'attr_val':f...}...)>>>x=Foo()>>>x.attr100>>>x.attr_val()attr=100>>>deff(obj):...print('attr=',obj.attr)...>>>Foo类:...attr=100...attr_val=f...>>>x=Foo()>>>x.attr100>>>x.attr_val()attr=100自定义元类再次考虑这个老例子:>>>classFoo:...pass...>>>f=Foo()表达式Foo()创建了类Foo的一个新实例。当解释器遇到Foo()时,会发生以下情况:调用Foo的父类的__call__()方法。由于Foo是一个标准的新类型类,它的父类是类型元类,因此调用了类型的__call__()方法。__call__()方法依次调用以下方法:__new__()__init__()如果Foo没有定义__new__()和__init__(),则默认方法继承自Foo的祖先。但是,如果Foo确实定义了这些方法,它们将覆盖祖先中的方法,从而在实例化时允许Foo的自定义行为。下面,定义了一个自定义方法,并将new()指定为方法Foo的__new__():>>>defnew(cls):...x=object.__new__(cls)...x.attr=100...returnx...>>>Foo.__new__=new>>>f=Foo()>>>f.attr100>>>g=Foo()>>>g.attr100这修改了实例化行为classFoo:每次Foo创建一个实例时,默认情况下用一个名为attr的属性初始化,该属性的值为100。(此类代码通常会出现在__init__()方法中,而不是在__new__()方法中,这个例子是为演示目的而设计)现在,正如已经重申的那样,类也是对象。假设您希望在创建类似类时以类似的自定义方式完成Foo实例化行为。如果要遵循上述模式,则需要再次定义一个自定义方法并将其分配给__new__()作为类Foo的实例的方法。Foo是类型元类的一个实例,因此代码如下所示:#Spoileralert:Thisdoesn'twork!>>>defnew(cls):...x=type.__new__(cls)...x.attr=100...returnx...>>>type.__new__=newTraceback(最后一次调用):文件“”,第1行,在type.__new__=newTypeError:can'tsetattributesofbuilt-in/extensiontype'type'如您所见,除了__new__()方法不能重新分配元类类型。Python不允许这样做。这大概是一样的。type是所有新式类派生的元类。无论如何,你真的不应该搞砸这个。但是如果你想自定义类的实例化怎么办?一种可能的解决方案是自定义元类。本质上,您可以定义自己的从类型派生的元类,而不是定义类型元类,然后您可以使用该元类。第一步是定义一个派生自的元类,类型如下:>>>classMeta(type):...def__new__(cls,name,bases,dct):...x=super()。__new__(cls,name,bases,dct)...x.attr=100...returnx...定义类Meta(type):声明Meta派生自类型。既然type是一个元类,那么Meta也构成了一个元类。请注意,已经为自定义方法Meta定义了__new__()。没有办法直接在类型的元类上这样做。__new__()方法执行如下操作:super()的__new__()方法通过父元类(即type)的代理创建一个新类,为该类赋自定义属性attr,并返回一个值of100新创建的类现在,巫术的另一半:定义一个新类Foo并指定其元类是自定义元类Meta而不是标准元类类型。这是在类定义中使用元类关键字完成的,如下所示:>>>classFoo(metaclass=Meta):...pass...>>>Foo.attr100瞧!Foo获得了Meta元类的attr自动属性。当然任何其他类似定义的类都会做同样的事情:>>>classBar(metaclass=Meta):...pass...>>>classQux(metaclass=Meta):...pass...>>>Bar.attr,Qux.attr(100,100)与类作为创建对象的模板一样,元类作为创建类的模板。元类有时称为类工厂)。比较这两个例子:ObjectFactory:>>>classFoo:...def__init__(self):...self.attr=100...>>>x=Foo()>>>x.attr100>>>>y=Foo()>>>y.attr100>>>z=Foo()>>>z.attr100类工厂:>>>classMeta(type):...def__init__(...cls,名称、基础、dct...):...cls.attr=100...>>>X类(元类=元):...通过...>>>X.attr100>>>Y类(metaclass=Meta):...pass...>>>Y.attr100>>>classZ(metaclass=Meta):...pass...>>>Z.attr100这真的有必要吗?就像上面的类工厂示例一样简单,这就是元类工作原理的本质。它们允许自定义类的实例化方式。不过,attr为每个新创建的类提供自定义属性仍然很麻烦。你真的需要元类吗?在Python中,至少有一些其他方法可以有效地完成同一件事:简单继承:>>>classBase:...attr=100...>>>classX(Base):...pass。..>>>Y类(基础):...通过...>>>Z类(基础):...通过...>>>X.attr100>>>Y.attr100>>>Z.attr100类装饰器:>>>defdecorator(cls):...classNewClass(cls):...attr=100...returnNewClass...>>>@decorator...classX:...通过...>>>@decorator...Y类:...通过...>>>@decorator...Z类:...通过...>>>X.attr100>>>Y.attr100>>>Z.attr100结论正如TimPeters所建议的,元类很容易进入“从问题中找到解决方案”的领域。通常不需要创建自定义元类。如果手头的问题可以用更简单的方法解决,那么它应该是。不过,了解元类还是有好处的,这样您就可以对Python类有一个大概的了解,并且可以识别什么时候真正适合使用元类。?Python技巧?作者简介:JohnStutzAldenSantosDanBaderJoannaJablonski??Pythoning快乐!鸡蛋>>>导入这个