python面对对象编程----------7:callable(类调用)与context(上下文)
一:callables
callables使类实例能够像函数一样被调用
如果类需要一个函数型接口这时用callable,最好继承自abc.Callable,这样有些检查机制并且一看就知道此类的目的是callable对象
如果类需要有‘记忆’功能,使用callable是非常方便的相对于函数而言,callable语法什么的就要复杂多了,这也是其主要的缺点:
def x(args):
body
转化为callable对象:
class X(collections.abc.callable):
def __call__(self, args):
body
x= X()
1:计算x^y
1 import collections.abc #注,完全可以不引入cleection.abc,引入是为了能够做一些错误检查 2 class Power1( collections.abc.Callable ): 3 def __call__( self, x, n ): 4 p= 1 5 for i in range(n): 6 p *= x 7 return p 8 9 power= Power1() 10 >>> power( 2, 0 ) #像函数一样调用实例 11 1 12 >>> power( 2, 1 ) 13 2 14 >>> power( 2, 2 ) 15 4 16 >>> power( 2, 10 ) 17 1024 18 19 # 提升性能:上面是O(n),用递归改进为O(logn) 20 class Power4( abc.Callable ): 21 def __call__( self, x, n ): 22 if n == 0: 23 return 1 24 elif n % 2 == 1: 25 return self.__call__(x, n-1)*x 26 else: 27 t= self.__call__(x, n//2) 28 return t*t 29 30 pow4= Power4() 31 32 # 再次提升性能,使用记忆功能【注:可以{(2,4):16,... } 33 class Power5( collections.abc.Callable ): 34 def __init__( self ): 35 self.memo = {} 36 def __call__( self, x, n ): 37 if (x,n) not in self.memo: 38 if n == 0: 39 self.memo[x,n]= 1 40 elif n % 2 == 1: 41 self.memo[x,n]= self.__call__(x, n-1) * x 42 elif n % 2 == 0: 43 t= self.__call__(x, n//2) 44 self.memo[x,n]= t*t 45 else: 46 raise Exception("Logic Error") 47 return self.memo[x,n] 48 49 pow5= Power5() 50 51 # 再次改进,python库自带了一个记忆装饰器,可以使用这个从而不不用自定义callable对象 52 from functools import lru_cache 53 @lru_cache(None) 54 def pow6( x, n ): 55 if n == 0: 56 return 1 57 elif n % 2 == 1: 58 return pow6(x, n-1)*x 59 else: 60 t= pow6(x, n//2) 61 return t*t 62 # Previous requests are stored in a memoization cache. The requests are 63 # tracked in the cache, and the size is limited. The idea behind an LRU cache is that 64 # the most recently made requests are kept and the least recently made requests are quietly purged.
2:赌注翻倍:综合运用callables,输家翻倍赌注政策:每输一次后赌注就加倍直到赢了后回归原本赌注
1 class BettingMartingale( BettingStrategy ): 2 def __init__( self ): 3 self._win= 0 4 self._loss= 0 5 self.stage= 1 6 @property 7 def win(self): 8 return self._win 9 @win.setter 10 def win(self, value): 11 self._win = value 12 self.stage= 1 13 @property 14 def loss(self): 15 return self._loss 16 @loss.setter 17 def loss(self, value): 18 self._loss = value 19 self.stage *= 2 20 21 def __call__( self ): 22 return self.stage 23 24 >>> bet= BettingMartingale() 25 >>> bet() 26 1 27 >>> bet.win += 1 28 >>> bet() 29 1 30 >>> bet.loss += 1 31 >>> bet() 32 2 33 34 # property的使用使类的定义显得冗杂,实际上我们只关心setters,所以我们用__setattr__来改进上述版本 35 class BettingMartingale2( BettingStrategy ): 36 def __init__( self ): 37 self.win= 0 38 self.loss= 0 39 self.stage= 1 40 def __setattr__( self, name, value ): 41 if name == 'win': 42 self.stage = 1 43 elif name == 'loss': 44 self.stage *= 2 45 super().__setattr__( name, value ) 46 def __call__( self ): 47 return self.stage
二:context
A context is generally used to acquire/release, open/close, and lock/unlock types of operation pairs.
Most of the examples are file I/O related, and most of the file-like objects in Python are already proper context managers.
1:一些context
1:最常见的是用在文件的,with语句创建
2:decimal context:decimal是一个模块,常用于一些对于精度要求比较严格的计算,其本身运行在一个context中,通过改context可以对全局的计算产生影响
3:还有一些context,主要都是用于类文件的操作
2:构造context(第八章会详细讲解构造context)
context最主要的是有__enter__()与__exit__()方法,分别在with语句开始和结束时调用
抛出的问题都会以traceback参数传递到__exit__()函数中,应该做相应处理。
例子:错误处理context:在打开文件时做备份,若处理完文件没出问题就删除备份,若出了问题就使用备份来恢复原文件
1 import os 2 class Updating: 3 def __init__( self, filename ): 4 self.filename= filename 5 def __enter__( self ): #做文件备份 6 try: 7 self.previous= self.filename+" copy" 8 os.rename( self.filename, self.previous ) 9 except FileNotFoundError: 10 # Never existed, no previous copy 11 self.previous= None 12 13 def __exit__( self, exc_type, exc_value, traceback ): # 14 if exc_type is not None: 15 try: 16 os.rename( self.filename, self.filename+ " error" ) 17 except FileNotFoundError: 18 pass # Never even got created? 19 if self.previous: 20 os.rename( self.previous, self.filename ) #用备份文件恢复原文件 21 22 with Updating( "some_file" ): 23 with open( "some_file", "w" ) as target: 24 process( target )