天津建设网站安全员考试查询温州网站改版
web/
2025/9/27 0:19:14/
文章来源:
天津建设网站安全员考试查询,温州网站改版,自贡市住房和城乡建设局网站,用360打开自己做的网站有广告Python元类#xff1a;metaclass 1、类也是对象2、什么是元类3、__metaclass__属性4、自定义元类5、为什么要用metaclass类而不是函数6、究竟为什么要使用元类7、结语 声明#xff1a; 本文主要参考文章#xff1a;https://stackoverflow.com/questions/100003/what-are-met… Python元类metaclass 1、类也是对象2、什么是元类3、__metaclass__属性4、自定义元类5、为什么要用metaclass类而不是函数6、究竟为什么要使用元类7、结语 声明 本文主要参考文章https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python 尊重原创如有侵权请联系删除 1、类也是对象 所有对象都是实例化或者调用类而得到的Python中一切都是对象通过class关键字定义的类本质也是对象对象又是通过调用类得到的因此通过class关键字定义的类肯定也是调用了一个类得到的这个类就是元类。type就是Python内置的元类
在理解元类之前你需要先掌握Python中的类。Python中类的概念借鉴于Smalltalk语言这显得有些奇特。在大多数编程语言中类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立
class ObjectCreator(object):passobj ObjectCreator()
print(obj) # __main__.ObjectCreator object at 0x0000021098A4AFB0但是Python中的类还远不止如此。类同样也是一种对象。是的没错就是对象。只要你使用关键字classPython解释器在执行的时候就会创建一个对象
例如上面class代码段将在内存中创建对象ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力而这就是它为什么是类也是对象的原因
但是它本质上仍然是一个对象于是乎你可以对它做如下操作
1你可以将它赋值给一个变量2你可以拷贝它3你可以为它增加属性4你可以将它作为函数参数进行传递
下面是示例
# 你可以打印一个类因为它就是一个对象
print(ObjectCreator) # class __main__.ObjectCreator
# 你可以将类作为参数传给函数
def echo(o):print(o)
echo(ObjectCreator) # class __main__.ObjectCreator
# 你可以为类增加属性
ObjectCreator.field value
print(hasattr(ObjectCreator, field)) # True
print(ObjectCreator.field) # value
# 你可以将类复制给一个变量
var ObjectCreator
print(var()) # __main__.ObjectCreator object at 0x0000026AF00EABF0动态地创建类
因为类也是对象你可以在运行时动态的创建它们就像其他任何对象一样。首先你可以在函数中创建类使用class关键字即可
def choose_class(name):match name:case stu:class Stu(object):passreturn Stucase emp:class Emp(object):passreturn Empstu choose_class(stu)
# 返回类而不是类的实例
print(stu) # class __main__.choose_class.locals.Stu
# 可以通过这个类创建类的实例类对象
print(stu()) # __main__.choose_class.locals.Stu object at 0x000001D7E6D9AAA0但这还不够动态因为你仍然需要自己编写整个类的代码。由于类也是对象所以它们应该也是通过什么东西来生成的才对。当你使用class关键字时Python解释器自动创建这个对象。但就和Python中的大多数事情一样Python仍然提供给你手动处理的方法
还记得内建函数type()吗这个古老但强大的函数能够让你知道一个对象的类型是什么就像这样
print(type(0)) # class int
print(type(0)) # class str
print(type(ObjectCreator)) # class type
print(type(ObjectCreator())) # class __main__.ObjectCreator这里type有一种完全不同的能力它也能动态的创建类。type可以接受一个类的描述作为参数然后返回一个类
我知道根据传入参数的不同同一个函数拥有两种完全不同的用法是一件很愚蠢的事情但这在Python中是为了保持向后兼容性
type可以像这样工作 type(name, bases, attrs)
name类的名称 bases父类用于继承元组类型可为空 attrs包含属性名称和属性值的字典比如下面的代码
class MyShinyClass(object):pass# 可以手动像这样创建
MyShinyClass type(MyShinyClass, (), {})
# 返回类对象
print(MyShinyClass) # class __main__.MyShinyClass
# 创建该类的实例
print(MyShinyClass()) # __main__.MyShinyClass object at 0x0000018E2F0FAAA0你会发现我们使用MyShinyClass作为类名并且也可以把它当做一个变量来作为类的引用。类和变量是不同的这里没有任何理由把事情弄的复杂
type接受一个字典来为类定义属性因此
class Foo(object):flag True可以翻译为
Foo type(Foo, (), {flag: True})并且可以将Foo当成一个普通的类一样使用
print(Foo) # class __main__.Foo
print(Foo.flag) # True
foo Foo()
print(foo) # __main__.Foo object at 0x00000203B4A4AA70
print(foo.flag) # True当然你可以向这个类继承
class FooChild(Foo):pass就可以写成
FooChild type(FooChild, (Foo,), {})print(FooChild) # class __main__.FooChild
# flag属性是由继承而来的
print(FooChild.flag) # True最终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了
def echo_flag(self):print(self.flag)FooChild type(FooChild, (Foo,), {echo_flag: echo_flag})
print(hasattr(Foo, echo_flag)) # False
print(hasattr(FooChild, echo_flag)) # True
child FooChild()
child.echo_flag() # True你可以看到在Python中类也是对象你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情而这就是通过元类来实现的
2、什么是元类 元类就是用来创建类的东西。你创建类就是为了创建类的实例对象不是吗但是我们已经知道Python中的类也是对象。好吧元类就是用来创建这些类对象的元类就是类的类你可以这样理解 MyClass MetaClass()
MyObject MyClass()你已经看到了type可以让你像这样做
MyClass type(MyClass, (), {})这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢好吧我猜这是为了和str保持一致性str是用来创建字符串对象的类而int是用来创建整数对象的类
type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西注意我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象而且它们都是从一个类创建而来
age 18
print(age.__class__) # class int
name Tom
print(name.__class__) # class str
def method(): pass
print(method.__class__) # class function
class Bar(object): pass
bar Bar()
print(bar.__class__) # class __main__.Bar那么对于任何一个__class__的__class__属性又是什么呢
print(age.__class__.__class__) # class type
print(name.__class__.__class__) # class type
print(method.__class__.__class__) # class type
print(bar.__class__.__class__) # class type因此元类就是创建类这种对象的东西。如果你喜欢的话可以把元类称为类工厂不要和工厂类搞混了type就是Python的内建元类当然了你也可以创建自己的元类
3、__metaclass__属性 你可以在写一个类的时候为其添加__metaclass__属性
class Foo(object):__metaclass__ something如果你这么做了Python就会用元类来创建类Foo。小心点这里面有些技巧。你首先写下class Foo(object)但是类对象Foo还没有在内存中创建
Python会在类的定义中寻找__metaclass__属性如果找到了Python就会用它来创建类Foo如果没有找到就会用内建的type来创建这个类
class Foo(Bar):pass当你写该代码时Python做了如下的操作
Foo中有__metaclass__这个属性吗如果是Python会在内存中通过__metaclass__创建一个名字为Foo的类对象。如果Python没有找到__metaclass__它会继续在Bar父类中寻找__metaclass__属性并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__它就会在模块层次中去寻找__metaclass__并尝试做同样的操作如果还是找不到__metaclass__Python就会用内置的type来创建这个类对象
现在的问题就是你可以在__metaclass__中放置些什么代码呢答案就是可以创建一个类的东西。那么什么可以用来创建一个类呢type或任何使用到type或子类化type的东西都可以
Python3中的元类
在Python3中设置元类的语法已经更改
class Foo(object, metaclasssomething):pass即不再使用metaclass属性而是在基类列表中使用__metaclass__关键字参数。然而元类的行为基本保持不变
4、自定义元类 元类的主要目的就是为了当创建类时能够自动地改变类。通常你会为API做这样的事情你希望可以创建符合当前上下文的类。假想一个很愚蠢的例子你决定在你的模块里所有的类的属性都应该是大写形式
有好几种方法可以办到但其中一种就是通过在模块级别设定__metaclass__。采用这种方法这个模块中的所有类都会通过这个元类来创建我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了
幸运的是__metaclass__实际上可以被任意调用它并不需要是一个正式的类。所以我们这里就先以一个简单的函数作为例子开始
# 元类会自动将你通常传给type的参数作为自己的参数传入
def upper_attr(future_class_name, future_class_parents, future_class_attrs):返回一个类对象将属性全部转为大写形式# 选择所有不以__开头的属性私有属性将它们转为大写形式uppercase_attrs {attr if attr.startswith(__) else attr.upper(): vfor attr, v in future_class_attrs.items()}# 通过type来做类对象的创建return type(future_class_name, future_class_parents, uppercase_attrs)# 这会作用到这个模块中的所有类
__metaclass__ upper_attr# 需要注意的是全局__metaclass__将不能与object一起工作
class Foo():# 我们也可以只在这里定义__metaclass__这样就只会作用于这个类中# __metaclass__ upper_attrbar bipprint(hasattr(Foo, bar)) # True
print(hasattr(Foo, BAR)) # False
foo Foo()
print(foo.bar) # bip注意此处存在问题结果与预期相反原因未知有人知道什么原因吗 现在让我们做完全相同的事情但对元类使用一个真正的类
# 请记住type实际上是一个类就像str和int一样所以你可以从type继承
class UpperAttrMetaClass(type):# __new__是在__init__之前被调用的特殊方法是用来创建对象并返回的方法# 而__init__只是用来将传入的参数初始化给对象你很少用到__new__除非你希望能够控制对象的创建# 这里创建的对象是类我们希望能够自定义它所以我们这里改写__new__# 如果你希望的话你也可以在__init__中做些事情还有一些高级的用法会涉及到改写__call__特殊方法但是我们这里不用def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attrs):uppercase_attrs {attr if attr.startswith(__) else attr.upper(): vfor attr, v in future_class_attrs.items()}return type(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attrs)但是这种方式其实不是OOP。我们直接调用了type而且我们没有改写父类的__new__方法。现在让我们这样去处理
class UpperAttrMetaClass(type):def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attrs):uppercase_attrs {attr if attr.startswith(__) else attr.upper(): vfor attr, v in future_class_attrs.items()}return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attrs)你可能已经注意到了有个额外的参数upperattr_metaclass这并没有什么特别的。类方法的第一个参数总是表示当前的实例就像在普通的类方法中的self参数一样
当然了为了清晰起见这里的名字我起的比较长。但是就像self一样所有的参数都有它们的传统名称。因此在真实的产品代码中一个元类应该是像这样的
class UpperAttrMetaclass(type):def __new__(cls, clsname, bases, attrs):uppercase_attrs {attr if attr.startswith(__) else attr.upper(): vfor attr, v in attrs.items()}return type.__new__(cls, clsname, bases, uppercase_attrs)如果使用super方法的话我们还可以使它变得更清晰一些这会简化继承你可以拥有元类从元类继承从type继承
class UpperAttrMetaclass(type):def __new__(cls, clsname, bases, attrs):uppercase_attrs {attr if attr.startswith(__) else attr.upper(): vfor attr, v in attrs.items()}return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attrs)class Foo(object, metaclassUpperAttrMetaclass):bar bipprint(hasattr(Foo, bar)) # False
print(hasattr(Foo, BAR)) # True
foo Foo()
print(foo.BAR) # bip在Python3中如果你使用关键字参数进行调用如下所示
class Foo(object, metaclassMyMetaclass, kwargdefault):pass它在元类中可转换为
class MyMetaclass(type):def __new__(cls, clsname, bases, dct, kwargsdefault):pass就是这样除此之外关于元类真的没有别的可说的了。使用到元类的代码比较复杂这背后的原因倒并不是因为元类本身而是因为你通常会使用元类去做一些晦涩的事情依赖于自省控制继承等
确实用元类来搞些“黑暗魔法”是特别有用的因而会搞出些复杂的东西来。但就元类本身而言它们其实是很简单的
1拦截类的创建2修改类3返回修改之后的类
5、为什么要用metaclass类而不是函数 由于__metaclass__可以接受任何可调用的对象那为何还要使用类呢因为很显然使用类会更加复杂啊这样做有以下几个原因
1意图会更加清晰。当你读到UpperAttrMetaclass(type)时你知道接下来要发生什么2你可以使用OOP编程。元类可以从元类中继承而来改写父类的方法。元类甚至还可以使用元类3你可以把代码组织的更好。当你使用元类的时候肯定不会是像我上面举的这种简单场景通常都是针对比较复杂的问题。将多个方法归总到一个类中会很有帮助也会使得代码更容易阅读4你可以使用__new__、__init__以及__call__这样的特殊方法。它们能帮你处理不同的任务。就算通常你可以把所有的东西都在__new__里处理掉有些人还是觉得用__init__更舒服些5哇哦这东西的名字是metaclass肯定非善类我要小心
6、究竟为什么要使用元类 现在回到我们的主题上来究竟是为什么你会去使用这样一种容易出错且晦涩的特性好吧一般来说你根本就用不上它
Python界的领袖Tim Peters说
元类就是深度的魔法99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么而且根本不需要解释为什么要用元类
元类的主要用途是创建API。一个典型的例子是Django ORM。它允许你像这样定义
class Person(models.Model):name models.CharField(max_length30)age models.IntegerField()但是如果你这样做
person Person(nameTom, age18)
print(person.age)这并不会返回一个IntegerField对象而是会返回一个int甚至可以直接从数据库中取出数据。这是有可能的因为models.Model定义了__metaclass__ 并且使用了一些魔法能够将你刚刚定义的简单的Person类转变成对数据库的一个复杂hook。Django框架将这些看起来很复杂的东西通过暴露出一个简单的使用元类的API将其化简通过这个API重新创建代码在背后完成真正的工作
7、结语 首先你知道了类其实是能够创建出类实例的对象。好吧事实上类本身也是实例当然它们是元类的实例
Python中的一切都是对象它们要么是类的实例要么是元类的实例除了type
type实际上是它自己的元类在纯Python环境中这可不是你能够做到的这是通过在实现层面做一些手段实现的
其次元类是很复杂的。对于非常简单的类你可能不希望通过使用元类来对类做修改。你可以通过其他两种技术来修改类
monkey patching猴子打补丁class decorators类装饰器
当你需要动态修改类时99%的时间里你最好使用上面这两种技术。当然了其实在99%的时间里你根本就不需要动态修改类
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/81331.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!