- class A:
- def spam(self, x):
- pass
- def foo(self):
- pass
- class B1:
- """简单的代理"""
- def __init__(self):
- self._a = A()
- def spam(self, x):
- # Delegate to the internal self._a instance
- return self._a.spam(x)
- def foo(self):
- # Delegate to the internal self._a instance
- return self._a.foo()
- def bar(self):
- pass
我们可以通过 B1 的实例化去访问到 A 类, 如果仅仅就两个方法需要代理, 那么像这样写就足够了. 但是, 如果有大量的方法需要代理, 那么使用 __getattr__() 方法或许或更好些:
- class B2:
- """使用__getattr__的代理, 代理方法比较多时候"""
- def __init__(self):
- self._a = A()
- def bar(self):
- pass
- # Expose all of the methods defined on class A
- def __getattr__(self, name):
- """ 这个方法在访问的 attribute 不存在的时候被调用
- the __getattr__() method is actually a fallback method
- that only gets called when an attribute is not found"""
- return getattr(self._a, name)
另外一个代理例子是实现代理模式,
- class Proxy:
- def __init__(self, obj):
- self._obj = obj
- # Delegate attribute lookup to internal obj
- def __getattr__(self, name):
- print('getattr:', name)
- return getattr(self._obj, name)
- # Delegate attribute assignment
- def __setattr__(self, name, value):
- if name.startswith('_'):
- super().__setattr__(name, value)
- else:
- print('setattr:', name, value)
- setattr(self._obj, name, value)
- # Delegate attribute deletion
- def __delattr__(self, name):
- if name.startswith('_'):
- super().__delattr__(name)
- else:
- print('delattr:', name)
- delattr(self._obj, name)
- class Spam:
- def __init__(self, x):
- self.x = x
- def bar(self, y):
- print('Spam.bar:', self.x, y)
- # Create an instance
- s = Spam(2)
- # Create a proxy around it
- p = Proxy(s)
- # Access the proxy
- print(p.x) # Outputs 2
- p.bar(3) # Outputs "Spam.bar: 2 3"
- p.x = 37 # Changes s.x to 37
我们可以将一个类的实例化传入到另一个类中, 再次实例化就可以达到属性代理的访问模式. 通过自定义属性访问方法, 你可以用不同方式自定义代理类行为(比如加入日志功能, 只读访问等).
七, 你想创建一个实例, 但是希望绕过执行 __init__() 方法.
遗漏点: 可以通过 __new__() 方法创建一个未初始化的实例. 例如考虑如下这个类:
- class Date:
- def __init__(self, year, month, day):
- self.year = year
- self.month = month
- self.day = day
如果 Date 实例的属性 year 还不存在, 所以你需要手动初始化:
- >>> d = Date.__new__(Date)
- >>> d
- <__main__.Date object at 0x1006716d0>
- >>> d.year
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- AttributeError: 'Date' object has no attribute 'year'
- >>>
- >>> data = {'year':2012, 'month':8, 'day':29}
- >>> for key, value in data.items():
- ... setattr(d, key, value)
- ...
- >>> d.year
- 2012
- >>> d.month
- 8
- >>>
八, 你想实现一个状态机或者是在不同状态下执行操作的对象, 但是又不想在代码中出现太多的条件判断语句.
- class Connection:
- """普通方案, 好多个判断语句, 效率低下~~"""
- def __init__(self):
- self.state = 'CLOSED'
- def read(self):
- if self.state != 'OPEN':
- raise RuntimeError('Not open')
- print('reading')
- def write(self, data):
- if self.state != 'OPEN':
- raise RuntimeError('Not open')
- print('writing')
- def open(self):
- if self.state == 'OPEN':
- raise RuntimeError('Already open')
- self.state = 'OPEN'
- def close(self):
- if self.state == 'CLOSED':
- raise RuntimeError('Already closed')
- self.state = 'CLOSED'
这样写有很多缺点, 首先是代码太复杂了, 好多的条件判断. 其次是执行效率变低, 因为一些常见的操作比如 read(),write()每次执行前都需要执行检查.
一个更好的办法是为每个状态定义一个对象:
- class Connection1:
- """新方案 -- 对每个状态定义一个类"""
- def __init__(self):
- self.new_state(ClosedConnectionState)
- def new_state(self, newstate):
- self._state = newstate
- # Delegate to the state class
- def read(self):
- return self._state.read(self)
- def write(self, data):
- return self._state.write(self, data)
- def open(self):
- return self._state.open(self)
- def close(self):
- return self._state.close(self)
- # Connection state base class
- class ConnectionState:
- @staticmethod
- def read(conn):
- raise NotImplementedError()
- @staticmethod
- def write(conn, data):
- raise NotImplementedError()
- @staticmethod
- def open(conn):
- raise NotImplementedError()
- @staticmethod
- def close(conn):
- raise NotImplementedError()
- # Implementation of different states
- class ClosedConnectionState(ConnectionState):
- @staticmethod
- def read(conn):
- raise RuntimeError('Not open')
- @staticmethod
- def write(conn, data):
- raise RuntimeError('Not open')
- @staticmethod
- def open(conn):
- conn.new_state(OpenConnectionState)
- @staticmethod
- def close(conn):
- raise RuntimeError('Already closed')
- class OpenConnectionState(ConnectionState):
- @staticmethod
- def read(conn):
- print('reading')
- @staticmethod
- def write(conn, data):
- print('writing')
- @staticmethod
- def open(conn):
- raise RuntimeError('Already open')
- @staticmethod
- def close(conn):
- conn.new_state(ClosedConnectionState)
让我们跟着演示走一下流程, 你会清晰一点.
- >>> c = Connection() # 实例化 init 方法执行了 new_state(ClosedConnectionState) --> _state = ClosedConnectionState
- >>> c._state # 查看类
- <class '__main__.ClosedConnectionState'>
- >>> c.read() #在 ClosedConnectionState 类中如果直接 read 会 raise RuntimeError('Not open')
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "example.py", line 10, in read
- return self._state.read(self)
- File "example.py", line 43, in read
- raise RuntimeError('Not open')
- RuntimeError: Not open
- >>> c.open() # 执行了 ClosedConnectionState 类 open 方法 --> _state=OpenConnectionState
- >>> c._state # 在 OpenConnectionState 类中可以调用相关方法
- <class '__main__.OpenConnectionState'>
- >>> c.read()
- reading
- >>> c.write('hello')
- writing
- >>> c.close()
- >>> c._state
- <class '__main__.ClosedConnectionState'>
- >>>
注意: 在状态类中, 我们要调用静态方法修饰的目的是可以传递 self 参数, 也就是 Connection 类, 这样我们可以切换不同的状态. 在不同的状态中方法可以写不同的业务逻辑. 设计模式中有一种模式叫状态模式, 这一部分算是一个初步入门!
九, 实现访问者模式
假设你要写一个表示数学表达式的程序, 1 + 2 * (3 - 4) / 5, 那么你可能需要定义如下的类:
- class Node:
- pass
- class UnaryOperator(Node):
- def __init__(self, operand):
- self.operand = operand
- class BinaryOperator(Node):
- def __init__(self, left, right):
- self.left = left
- self.right = right
- class Add(BinaryOperator):
- pass
- class Sub(BinaryOperator):
- pass
- class Mul(BinaryOperator):
- pass
- class Div(BinaryOperator):
- pass
- class Negate(UnaryOperator):
- pass
- class Number(Node):
- def __init__(self, value):
- self.value = value
然后利用这些类构建嵌套数据结构, 如下所示:
- # Representation of 1 + 2 * (3 - 4) / 5
- t1 = Sub(Number(3), Number(4))
- t2 = Mul(Number(2), t1)
- t3 = Div(t2, Number(5))
- t4 = Add(Number(1), t3)
这样做的问题是对于每个表达式, 每次都要重新定义一遍, 有没有一种更通用的方式让它支持所有的数字和操作符呢. 这里我们使用访问者模式可以达到这样的目的:
- class NodeVisitor:
- def visit(self, node):
- methname = 'visit_' + type(node).__name__
- meth = getattr(self, methname, None)
- if meth is None:
- meth = self.generic_visit
- return meth(node)
- def generic_visit(self, node):
- raise RuntimeError('No {} method'.format('visit_' + type(node).__name__))
为了使用这个类, 可以定义一个类继承它并且实现各种 visit_Name() 方法, 其中 Name 是 node 类型. 例如, 如果你想求表达式的值, 可以这样写:
- class Evaluator(NodeVisitor):
- def visit_Number(self, node):
- return node.value
- def visit_Add(self, node):
- return self.visit(node.left) + self.visit(node.right)
- def visit_Sub(self, node):
- return self.visit(node.left) - self.visit(node.right)
- def visit_Mul(self, node):
- return self.visit(node.left) * self.visit(node.right)
- def visit_Div(self, node):
- return self.visit(node.left) / self.visit(node.right)
- def visit_Negate(self, node):
- return -node.operand
使用示例, 内部过程有点绕, 让我们再过一下流程:
- >>> e = Evaluator()
- # 实例化
- >>> e.visit(t4)
- # 执行了 visit 方法, t4 是 ADD 类, 所以 methname=visit_add, 利用反射 meth=getattr(self,visit_add,None)
--> Evaluator.visit_Add(t4) --> 在根据 visit_Add 的表达式来分解, 一直递归.
0.6
访问者模式本质就是根据对象的不同, 执行不同的访问方法.
总结一下, 我们定义的功能类, 要继承访问者的处理类 NodeVisitor, 并且还要根据不同的访问方法来定义不同的功能函数, 这个功能函数会用到 NodeVisitor 里的 visit 方法, 可以根据字符串处理继续递归访问下去.
十, 你想让某个类的实例支持标准的比较运算(比如>=,!=,<=,<等), 但是又不想去实现那一大丢的特殊方法.
Python 类对每个比较操作都需要实现一个特殊方法来支持. 例如为了支持>= 操作符, 你需要定义一个 __ge__() 方法. 尽管定义一个方法没什么问题, 但如果要你实现所有可能的比较方法那就有点烦人了.
遗漏点: 装饰器 functools.total_ordering 就是用来简化这个处理的. 使用它来装饰一个来, 你只需定义一个 __eq__() 方法, 外加其他方法 (__lt__, __le__, __gt__, or __ge__) 中的一个即可. 然后装饰器会自动为你填充其它比较方法.
作为例子, 我们构建一些房子, 然后给它们增加一些房间, 最后通过房子大小来比较它们:
- from functools import total_ordering
- class Room:
- def __init__(self, name, length, width):
- self.name = name
- self.length = length
- self.width = width
- self.square_feet = self.length * self.width
- @total_ordering
- class House:
- def __init__(self, name, style):
- self.name = name
- self.style = style
- self.rooms = list()
- @property
- def living_space_footage(self):
- return sum(r.square_feet for r in self.rooms)
- def add_room(self, room):
- self.rooms.append(room)
- def __str__(self):
- return '{}: {} square foot {}'.format(self.name,
- self.living_space_footage,
- self.style)
- def __eq__(self, other):
- return self.living_space_footage == other.living_space_footage
- def __lt__(self, other):
- return self.living_space_footage <other.living_space_footage
这里我们只是给 House 类定义了两个方法:__eq__() 和 __lt__() , 它就能支持所有的比较操作:
- h1 = House('h1', 'Cape')
- h1.add_room(Room('Master Bedroom', 14, 21))
- h1.add_room(Room('Living Room', 18, 20))
- h1.add_room(Room('Kitchen', 12, 16))
- h1.add_room(Room('Office', 12, 12))
- h2 = House('h2', 'Ranch')
- h2.add_room(Room('Master Bedroom', 14, 21))
- h2.add_room(Room('Living Room', 18, 20))
- h2.add_room(Room('Kitchen', 12, 16))
- h3 = House('h3', 'Split')
- h3.add_room(Room('Master Bedroom', 14, 21))
- h3.add_room(Room('Living Room', 18, 20))
- h3.add_room(Room('Office', 12, 16))
- h3.add_room(Room('Kitchen', 15, 17))
- houses = [h1, h2, h3]
- print('Is h1 bigger than h2?', h1> h2) # prints True
- print('Is h2 smaller than h3?', h2 <h3) # prints True
- print('Is h2 greater than or equal to h1?', h2>= h1) # Prints False
- print('Which one is biggest?', max(houses)) # Prints 'h3: 1101-square-foot Split'
- print('Which is smallest?', min(houses)) # Prints 'h2: 846-square-foot Ranch'
来源: http://www.bubuko.com/infodetail-3045451.html