前言
在上一篇文章[python 进阶] 详解元类及其应用 1 中, 我们提到了关于元类的一些前置知识, 介绍了类对象, 动态创建类, 使用 type 创建类, 这一节我们将继续接着上文来讲~~~
5. 使 type 创建带有法的类
最终你会希望为你的类增加法. 只需要定义个有着恰当签名的函数并将 其作为属性赋值就可以了.
添加实例法
- In [14]: def echo_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
- ...: def testStatic():
- ...: 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]: <function __main__.testStatic>
- In [24]: fooclid.testStatic()
- static method ....
- In [25]: fooclid.echo_bar()
- True
添加类法
- In [26]: @classmethod
- ...: def testClass(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]: def foo():
- ...: pass
- ...:
- In [35]: foo.__class__
- Out[35]: function
- In [36]: class Bar(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]: class Foo(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-*-
- def upper_attr(future_class_name,future_class_parents,future_class_attr):
- #遍历属性字典, 把不是__开头的属性名字变为写
- newAttr = {}
- for name,value in future_class_attr.items():
- if not name.startswith("__"):
- newAttr[name.upper()] = value
- #调 type 来创建个类
- return type(future_class_name,future_class_parents,newAttr)
- class Foo(object,metaclass=upper_attr):
- bar = 'bip'
- print(hasattr(Foo,'bar'))
- print(hasattr(Foo,'BAR'))
- f = Foo()
- print(f.BAR)
现在让我们再做次, 这次个真正的 class 来当做元类.
- #-*-coding:utf-8-*-
- class UpperAttrMetaClass(type):
- #__new__是在__init__之前被调的特殊法
- #__new__是来创建对象并返回之的法
- #__init__只是来将传的参数初始化给对象
- #你很少到__new__, 除你希望能够控制对象的创建
- #这, 创建的对象是类, 我们希望能够定义它, 所以我们这改写__new__
- #如果你希望的话, 你也可以在__init__中做些事情
- #还有些级的法会涉及到改写__call__特殊法, 但是我们这不
- def __new__(cls,future_class_name,future_class_parents,future_class_attr):
- #遍历属性字典, 把不是__开头的属性名字变为写
- newAttr = {}
- for name,value in future_class_attr.items():
- if not name.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 法
- return super(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 的法
- class Foo(object,metaclass=upper_attr):
- bar = 'bip'
- print(hasattr(Foo,'bar'))
- # 输出: False
- print(hasattr(Foo,'BAR'))
- # 输出: True
- f = Foo()
- print(f.BAR)
- # 输出:'bip'
就是这样, 除此之外, 关于元类真的没有别的可说的了. 但就元类本身 , 它们其实是很简单的:
拦截类的创建
修改类
返回修改之后的类
究竟为什么要使元类?
现在回到我们的主题上来, 究竟是为什么你会去使这样种容易出错且晦涩的特性? 好吧, 般来说, 你根本就不上它:
"元类就是深度的魔法, 99% 的户应该根本不必为此操. 如果你想搞清楚 究竟是否需要到元类, 那么你就不需要它. 那些实际到元类的都常 清楚地知道他们需要做什么, 且根本不需要解释为什么要元类." -- Python 界的领袖 Tim Peters
来源: https://www.cnblogs.com/ECJTUACM-873284962/p/8886528.html