Skip to content

Commit 1874257

Browse files
committed
增加 metaclass
1 parent 5fdfd93 commit 1874257

File tree

6 files changed

+565
-0
lines changed

6 files changed

+565
-0
lines changed

metaclass/abc_ABCMeta.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# @File : abc_ABCMeta.py
4+
# @Author: yubo
5+
# @Date : 2019/11/29
6+
# @Desc :
7+
8+
import abc
9+
10+
"""
11+
解释器创建类时,会检测 cls.__abstractmethods__ 是否为空,若不为空,则拒绝创建对象
12+
13+
ABCMeta 利用了一点,在类 TestA 创建时,检测是否含有 @abc.abstractmethod 修饰的成员,即设置了 .__isabstractmethod__
14+
根据各成员生成一个 __abstractmethods__ 集合,若集合不为空,则无法创建示例,即为抽象类
15+
16+
class ABC(metaclass=ABCMeta):
17+
pass
18+
19+
ABC 指定元类为 ABCMeta, 故继承 ABC 的类的元类也为 ABCMeta
20+
21+
class TestA(abc.ABC) 等同于
22+
class TestA(metaclass=ABCMeta)
23+
"""
24+
class TestA(abc.ABC):
25+
26+
a = 1
27+
b = "a"
28+
29+
@abc.abstractmethod
30+
def func1(self):
31+
pass
32+
33+
34+
class TestB(TestA):
35+
@abc.abstractmethod
36+
def func2(self):
37+
pass
38+
39+
40+
class TestC(TestB):
41+
@abc.abstractmethod
42+
def func3(self):
43+
pass
44+
45+
'''
46+
The notion is correct; the ABCMeta code does not distinguish between a abstractproperty and a abstractmethod.
47+
48+
Both of these decorators add an attribute to the decorated item, .__isabstractmethod__,
49+
which ABCMeta uses to add an .__abstractmethods__ attribute (a frozenset) to the ABC you defined.
50+
The object type then guards against creating an instance of any class where any of the names listed
51+
in .__abstractmethods__ does not have a concrete implementation. No checks are made for functions versus properties there.
52+
53+
To illustrate:
54+
55+
>>> from abc import *
56+
>>> class C:
57+
... __metaclass__ = ABCMeta
58+
... @abstractmethod
59+
... def abstract_method(self): pass
60+
... @abstractproperty
61+
... def abstract_property(self): return 'foo'
62+
...
63+
>>> C.__abstractmethods__
64+
frozenset(['abstract_method', 'abstract_property'])
65+
By creating new overrides for these in a subclass, the ABCMeta class will find fewer methods or properties with
66+
the . __isabstractmethod__ attribute, thus making the resulting __abstractmethods__ set smaller; once the set is empty
67+
you can create instances of such a subclass.
68+
69+
These checks are made in the ABCMeta.__new__ constructor and no checks are made to match descriptor types:
70+
71+
cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace)
72+
# Compute set of abstract method names
73+
abstracts = set(name
74+
for name, value in namespace.items()
75+
if getattr(value, "__isabstractmethod__", False))
76+
for base in bases:
77+
for name in getattr(base, "__abstractmethods__", set()):
78+
value = getattr(cls, name, None)
79+
if getattr(value, "__isabstractmethod__", False):
80+
abstracts.add(name)
81+
cls.__abstractmethods__ = frozenset(abstracts)
82+
You'd have to create a subclass of ABCMeta that overrides the __new__ method, and check that any abstract method or
83+
property named on a base class is indeed matched with a non-abstract method or property on cls instead.
84+
'''

metaclass/metaclass_0.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# @File : metaclass_0.py
4+
# @Author: yubo
5+
# @Date : 2019/11/18
6+
# @Desc :
7+
8+
9+
class Meta(type):
10+
"""
11+
自定义元类
12+
"""
13+
def __new__(cls, name, bases, d):
14+
"""
15+
使用自定义元类,最终都是要调用 type 生成类实例
16+
return super(Meta, cls).__new__(cls, name, bases, d)
17+
等同于
18+
type.__new__(cls, name, bases, d)
19+
"""
20+
print("call Meta __new__")
21+
d["a"] = "a"
22+
return super(Meta, cls).__new__(cls, name, bases, d)
23+
24+
def __init__(self, *args, **kwargs):
25+
print("call Meta __init__")
26+
super(Meta, self).__init__(*args, **kwargs)
27+
28+
29+
class MetaNormal0(object):
30+
"""
31+
我们写下如下代码时:
32+
33+
class Foo(object):
34+
pass
35+
实际上,解释器将其解释为:
36+
37+
Foo = type('Foo', (object,), {})
38+
"""
39+
pass
40+
41+
42+
class MetaNormal1(metaclass=Meta):
43+
"""
44+
如果定义了元类
45+
class MyMetaType(type):
46+
pass
47+
class Foo(metaclass=MyMetaType):
48+
pass
49+
50+
解释器解释到class Foo(...)的时候,就会转换为:
51+
Foo = MyMetaType('Foo', (object,), {})
52+
53+
此时调用 MyMetaType.__new__, MyMetaType.__init__, 生成类MyMetaType 的实例化对象:Foo
54+
"""
55+
pass
56+
57+
58+
"""
59+
MetaNormal1 的元类是 Meta, 父类是 object
60+
"""
61+
print(type(MetaNormal1))
62+
print(MetaNormal1.__mro__)
63+
"""
64+
call Meta __new__
65+
call Meta __init__
66+
<class '__main__.Meta'>
67+
(<class '__main__.MetaNormal1'>, <class 'object'>)
68+
"""

metaclass/metaclass_1.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# @File : metaclass_1.py
4+
# @Author: yubo
5+
# @Date : 2019/11/18
6+
# @Desc :
7+
8+
class_definition = type('class_name', (), {}) # ➊
9+
10+
print(type(class_definition))
11+
12+
13+
class Meta(type): # ➋
14+
def __new__(cls, name, bases, d):
15+
print("call Meta __new__")
16+
d["a"] = "a"
17+
return super(Meta, cls).__new__(cls, name, bases, d)
18+
19+
def __init__(self, *args, **kwargs):
20+
print("call Meta __init__")
21+
super(Meta, self).__init__(*args, **kwargs)
22+
23+
24+
print(type(class_definition)) # ➌
25+
print(class_definition) # ➍
26+
print(Meta)
27+
print(type(Meta))
28+
29+
meta_definition = Meta('meta', (), {}) # ➎
30+
print(meta_definition)
31+
print(type(meta_definition)) # ➏
32+
33+
34+
class MetaNormal0(Meta):
35+
"""
36+
我们写下如下代码时:
37+
38+
class Foo(object):
39+
pass
40+
实际上,解释器将其解释为:
41+
42+
Foo = type('Foo', (object,), {})
43+
"""
44+
pass
45+
46+
47+
class MetaNormal1(metaclass=Meta):
48+
"""
49+
如果定义了元类
50+
class MyMetaType(type):
51+
pass
52+
class Foo(metaclass=MyMetaType):
53+
pass
54+
55+
解释器解释到class Foo(...)的时候,就会转换为:
56+
Foo = MyMetaType('Foo', (object,), {})
57+
"""
58+
pass
59+
60+
61+
class MetaNormal2(meta_definition):
62+
'''
63+
为什么这里可以用 meta_definition 定义元类?而不需要 metaclass 关键字?
64+
'''
65+
pass
66+
67+
68+
print(type(MetaNormal2))
69+
print(MetaNormal2.a)
70+
71+
# print(MetaNormal0.__mro__)
72+
# print(MetaNormal1.__mro__)
73+
"""
74+
➊ ➋ type 类或者继承自 type 的类称为元类
75+
➌ class_definition 是type创建,所以 type(class_definition) 为 <class 'type'>
76+
➍ class_definition 是一个类
77+
➎ meta_definition 由元类 Meta 创建,创建 meta_definition 时,
78+
1. 先调用 Meta 的 __new__,
79+
2. 再调用 Meta 的 __init__,
80+
这样,meta_definition(类) 作为 元类 Meta 的一个实例就被创建好
81+
➏ meta_definition 的元类为 Meta
82+
"""
83+
84+
""" 输出:
85+
<class 'type'>
86+
<class '__main__.class_name'>
87+
<class 'type'>
88+
call Meta __new__
89+
call Meta __init__
90+
<class '__main__.meta'>
91+
<class '__main__.Meta'>
92+
"""

metaclass/metaclass_2.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# @File : metaclass_2.py
4+
# @Author: yubo
5+
# @Date : 2019/11/18
6+
# @Desc :
7+
8+
9+
class SimpleMetaClass(type):
10+
def __new__(cls, *args, **kwargs):
11+
print("creating class Earth...")
12+
return super(SimpleMetaClass, cls).__new__(cls, *args, **kwargs)
13+
14+
def __init__(self, *args, **kwargs):
15+
print("you have create a class instance by metaclass")
16+
super(SimpleMetaClass, self).__init__(*args, **kwargs)
17+
18+
def __call__(self, *args, **kwargs):
19+
print("__call__ in metaclass has been invoked...", "the args:", args, kwargs)
20+
return super(SimpleMetaClass, self).__call__(*args, **kwargs)
21+
22+
23+
class Earth(metaclass=SimpleMetaClass):
24+
def __new__(cls, g, R=65535):
25+
print("creating instance using __new__")
26+
cls.g = g
27+
cls.R = R
28+
return super(Earth, cls).__new__(cls)
29+
30+
def __init__(self, g, R=65535):
31+
print("initializing instance in __init__")
32+
print("gravity on Earth is:%f" % self.g)
33+
34+
def __call__(self):
35+
print(self.g)
36+
37+
def sayHello(self):
38+
print("hello earth,your gravity is:%f" % self.g)
39+
40+
41+
if __name__ == "__main__":
42+
"""earth = Earth(9.8, R=65535)
43+
1、调用 创建 Earth 的类的__call__ 方法,即 SimpleMetaClass.__call__
44+
1、调用 type.__call__
45+
2、调用 Earth.__new__
46+
3、type.__call__ 返回
47+
SimpleMetaClass.__call__ 返回 Earth 的一个实例
48+
2、调用 Earth.__init__
49+
"""
50+
earth = Earth(9.8, R=65535)
51+
"""
52+
earth() 调用创建 earth 的类的__call__方法,即 Earth.__call__
53+
"""
54+
earth()
55+
earth.sayHello()
56+
57+
"""
58+
creating class Earth...
59+
you have create a class instance by metaclass
60+
__call__ in metaclass has been invoked... the args: (9.8,) {'R': 65535}
61+
creating instance using __new__
62+
initializing instance in __init__
63+
gravity on Earth is:9.800000
64+
9.8
65+
hello earth,your gravity is:9.800000
66+
"""
67+
68+
"""
69+
1. 首先python创建SimpleMetaClass类,这个SimpleMetaClass是元类,应该是由type创建的。
70+
2. 当创建Earth这个类时,找到了它类中有__metaclass__属性,于是,采用SimpleClass来创建这个类
71+
3. 创建Earth类时,解释器会把类名,父类元祖,类的属性三个参数传给SimpleMetaClass(传给 type 也是这几个参数)
72+
4. SimpleMetaClass 根据 clazzName,(parent2,parent1,..),{‘attribute’:….,’method’:”}在自己__new__方法中创建出这个Earth实例【打印出①】,
73+
然后调用自己的__init__方法初始化类的参数【打印出②】。这时,这个Earth类作为metaclass的一个实例就被创建好了。
74+
5. 接下来通过 earth = Earth(9.8,R=65535) 创建一个Earth对象实例earth。这一步实际上是调用 Earth这个类对象的__call__(SimpleMetaClass::__call__)
75+
方法来创建一个Earth的实例。【打印出③,我们还能看到调用__call__的参数】。
76+
6. 而创建earth实例的方法__new__(Earth::__new__),和__init__(Earth:__init__),将会在Earth实例中的__call__(SimpleMetaClass::__call__)
77+
当中先后得以执行【先后打印出④⑤⑥】执行完成Earth实例earth对象被返回。
78+
7.8. 容易理解
79+
80+
"""

0 commit comments

Comments
 (0)