[python 进阶] 详解元类及其应用 2
前言
在上一篇文章[python 进阶] 详解元类及其应用 1 中, 我们提到了关于元类的一些前置知识, 介绍了类对象, 动态创建类, 使用 type 创建类, 这一节我们将继续接着上文来讲~~~
5. 使 type 创建带有法的类
最终你会希望为你的类增加法. 只需要定义个有着恰当签名的函数并将 其作为属性赋值就可以了.
添加实例法In[14]:defecho_bar(self):#定义了一个普通的函数
...:print(self.bar)
...:
In[15]:FooChild=type('FooChild',(Foo,),{'echo_bar':echo_bar})
In[16]:hasattr(Foo,'echo_bar')#判断Foo类中,是否有echo_bar这个属性
Out[16]:False
In[17]:hasattr(FooChild,'echo_bar')#判断FooChild类中,是否有echo_bar这个属性
Out[17]:True
In[18]:my_foo=FooChild()
...:
In[19]:my_foo.echo_bar()
True
添加静态法In[20]:@staticmethod
...:deftestStatic():
...:print("static method ....")
...:
In[21]:Foochild=type('Foochild',(Foo,),{"echo_bar":echo_bar,"testStatic":testStatic})
In[22]:fooclid=Foochild()
In[23]:fooclid.testStatic
Out[23]:
In[24]:fooclid.testStatic()
staticmethod....
In[25]:fooclid.echo_bar()
True
添加类法In[26]:@classmethod
...:deftestClass(cls):
...:print(cls.bar)
...:
In[27]:Foochild=type('Foochild',(Foo,),{"echo_bar":echo_bar,"testStatic":testStatic,"testClass":testClass})
In[28]:fooclid=Foochild()
In[29]:fooclid.testClass()
True
你可以看到, 在 Python 中, 类也是对象, 你可以动态的创建类. 这就是当你 使关键字 class 时 Python 在幕后做的事情, 这就是通过元类来实现的.
6. 到底什么是元类(终于到主题了)
元类就是来创建类的 "东". 你创建类就是为了创建类的实例对象, 不是吗? 但是我们已经学习到了 Python 中的类也是对象.
元类就是来创建这些类 (对象) 的, 元类就是类的类, 你可以这样理解为:MyClass=MetaClass()#使元类创建出个对象,这个对象称为"类"
MyObject=MyClass()#使"类"来创建出实例对象
你已经看到了 type 可以让你像这样做:
MyClass=type('MyClass',(),{})
这是因为函数 type 实际上是个元类. type 就是 Python 在背后来创建所有类的元类. 现在你想知道那为什么 type 会全部采写形式不是 Type 呢? 好吧, 我猜这是为了和 str 保持致性, str 是来创建字符串对象的类, int 是来创建整数对象的类. type 就是创建类对象的类. 你可以通过检查 __class__属性来看到这点. Python 中所有的东, 注意, 我是指所有的东 -- 都是对象. 这包括整数, 字符串, 函数以及类. 它们全部都是对象, 且它们都是从个类创建来, 这个类就是 type.In[30]:age=35
In[31]:age.__class__
Out[31]:int
In[32]:name='bob'
In[33]:name.__class__
Out[33]:str
In[34]:deffoo():
...:pass
...:
In[35]:foo.__class__
Out[35]:function
In[36]:classBar(object):
...:pass
...:
In[37]:b=Bar()
In[38]:b.__class__
Out[38]:__main__.Bar
现在, 对于任何个__class__的__class__属性是什么呢?In[40]:name.__class__.__class__
Out[40]:type
In[41]:age.__class__.__class__
Out[41]:type
In[42]:foo.__class__.__class__
Out[42]:type
In[43]:b.__class__.__class__
Out[43]:type
因此, 元类就是创建类这种对象的东. type 就是 Python 的内建元类, 当然 了, 你也可以创建的元类.
7.__metaclass__属性
你可以在定义个类的时候为其添加__metaclass__属性.
class Foo(object):
__metaclass__ = something...
... 省略...
如果你这么做了, Python 就会元类来创建类 Foo. 点, 这有些技 巧. 你先写下 class Foo(object), 但是类 Foo 还没有在内存中创建. Python 会在类的定义中寻找__metaclass__属性, 如果找到了, Python 就会它来创建类 Foo, 如果没有找到, 就会内建的 type 来创建这个类. 把下这段话反复读次. 当你写如下代码时 :In[44]:classFoo(Bar):
...:pass
...:
Python 做了如下的操作:
Foo 中有__metaclass__这个属性吗? 如果是, Python 会通过 __metaclass__创建个名字为 Foo 的类(对象)
如果 Python 没有找到__metaclass__, 它会继续在 Bar(类)中寻找 __metaclass__属性, 并尝试做和前同样的操作.
如果 Python 在任何类中都找不到__metaclass__, 它就会在模块层次 中去寻找__metaclass__, 并尝试做同样的操作.
如果还是找不到__metaclass__,Python 就会内置的 type 来创建这个类对象.
现在的问题就是, 你可以在__metaclass__中放置些什么代码呢? 答案就 是: 可以创建个类的东. 那么什么可以来创建个类呢? type, 或者任何使到 type 或者类化 type 的东东都可以.
8. 定义元类
元类的主要的就是为了当创建类时能够动地改变类. 通常, 你会为 API 做 这样的事情, 你希望可以创建符合当前上下的类.
假想个很傻的例, 你决定在你的模块所有的类的属性都应该是写形 式. 有好种法可以办到, 但其中种就是通过在模块级别设定 __metaclass__. 采这种法, 这个模块中的所有类都会通过这个元类来创建, 我们只需要告诉元类把所有的属性都改成写形式就万事吉了.
幸运的是,__metaclass__实际上可以被任意调, 它并不需要是个正式的类. 所以, 我们这就先以个简单的函数作为例开始.
python3 中#-*-coding:utf-8-*-
defupper_attr(future_class_name,future_class_parents,future_class_attr):
#遍历属性字典, 把不是__开头的属性名字变为写
newAttr={}
forname,valueinfuture_class_attr.items():
ifnotname.startswith("__"):
newAttr[name.upper()]=value
#调 type 来创建个类
returntype(future_class_name,future_class_parents,newAttr)
classFoo(object,metaclass=upper_attr):
bar='bip'
print(hasattr(Foo,'bar'))
print(hasattr(Foo,'BAR'))
f=Foo()
print(f.BAR)
现在让我们再做次, 这次个真正的 class 来当做元类.#-*-coding:utf-8-*-
classUpperAttrMetaClass(type):
#__new__是在__init__之前被调的特殊法
#__new__是来创建对象并返回之的法
#__init__只是来将传的参数初始化给对象
#你很少到__new__, 除你希望能够控制对象的创建
#这, 创建的对象是类, 我们希望能够定义它, 所以我们这改写__new__
#如果你希望的话, 你也可以在__init__中做些事情
#还有些级的法会涉及到改写__call__特殊法, 但是我们这不
def__new__(cls,future_class_name,future_class_parents,future_class_attr):
#遍历属性字典, 把不是__开头的属性名字变为写
newAttr={}
forname,valueinfuture_class_attr.items():
ifnotname.startswith("__"):
newAttr[name.upper()]=value
#法 1: 通过'type'来做类对象的创建
#return type(future_class_name,future_class_parents,newAttr)
#法 2: 复 type.__new__法
#这就是基本的 OOP 编程, 没什么魔法
#return type.__new__(cls,future_class_name,future_class_parents,future_class_attr)
#法 3: 使 super 法
returnsuper(UpperAttrMetaClass,cls).__new__(cls,future_class_name,future_class_parents,future_class_attr)
#python2 的法
#class Foo(object):
# __metaclass__ = upper_attr# 设置 Foo 类的元类为 upper_attr
# bar = 'bip'
#python3 的法
classFoo(object,metaclass=upper_attr):
bar='bip'
print(hasattr(Foo,'bar'))
# 输出: False
print(hasattr(Foo,'BAR'))
# 输出: True
f=Foo()
print(f.BAR)
# 输出:'bip'
就是这样, 除此之外, 关于元类真的没有别的可说的了. 但就元类本身 , 它们其实是很简单的:
拦截类的创建
修改类
返回修改之后的类
究竟为什么要使元类?
现在回到我们的主题上来, 究竟是为什么你会去使这样种容易出错且晦涩的特性? 好吧, 般来说, 你根本就不上它:
"元类就是深度的魔法, 99% 的户应该根本不必为此操. 如果你想搞清楚 究竟是否需要到元类, 那么你就不需要它. 那些实际到元类的都常 清楚地知道他们需要做什么, 且根本不需要解释为什么要元类."-- Python 界的领袖TimPeters
来源: https://www.cnblogs.com/ECJTUACM-873284962/p/8886528.html