当前位置: 首页 > 后端技术 > Python

Python中的元类到底是什么?这篇文章可能是最清晰的

时间:2023-03-25 23:11:36 Python

类作为对象在理解元类之前,你需要掌握Python类。Python从Smalltalk语言中借用了一个非常具体的类概念。在大多数语言中,类只是描述如何生成对象的一段代码。在Python中也是如此:>>>classObjectCreator(object):...pass...>>>my_object=ObjectCreator()>>>print(my_object)<__main__.ObjectCreatorobjectat0x8974f2c>但是Python类更甚。在Python中,Python类也是对象。是的,它也是一个对象。一旦使用关键字class,Python就会执行它并创建一个对象。示例代码:>>>classObjectCreator(object):...pass...上面的代码在内存中创建了一个名为“ObjectCreator”的对象。这个对象(类)本身具有创建对象(实例)的能力,所以它也是一个类。但是,它仍然是一个对象,因为:您可以将它分配给一个变量您可以复制它您可以向它添加属性您可以将它作为函数参数传递例如:>>>print(ObjectCreator)#你可以打印一个类,因为它是一个对象>>>defecho(o):...print(o)...>>>echo(ObjectCreator)#classescanbepassedasparameters>>>print(hasattr(ObjectCreator,'new_attribute'))False>>>ObjectCreator.new_attribute='foo'#可以给类添加属性>>>print(hasattr(ObjectCreator,'new_attribute'))True>>>print(ObjectCreator.new_attribute)foo>>>ObjectCreatorMirror=ObjectCreator#可以为变量指定一个类>>>print(ObjectCreatorMirror.new_attribute)foo>>>print(ObjectCreatorMirror())<__main__.ObjectCreatorobjectat0x8997b4c>动态创建类由于类是对象,您可以像创建任何对象一样即时创建它们。首先,您可以使用类在函数内创建类:>>>defchoose_class(name):...ifname=='foo':...classFoo(object):...pass...returnFoo#返回类,而不是实例...else:...classBar(object):...pass...returnBar...>>>MyClass=choose_class('foo')>>>print(MyClass)#函数返回一个类,而不是一个实例>>>print(MyClass())#你可以从这个类创建一个对象<__main__.Fooobjectat0x89c6d4c>但这不是如此动态,因为您仍然必须自己编写整个课程。由于类是对象,因此它们必须由某些东西生成。Python在使用class关键字时自动创建这个对象。然而,与Python中的大多数事情一样,它为您提供了一种手动完成的方法。还记得函数类型吗?这个函数让你知道对象的类型:>>>print(type(1))>>>print(type("1"))>>>print(type(ObjectCreator))>>>print(type(ObjectCreator()))嗯,type有完全不同的功能,它还可以动态创建类。type可以将类描述作为参数并返回一个类。(我知道同一个函数可以根据传递给它的参数有两种完全不同的用途是很愚蠢的。这是Python向后兼容的问题)typeusage:type(name,bases,attrs)parameters:name:类名bases:父类的元组(为了继承,可以为空)attrs:包含属性名称的字典和值示例:>>>classMyShinyClass(object):...pass可以手动创建:>>>MyShinyClass=type('MyShinyClass',(),{})#returnsaclassobject>>>print(MyShinyClass)>>>print(MyShinyClass())#创建类的实例<__main__.MyShinyClassobjectat0x8997cec>你会注意到我们使用“MyShinyClass”作为类的名称类和变量来保存类引用。type接受字典来定义类的属性。所以:>>>classFoo(object):...bar=True可以转换为:>>>Foo=type('Foo',(),{'bar':True})并用作普通类:>>>>print(Foo)>>>print(Foo.bar)True>>>f=Foo()>>>print(f)<__main__.Fooobjectat0x8a9b84c>>>>print(f.bar)True当然你可以继承它,所以:>>>classFooChild(Foo):...pass将是:>>>FooChild=type('FooChild',(Foo,),{})>>>print(FooChild)>>>print(FooChild.bar)#bar继承自FooTrue最后,你需要给你的类添加方法。只需定义一个具有适当签名的函数并将其分配为属性即可。>>>defecho_bar(self):...print(self.bar)...>>>FooChild=type('FooChild',(Foo,),{'echo_bar':echo_bar})>>>hasattr(foo,'echo_bar')False>>>hasattr(FooChild,'echo_bar')True>>>my_foo=FooChild()>>>my_foo.echo_bar()True动态创建类后,可以添加更多的方法,只是就像向正常创建的类对象添加方法一样。>>>defecho_bar_more(self):...print('yetanothermethod')...>>>FooChild.echo_bar_more=echo_bar_more>>>hasattr(FooChild,'echo_bar_more')True最终你会看到我们想要它说的是:在Python中,类是对象,您可以动态地动态创建类。这就是Python在使用关键字class时所做的,它是通过使用元类来实现的。什么是元类(最终)元类是创建类的“事物”。您定义类来创建对象,对吗?但是我们了解到Python类是对象。嗯,元类是创建这些对象的原因。它们是类的类,您可以通过以下方式描绘它们:MyClass=MetaClass()my_object=MyClass()您已经看到,类型您可以这样做:MyClass=type('MyClass',(),{})这是因为函数类型实际上是一个元类。type是Python用来在幕后创建所有类的元类。现在,您想知道为什么是小写而不是小写Type?嗯,我猜和str创建字符串对象int的类和创建整数对象的类的一致性有关。类型只是创建类对象的类。您可以通过检查__class__属性看到这一点。一切,我的意思是一切,在Python中都是一个对象。这些包括整数、字符串、函数和类。它们都是对象。所有这些都是从一个类创建的:>>>age=35>>>age.__class__>>>name='bob'>>>name.__class__>>>deffoo():pass>>>foo.__class__>>>classBar(object):pass>>>b=Bar()>>>b.__class__现在,__class__和__class__是什么?>>>age.__class__.__class__>>>name.__class__.__class__>>>foo.__class__.__class__>>>b.__class__.__class__所以元类只是创建类对象的东西。如果你愿意,可以称它为“类工厂”。type是Python使用的内置元类,但您当然可以创建自己的元类。__metaclass__属性在Python2中,您可以在编写类时添加一个__metaclass__属性(有关Python3语法,请参阅下一节):classFoo(object):__metaclass__=something...[...]如果这样做,Python将使用元类来创建类Foo。小心,这很棘手。你先写classFoo(object),但是内存中还没有创建Foo的类对象。Python在类定义中查找__metaclass__。如果找到它,它会使用它来创建对象类Foo。如果不是,它使用类型来创建类。多读几遍。当你这样做时:classFoo(Bar):passPython会做类似的事情:Foo中是否有__metaclass__属性?如果是这样,请在内存中创建一个类对象(我说的是类对象,请留在此处),使用__metaclass__命名为Foo。如果Python找不到__metaclass__,它会在MODULE级别寻找__metaclass__,并尝试做同样的事情(但仅限于不继承任何东西的类,基本上是老式的类)。然后,如果它根本找不到任何对象__metaclass__,它会使用Bar(第一个父级)自己的元类(可能是默认值类型)创建类对象。请注意,__metaclass__属性不会被继承,父类(Bar.__class__)的元类将被继承。如果Bar使用通过(而不是)__metaclass__创建的属性,子类将不会继承该行为。Bar`type()`type.__new__()现在最大的问题是,你可以在__metaclass__中输入什么?答案是:可以创建类的东西。什么可以创建类?类型,或继承自或使用它的任何东西。Python3中的元类设置元类的语法在Python3中发生了变化:classFoo(object,metaclass=something):...也就是说,不再使用__metaclass__属性,而是在基类列表中使用关键字范围。然而,元类的行为在很大程度上保持不变。在python3中添加到元类的其中一件事是您还可以将属性作为关键字参数传递给元类,如下所示:classFoo(object,metaclass=something,kwarg1=value1,kwarg2=value2):...why使用元类?现在这是一个大问题。为什么要使用一些晦涩难懂的容易出错的函数?好吧,通常你不需要:元类是更深层次的魔法,99%的用户永远不必担心。如果你想知道你是否需要它们,你不需要(真正需要它们的人肯定知道他们需要它们,并且不需要解释为什么)。Python大师TimPeters的元类的主要用例是创建API。一个典型的例子是DjangoORM。它允许你定义这样的东西:classPerson(models.Model):name=models.CharField(max_length=30)age=models.IntegerField()但是如果你这样做:='35')print(person.age)它不会返回IntegerField对象。它会返回int,你甚至可以直接从数据库中获取它。这是可能的,因为models.Model定义了__metaclass__并使用了一些魔法,可以将您用简单语句定义的Person对象变成一个复杂的数据库字段挂钩。Django通过公开一个简单的API并使用元类从该API重新创建代码来完成幕后的实际工作,从而使看似复杂的事情变得简单。最后一点首先,您知道类是可以从中创建实例的对象。事实上,类本身就是元类的实例。>>>classFoo(object):pass>>>id(Foo)在Python中一切都是对象,它们都是类的实例或者元类的实例。除了类型。type实际上是它自己的元类。其次,元类很复杂。您可能不想将它们用于非常简单的类更改。您可以使用两种不同的技术来更改类:猴子修补类装饰器99%的时间您需要更改类,最好使用这些。但是98%的时间你根本不需要改变类。