针对Python中的metaclasses是什么?和python__metaclass__这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展python中的元类metaclass详情、djan
针对Python 中的metaclasses是什么?和python __metaclass__这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展 python中的元类metaclass详情、django classonlymethod和python classmethod有什么区别?、groovy – metaClass.methods和metaClass.metaMethods有什么区别?、Python Metaclass等相关知识,希望可以帮助到你。
本文目录一览:- Python 中的metaclasses是什么?(python __metaclass__)
- python中的元类metaclass详情
- django classonlymethod和python classmethod有什么区别?
- groovy – metaClass.methods和metaClass.metaMethods有什么区别?
- Python Metaclass
Python 中的metaclasses是什么?(python __metaclass__)
在 Python 中,metaclasses是什么,我们用它们做什么?
python中的元类metaclass详情
动机
python语言因为工作偏向于 AI ,所以对于这门语言还停留在表面,对于 python 深层并没有接触到。
今天来聊一聊元类(metaclass) ,想必大多数人都或多或少听过元编程这个词,但是对于元编程是什么以及如何应用元编程熟悉应该不多,在 python 中的 metaclass 就是帮助 developer 实现元编程,因此产生一个想法
最近时间还算宽裕,所以想要文章认真弄一弄
从一个问题引出 MetaClass
在 python 语言中,并没有函数重载,我们下面通过一个具体例子来说明。
class A(): def f(self, x:int): print(''A.f int overload'',self,x) def f(self,x:str): print(''A.f str overload'',self,x) def f(self,x,y): print(''A.f two arg overload'',self,x,y) if __name__ == "__main__": a = A() a.f(1)
当执行上面代码我们会得到一个错误信息,实例化 A 类后,调用实例的 f 方法,因为在 python 语言中没有重装方法,所以 def f(self,x:str)
会覆盖之前的 def f(self, x:int)
, 而 def f(self,x,y)
方法会覆盖于 def f(self,x:str)
方法,所以当通过传入 1 一个参数,不会调用 def f(self,x:int)
而是调用 def f(self,x,y)
方法。
TypeError: f() missing 1 required positional argument: ''y''
那么什么是正确的姿势解决这个问题呢? 这里先不急于给出答案,当我们介绍完 metaclass 后,答案就自然浮出水面。
Metaclass 编程
想要了解 Metaclass 也就是元类,meta 在英文中超越的意思,也就是 Metaclass 是高级于 class,用于创建 class 的 class。有的时候我们需要控制类创建过程,通常创建类工作是由 type 完成的,因为 type 直接设计到 c,我们希望在 type 创建类过程插入一些自定义的东西,所以引入了 Metaclass 让某一个类创建工作不再由 type 来实现,而是由指定 class 来实现
在 python 中,我们可以通过 class 来实例化对象,不过这里要说在 python 中 class 其实也是对象。既然 class 也是对象,那么 class 的类型又是什么呢
class A: a = 1 b = "hello" def f(self): return 12 def main(): print(f''{type(2)=}'') print(f''{type("hello")=}'') print(f''{type([])=}'') print(f''{type(A())=}'') if __name__ == "__main__": main()
输出一下 2、hello 、空数组和 A 类实例的类型,结果发现他们类别分别为 int、str、list 和 A 类别。其实他们也是对象,既然是对象,那么就会有一个 class 用于创建这个类别。
type(2)=<class ''int''> type("hello")=<class ''str''> type([])=<class ''list''> type(A())=<class ''__main__.A''>
接下来我们就看一下这些 class(int,str,list) 那么这些对象又是什么类别呢
class A: a = 1 b = "hello" def f(self): return 12 if __name__ == "__main__": print(f''{type(int)=}'') print(f''{type(str)=}'') print(f''{type(list)=}'') print(f''{type(A)=}'')
type(int)=<class ''type''> type(str)=<class ''type''> type(list)=<class ''type''> type(A)=<class ''type''>
不难看出多有 class 的类型都是 type ,例如数字 2 的 int 的一个实例,而 int 又是 type 的一个实例。
如果大家从类似 java 这些语言开始,然后再去学习 python 可能会有这样疑问,在 python 中 type 和 class 有什么区别,他们不都是类型吗? 其实答案就是这两者在 python3 中并没有区别,可以将他们看做一个东西。
def main(): x = int() print(f''{x=}'') B = type(''B'',(),{}) print(f''{B=}'') if __name__ == "__main__": main()
不过如果进一步深入研究,两种 class 和 type 在字面上,是不同两样东西,class 作为关键字来定义类型时,是调用其构造器来做了一些初始化的工作。
def main(): x = int() print(f''{x=}'') B = type(''B'',(),{}) print(f''{B=}'') if __name__ == "__main__": main()
我们可以这样来定义一个类型
x=0 B=<class ''__main__.B''>
可以用 class 方式来定义一个类 A,然后我们在用 type 方式来创建一个类,type 接受 3 个参数分别是类的名称,这里接受的字符串类型的名称、以及该类的基类,是组元的形式,接下来是就是一个属性,属性是字典形式数据,键是属性名称,值是属性值。
class A: a = 2 b = ''hello'' def f(self): return 12
下面我们用 make_A
来创建一个类, 这里使用 type 来定义一个类
def make_A(): name = ''A'' bases = () a = 2 b = ''hello'' def f(self): return 12 namespace = {''a'':a,''b'':b,''f'':f} A = type(name,bases,namespace) return
通过 type 创建类时候需要传入类名称 A 然后 base 是一个要创建类 A 的基类,namescpace 是类属性,是 dict 形式,键是属性名称,而值是属性值。
def make_A_more_accurate(): name = ''A'' bases = () namespace = type.__prepare__(name,bases) body = ( """ a = 1 b = ''hello'' def f(self): return 12 """ ) exec(body,globals(),namespace) A = type(name,bases,namespace) return A
metaclass 是继承于 type,那么 metaclass 的工作也是用于创建 class,我们可以在 metaclass 中做一些自定义的事情,
这里可能比较难理解是 __prepare__
上网找到关于 __prepare__
解释,暂时说一下自己认识,可能有点浅,感觉就是为类创建了一个局部的作用域。
namespace = type.__prepare__(name,bases) print(namespace)
type.__prepare__
应该是返回一个局部命名空间,
exec(body,globals(),namespace)
class Tut: ... tut = Tut() print(f''{type(tut)=}'') print(f''{type(Tut)=}'')
上面例子定义一个类,然后实例化 Tut 类得到对象 tut,接下来分别输出 tut 和 Tut 类型
type(tut)=<class ''__main__.Tut''> type(Tut)=<class ''type''>
不难看出 tut 是 Tut 的实例,而 Tut 是 type 的对象
class TutMetaClass(type): ... class Tut(metaclass=TutMetaClass): ...
然后我们定义一个 TutMetaClass 继承于 type,然后将 Tut 类的 metaclass 指向 TutMetaClass ,然后 tut 类型为 Tut,而 Tut 类型为 TutMetaClass 类型
type(tut)=<class ''__main__.Tut''> type(Tut)=<class ''__main__.TutMetaClass''>
到此这篇关于 python 中的 元类metaclass详情的文章就介绍到这了,更多相关 python metaclass 内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
- 详解python metaclass(元类)
- 举例讲解Python中metaclass元类的创建与使用
- 深入理解Python中的元类(metaclass)
- Python Metaclass原理与实现过程详细讲解
django classonlymethod和python classmethod有什么区别?
为什么Django需要引入装饰器classonlymethod
?为什么它不能重用python classmethod
?
groovy – metaClass.methods和metaClass.metaMethods有什么区别?
class Example { def realFoo() { "foo" } } Example.MetaClass.MetaFoo = { -> "foo" } def reals = Example.MetaClass.methods*.name.grep{it.contains("Foo")} def Metas = Example.MetaClass.MetaMethods*.name.grep{it.contains("Foo")} println "reals = $reals,Metas = $Metas"
我希望reals的输出= [realFoo],Metas = [MetaFoo],但实际上我得到的是reals = [realFoo,MetaFoo],Metas = [].
看起来新的元方法存储在方法中,而不是MetaMethods.那么,MetaClass.methods和MetaClass.MetaMethods有什么区别?
解决方法
这些在DefaultGroovyMethods类中定义.
根据您要实例化的对象类型,它主要是迭代器,如每个,收集,查找等.
对代码的这种修改显示了仅限元,仅“真实”和共享的方法:
class Example { def realFoo() { "foo" } } Example.MetaClass.MetaFoo = { -> "foo" } def reals = Example.MetaClass.methods.name.sort().unique() def Metas = Example.MetaClass.MetaMethods.name.sort().unique() def MetaOnly = Metas - reals def realOnly = reals - Metas def shared = reals.findAll { Metas.contains(it) } println """ MetaOnly = $MetaOnly realOnly = $realOnly shared = $shared """
结果:
MetaOnly = [addShutdownHook,any,asBoolean,asType,collect,dump,each,eachWithIndex,every,find,findAll,findindexOf,findindexValues,findLastIndexOf,findResult,getAt,getMetaPropertyValues,getProperties,grep,hasProperty,identity,inject,inspect,is,isCase,iterator,MetaClass,print,printf,println,putAt,respondsTo,sleep,split,sprintf,use,with] realOnly = [equals,getClass,getProperty,hashCode,MetaFoo,notify,notifyAll,realFoo,setProperty,wait] shared = [getMetaClass,invokeMethod,setMetaClass,toString]
所有MetaOnly和shared方法都在DefaultGroovyMethods中.所有“真正的”方法都在类本身上,或者在它的父类(在本例中为Object)上,加上一些直接与MetaClass相关的groovy事物来获取/设置MetaClass以及getProperty / setProperty和invokeMethod它允许您覆盖方法行为.
如果你想搜索所有方法以查看存在的内容,我会使用以下内容:
def allMethods = (Example.MetaClass.methods + Example.MetaClass.MetaMethods).name.sort().unique()
Python Metaclass
类也是对象
在弄明白 metaclass 之前,你应该先清楚地知道什么是 Python 中的类(Class)。Python 中的这种从 Smalltalk 语言中借鉴而来的类十分奇怪。
在大部分的编程语言中,类就是一段用来描述怎样产生对象(Object)的代码。Python 也不例外:
1 2 3 4 5 6 7 |
|
但是 Python 中的类可不止是这些。类本身也是对象。
没错,就是对象。
你一使用 class
关键字,Python 就会执行它并创建一个对象。代码:
1 2 3 |
|
会在内存中创建一个名为 ObjectCreator 的对象。
这个对象(也就是这个类)本身拥有能力来创建对象(它的实例),这就是它之所以是类的原因。
但仍然,它还是一个对象,因此:
- 你可以将它赋值给一个变量
- 你可以复制(copy)它
- 你可以对它增加属性
- 你可以把它当做函数的参数
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
动态地创建类
既然类就是对象,那么你就可以像创建对象一样,动态地创建类。
首先,你可以在一个函数(function)中使用 class
创建类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
但这还不够动态,因为你还是写了定义类的全部代码。
既然类就是对象,那它一定是由什么东西生成的。
当你使用 class
关键字时,Python 会自动地创建一个对象。但就像 Python 中的大部分事情一样,你也可以手动地完成它。
还记得那个叫 type
的函数吗?这个经典地让你知道一个对象是什么类型的函数:
1 2 3 4 5 6 7 8 |
|
好吧,type
其实有着非常不一般的能力,它可以动态地创建类。type
可以接受那些描述类的参数,然后返回一个类。
(我懂的,根据传递的参数来决定两种完全不同的作用的函数是很二的,但这是 Python 为了向后兼容而不得不做的权衡)
type
是这样用的:
1 2 3 |
|
例如:
1 2 |
|
可以手动地写成这样:
1 2 3 4 5 |
|
你可能注意到了,我们使用“MyShinyClass”作为了类名并且用它作为了一个变量名来引用这个类。它们是可以不同的,但是没有理由把事情搞得更复杂。
type
接受一个字典(dictionary)来定义类的属性。所以:
1 2 |
|
可以被翻译成:
1 |
|
和使用一般的类没什么两样:
1 2 3 4 5 6 7 8 9 |
|
当然,你还可以继承它:
1 2 |
|
可以翻译成:
1 2 3 4 5 |
|
最后,你还可以给你的类定义方法。只需要正确地创建函数并且将它赋值成类的参数就可以了。
1 2 3 4 5 6 7 8 9 10 |
|
至此你应该明白了:在 Python 中,类本身也是对象,你可以动态地创建它。
这就是你在使用 class
关键字时 Python 所做的事情,你也可以用 metaclass 来完成。
什么是 metaclass(终于)
Metaclass 就是一种创建类的东西。
你定义类是为了创建对象,对不对?
但我们已经知道,Python 类也是对象了。
那么,metaclass 就是创建这些对象的东西。它是“类的类”,你可以把它们想象成这样:
1 2 |
|
你已经知道了,type
可以让你做类似这样的事情:
1 |
|
这是因为 type
函数本质上就是一个 metaclass。type
就是 Python 在底层用来创建所有类的 metaclass。
现在你也许在想,尼玛为什么要把它写成全小写呢,写成 Type
不行吗?
好吧,我猜这是为了和那个创建字符串对象的类 str
保持一致的风格,创建所有整数对象的 int
类也是如此。type
就是那个创建类对象的类。
你可以用 __class__
属性来验证。
在 Python 中,所有的东西,我是说一切的东西,都是一个对象。包括整数、字符串、函数和类。它们全都是对象,并且它们全都是被一个类来创建的:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
现在想想,__class__
的 __class__
又是什么呢?
1 2 3 4 5 6 7 8 |
|
所以,metaclass 就是创建类对象的东西。
如果你乐意你可以称它为“类工厂”。
内置的 type
就是 Python 使用的 metaclass,当然,你也可以创建你自己的 metaclass。
__metaclass__
属性
你可以在编写类的时候增加一个 __metaclass__
属性:
1 2 3 |
|
如果你这样写,Python 将会使用这个 metaclass 来创建 Foo
类。
小心点,这是一种奇技淫巧。
你先写下了 class Foo(object)
,但是类对象 Foo
还并不会在内存中创建。
Python 会检查类中有没有 __metaclass__
声明,如果有的话,就用它来创建类 Foo
。如果没有,就还是用 type
来创建这个类。
熟记上面这段话。
当你这样做的时候:
1 2 |
|
Python 会做下面这几件事:
Foo
中有 __metaclass__
属性吗?
如果有的话,就用这个 __metaclass__
来在内存中创建一个名为 Foo
的类对象(我是说的类对象,仔细点)。
如果 Python 找不到 __metaclass__
属性,它就会检查 Bar(父类)有没有 __metaclass__
,然后重复同样的动作。
如果 Python 在所有父类中都找不到 __metaclass__
,它就会在模块(Module)级别来找,然后重复同样的动作。
如果还是找不到 __metaclass__
,它就用 type
来创建类对象。
现在的重点是,你可以把什么写成 __metaclass__
呢?
答案就是:可以创建类的东西。
那什么可以创建类呢?type
,使用过它的,或者它的子类。
自定义 metaclass
使用 metaclass 的主要目的就是在创建类的时候自动地改变它。
你通常可以为 API 做这些,因为你需要创建符合上下文环境的类。
这里有个比较蠢的例子,就是你决定把你某个模块里的所有类的属性都写成大写的。有很多种方法可以做到,其中一个就是在模块级别声明一个 __metaclass__
。
这样,这个模块中的所有类都是用这个 __metaclass__
创建的,我们只需要告诉这个 metaclass 把所有的属性转成大写就可以了。
幸运的是,__metaclass__
其实可以是任意能被执行(callable)的东西,它并不要求一定要是一个常规的类(我知道,那些名字中有“类”的东西并不一定要是一个类,猜猜看……这对你有些帮助)。
所以,我们用一个函数来写一个简单的例子作为开头:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
现在,用一个真正的类作为 metaclass 来做同样的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
但是这还不够面向对象,我们直接调用了 type
而不是重写了父类的 __new__
。我们来写一个:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
你可能注意到了那个 upperattr_metaclass
。这玩意儿一点都不特别:一个方法总是把当前实例当做第一个参数传进来。就像普通方法的 self
参数一样。
当然,我用的这个名字可能太长了点,不像 self
这种约定俗成的名字。所以真实产品中的 metaclass 会像这个样子:
1 2 3 4 5 6 7 8 |
|
我们可以使用 super
让它变得更清晰一点,这样就能更容易地继承(是的,你可以定义 metaclass,继承自 metaclass 或者 type):
1 2 3 4 5 6 7 8 |
|
就是这样。关于 metaclass 的东西真的就这么多了。
那些使用了 metaclass 代码,并不是因为 metaclass 复杂,而是因为你通常使用 metaclass 来处理那些依赖于自省(introspection)的奇怪的功能或者对类的继承关系进行操作,就像 __dict__
等。
实际上,metaclass 特别适合做一些像巫术之类的事情,因此它有点难懂,但是它本身其实很简单:
- 拦截类的创建
- 修改类
- 返回被修改的类
为什么你要用类 metaclass 而不是函数 metaclass?
既然 __metaclass__
可以是任意的可执行的东西,为什么你应该使用明显更为复杂的类而不是更简单的函数呢?
这样做有几个原因:
- 你的意图更加清晰。当你读到
UpperAttrMetaclass(type)
时,你就知道接下来会发生什么事情 - 你可以使用面向对象编程。metaclass 可以继承自别的 metaclass,重写父类的方法。metaclass 甚至都可以定义 metaclass。
- 你可以更好地组织你的代码。你从来不会使用 metaclass 来做上文例子中那些简单琐碎的事情,它们通常用来干更复杂的事。把多个方法组织到一个类里是一个让代码更容易读的非常有效的方法。
- 你可以 hook on
__new__
,__init__
和__call__
。这可以让你做一些不寻常的事情,即使你可以在__new__
这一个方法中做它们所有能做的事情,有些人则更习惯用__init__
- 它们居然叫「metaclass(元类)」,可恶,这一定意味着些什么!
你到底为什么要使用 metaclass
现在到了考虑这个重要问题的时候了,为什么你要使用这个晦涩难懂又容易出错的特性呢?
好吧,通常来说你不应该用。
Metaclass 是 99% 的用户都可以不必考虑的深层技巧。如果你在想你是不是要用 metaclass 的时候,你就真的不要使用(真正需要使用它的人会确切地知道需要它的原因,并且不需要解释为什么需要它)。
Python 大师 Tim Peters
metaclass 最主要的使用场景就是创建 API。典型的一个例子就是 Django ORM。
它可以这样来定义一些东西:
1 2 3 |
|
但是如果你这样:
1 2 |
|
这并不会返回一个 IntegerField
对象。它会返回一个 int
,甚至可以直接从数据库中取出它。
这样之所以可行就是因为 models.Model
定义了 __metaclass__
,它会像有魔法似的返回 Person
,而你仅仅只是在复杂的数据库字段钩子函数里定义了一个简单的语句。
Django 使用 metaclass 让一些很复杂的东西变成了看起来很简单的 API。这些 API 重造出来的幕后代码才是真正工作的代码。
最后的话
首先,你知道了类是一些可以创造实例的对象。
好吧,实际上,类本身就是实例,metaclass 的实例。
1 2 3 |
|
Python 里所有的东西都是对象,所有的东西都是类的实例或者 metaclass 的实例。
除了 type
。
type
实际上是它自身的 metaclass。这点是你用纯 Python 代码重现不出来的,它是因为在实现层面做了点带欺骗性质的技巧而产生的结果。
其次,metaclass 是复杂的东西。你可能在做一些简单的修改类的工作时并不是真的需要它。你可以使用其他两种不同的技术来修改类:
- 猴子补丁(monkey patching)
- 类装饰器(class decorators)
99% 你需要修改类的时候,你最好使用这两种技术。
但是 99% 的这种时候,你其实根本就不需要修改类 :-)。
今天关于Python 中的metaclasses是什么?和python __metaclass__的介绍到此结束,谢谢您的阅读,有关 python中的元类metaclass详情、django classonlymethod和python classmethod有什么区别?、groovy – metaClass.methods和metaClass.metaMethods有什么区别?、Python Metaclass等更多相关知识的信息可以在本站进行查询。
本文标签: