Skip to content

Commit 36e92dc

Browse files
committed
8.21小节完成
1 parent a25c4c0 commit 36e92dc

File tree

2 files changed

+314
-3
lines changed

2 files changed

+314
-3
lines changed

cookbook/c08/p21_vistor_pattern.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env python
2+
# -*- encoding: utf-8 -*-
3+
"""
4+
Topic: 观察者模式
5+
Desc :
6+
"""
7+
8+
9+
class Node:
10+
pass
11+
12+
13+
class UnaryOperator(Node):
14+
def __init__(self, operand):
15+
self.operand = operand
16+
17+
18+
class BinaryOperator(Node):
19+
def __init__(self, left, right):
20+
self.left = left
21+
self.right = right
22+
23+
24+
class Add(BinaryOperator):
25+
pass
26+
27+
28+
class Sub(BinaryOperator):
29+
pass
30+
31+
32+
class Mul(BinaryOperator):
33+
pass
34+
35+
36+
class Div(BinaryOperator):
37+
pass
38+
39+
40+
class Negate(UnaryOperator):
41+
pass
42+
43+
44+
class Number(Node):
45+
def __init__(self, value):
46+
self.value = value
47+
48+
# Representation of 1 + 2 * (3 - 4) / 5
49+
t1 = Sub(Number(3), Number(4))
50+
t2 = Mul(Number(2), t1)
51+
t3 = Div(t2, Number(5))
52+
t4 = Add(Number(1), t3)
53+
54+
55+
class NodeVisitor:
56+
def visit(self, node):
57+
methname = 'visit_' + type(node).__name__
58+
meth = getattr(self, methname, None)
59+
if meth is None:
60+
meth = self.generic_visit
61+
return meth(node)
62+
63+
def generic_visit(self, node):
64+
raise RuntimeError('No {} method'.format('visit_' + type(node).__name__))
65+
66+
67+
class Evaluator(NodeVisitor):
68+
def visit_Number(self, node):
69+
return node.value
70+
71+
def visit_Add(self, node):
72+
return self.visit(node.left) + self.visit(node.right)
73+
74+
def visit_Sub(self, node):
75+
return self.visit(node.left) - self.visit(node.right)
76+
77+
def visit_Mul(self, node):
78+
return self.visit(node.left) * self.visit(node.right)
79+
80+
def visit_Div(self, node):
81+
return self.visit(node.left) / self.visit(node.right)
82+
83+
def visit_Negate(self, node):
84+
return -node.operand
85+
86+
87+
e = Evaluator()
88+
print(e.visit(t4))
89+
90+
91+
class StackCode(NodeVisitor):
92+
def generate_code(self, node):
93+
self.instructions = []
94+
self.visit(node)
95+
return self.instructions
96+
97+
def visit_Number(self, node):
98+
self.instructions.append(('PUSH', node.value))
99+
100+
def binop(self, node, instruction):
101+
self.visit(node.left)
102+
self.visit(node.right)
103+
self.instructions.append((instruction,))
104+
105+
def visit_Add(self, node):
106+
self.binop(node, 'ADD')
107+
108+
def visit_Sub(self, node):
109+
self.binop(node, 'SUB')
110+
111+
def visit_Mul(self, node):
112+
self.binop(node, 'MUL')
113+
114+
def visit_Div(self, node):
115+
self.binop(node, 'DIV')
116+
117+
def unaryop(self, node, instruction):
118+
self.visit(node.operand)
119+
self.instructions.append((instruction,))
120+
121+
def visit_Negate(self, node):
122+
self.unaryop(node, 'NEG')
123+
124+
125+
class HTTPHandler:
126+
def handle(self, request):
127+
methname = 'do_' + request.request_method
128+
getattr(self, methname)(request)
129+
def do_GET(self, request):
130+
pass
131+
def do_POST(self, request):
132+
pass
133+
def do_HEAD(self, request):
134+
pass

source/c08/p21_implementing_visitor_pattern.rst

Lines changed: 180 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,191 @@
55
----------
66
问题
77
----------
8-
todo...
8+
你要处理由大量不同类型的对象组成的复杂数据结构,每一个对象都需要需要进行不同的处理。
9+
比如,遍历一个树形结构,然后根据每个节点的相应状态执行不同的操作。
10+
11+
|
912
1013
----------
1114
解决方案
1215
----------
13-
todo...
16+
这里遇到的问题在编程领域中是很普遍的,有时候会构建一个由大量不同对象组成的数据结构。
17+
假设你要写一个表示数学表达式的程序,那么你可能需要定义如下的类:
18+
19+
.. code-block:: python
20+
21+
class Node:
22+
pass
23+
24+
class UnaryOperator(Node):
25+
def __init__(self, operand):
26+
self.operand = operand
27+
28+
class BinaryOperator(Node):
29+
def __init__(self, left, right):
30+
self.left = left
31+
self.right = right
32+
33+
class Add(BinaryOperator):
34+
pass
35+
36+
class Sub(BinaryOperator):
37+
pass
38+
39+
class Mul(BinaryOperator):
40+
pass
41+
42+
class Div(BinaryOperator):
43+
pass
44+
45+
class Negate(UnaryOperator):
46+
pass
47+
48+
class Number(Node):
49+
def __init__(self, value):
50+
self.value = value
51+
52+
然后利用这些类构建嵌套数据结构,如下所示:
53+
54+
.. code-block:: python
55+
56+
# Representation of 1 + 2 * (3 - 4) / 5
57+
t1 = Sub(Number(3), Number(4))
58+
t2 = Mul(Number(2), t1)
59+
t3 = Div(t2, Number(5))
60+
t4 = Add(Number(1), t3)
61+
62+
这样做的问题是对于每个表达式,每次都要重新定义一遍,有没有一种更通用的方式让它支持所有的数字和操作符呢。
63+
这里我们使用观察者模式可以达到这样的目的:
64+
65+
.. code-block:: python
66+
67+
class NodeVisitor:
68+
def visit(self, node):
69+
methname = 'visit_' + type(node).__name__
70+
meth = getattr(self, methname, None)
71+
if meth is None:
72+
meth = self.generic_visit
73+
return meth(node)
74+
75+
def generic_visit(self, node):
76+
raise RuntimeError('No {} method'.format('visit_' + type(node).__name__))
77+
78+
为了使用这个类,可以定义一个类继承它并且实现各种 ``visit_Name()`` 方法,其中Name是node类型。
79+
例如,如果你想求表达式的值,可以这样写:
80+
81+
.. code-block:: python
82+
83+
class Evaluator(NodeVisitor):
84+
def visit_Number(self, node):
85+
return node.value
86+
87+
def visit_Add(self, node):
88+
return self.visit(node.left) + self.visit(node.right)
89+
90+
def visit_Sub(self, node):
91+
return self.visit(node.left) - self.visit(node.right)
92+
93+
def visit_Mul(self, node):
94+
return self.visit(node.left) * self.visit(node.right)
95+
96+
def visit_Div(self, node):
97+
return self.visit(node.left) / self.visit(node.right)
98+
99+
def visit_Negate(self, node):
100+
return -node.operand
101+
102+
使用示例:
103+
104+
.. code-block:: python
105+
106+
>>> e = Evaluator()
107+
>>> e.visit(t4)
108+
0.6
109+
>>>
110+
111+
作为一个不同的例子,下面定义一个类在一个栈上面将一个表达式转换成多个操作序列:
112+
113+
.. code-block:: python
114+
115+
class StackCode(NodeVisitor):
116+
def generate_code(self, node):
117+
self.instructions = []
118+
self.visit(node)
119+
return self.instructions
120+
121+
def visit_Number(self, node):
122+
self.instructions.append(('PUSH', node.value))
123+
124+
def binop(self, node, instruction):
125+
self.visit(node.left)
126+
self.visit(node.right)
127+
self.instructions.append((instruction,))
128+
129+
def visit_Add(self, node):
130+
self.binop(node, 'ADD')
131+
132+
def visit_Sub(self, node):
133+
self.binop(node, 'SUB')
134+
135+
def visit_Mul(self, node):
136+
self.binop(node, 'MUL')
137+
138+
def visit_Div(self, node):
139+
self.binop(node, 'DIV')
140+
141+
def unaryop(self, node, instruction):
142+
self.visit(node.operand)
143+
self.instructions.append((instruction,))
144+
145+
def visit_Negate(self, node):
146+
self.unaryop(node, 'NEG')
147+
148+
使用示例:
149+
150+
.. code-block:: python
151+
152+
>>> s = StackCode()
153+
>>> s.generate_code(t4)
154+
[('PUSH', 1), ('PUSH', 2), ('PUSH', 3), ('PUSH', 4), ('SUB',),
155+
('MUL',), ('PUSH', 5), ('DIV',), ('ADD',)]
156+
>>>
14157
15158
----------
16159
讨论
17160
----------
18-
todo...
161+
刚开始的时候你可能会写大量的if/else语句来实现,
162+
这里观察者模式的好处就是通过 ``getattr()`` 来获取相应的方法,并利用递归来遍历所有的节点:
163+
164+
.. code-block:: python
165+
166+
def binop(self, node, instruction):
167+
self.visit(node.left)
168+
self.visit(node.right)
169+
self.instructions.append((instruction,))
170+
171+
还有一点需要指出的是,这种技术也是实现其他语言中switch或case语句的方式。
172+
比如,如果你正在写一个HTTP框架,你可能会写这样一个请求分发的控制器:
173+
174+
.. code-block:: python
175+
176+
class HTTPHandler:
177+
def handle(self, request):
178+
methname = 'do_' + request.request_method
179+
getattr(self, methname)(request)
180+
def do_GET(self, request):
181+
pass
182+
def do_POST(self, request):
183+
pass
184+
def do_HEAD(self, request):
185+
pass
186+
187+
观察者模式一个缺点就是它严重依赖递归,如果数据结构嵌套层次太深可能会有问题,
188+
有时候会超过Python的递归深度限制(参考 ``sys.getrecursionlimit()`` )。
189+
190+
可以参照8.22小节,利用生成器或迭代器来实现非递归遍历算法。
191+
192+
在跟解析和编译相关的编程中使用观察者模式是非常常见的。
193+
Python本身的 ``ast`` 模块值的关注下,可以去看看源码。
194+
9.24小节演示了一个利用 ``ast`` 模块来处理Python源代码的例子。
195+

0 commit comments

Comments
 (0)