从 Python 3 移植
Python 3 添加了很多 Python 2 没有的功能,可以把这些用法移植到使用 Python 2 的项目中。
partialmethod
顾名思义,partialmethod 是作用于类方法的 partial 函数:
def get_name(self): return self._name class Cell(object): def __init__(self): self._alive=False @property def alive(self): return self._alive def set_state(self, state): self._alive=bool(state) set_alive=partialmethod(set_state, True) set_dead=partialmethod(set_state, False) get_name=partialmethod(get_name)
这样使用它:
In : cell=Cell('cell_1') In : cell.alive Out: False In : cell.set_alive() In : cell.alive Out: True In : cell.get_name() Out: 'cell_1'
在 Python 2 中可以这样实现:
from functools import partial class partialmethod(partial): # noqa def __get__(self, instance, owner): if instance is None: return self return partial(self.func, instance, *(self.args or ()),**(self.keywords or{}))
这个使用描述符实现的版本并不是 Python 3 标准库中的实现,它只能作用于类方法,不能作用于已经被 partialmethod 包装的方法。举个 Python 3 的例子:
from functools import partialmethod class Request(object): default_url='http://www.dongwm.com' def request(self, method, url, params=None, data=None): print('execute request:{}'.format(url)) get=partialmethod(request, 'GET') post=partialmethod(request, 'POST') put=partialmethod(request, 'PUT') delete=partialmethod(request, 'DELETE') get_default_url=partialmethod(get, default_url)
使用 Python 2 版本的 partialmethod 不能包装这个例子中的 get 方法,也就是不能实现 get_default_url 这个方法。如果有这样的需求,就需要从 Python 3 标准库中移植代码了。
singledispatch
Python 3.4 为 functools 模块引入了将普通函数转换为泛型函数的工具 singledispatch。泛型设计是一种编程范式,泛型函数是一组实现不同类型输入,但是执行相同操作的的函数。
在 Python 2 中使用 singledispatch 需要安装第三方包:
> pip install singledispatch
我们知道 JSON 默认并不能序列化时间格式的值,举个例子:
import json from datetime import date, datetime class Board(object): def __init__(self, id, name, create_at=None): self.id=id self.name=name if create_at is None: create_at=datetime.now() self.create_at=create_at def to_dict(self): return{'id':self.id, 'name':self.name, 'create_at':self.create_at} board=Board(1, 'board_1')
如果想序列化 to_dict 方法的结果,就会报错:
In : print(json.dumps(board.to_dict())) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-2-9225d99f1e8c> in <module>() ----> 1 print(json.dumps(Board(1, 'board_1').to_dict())) ... TypeError:datetime.datetime(2016, 6, 08, 4, 14, 29, 857598) is not JSON serializable
这个时候需要在 to_dict 方法中对时间格式的值做特殊处理,通常是添加一个 encoder 函数,函数代码如下:
def json_serial(obj): if isinstance(obj, datetime): serial=obj.isoformat() return serial TypeError(repr(obj)+ ' is not JSON serializable') json.dumps(board.to_dict(), default=json_serial)
特殊类型越多,这样的判断也就越多,性能越差。如果使用 singledispatch 实现,则代码如下:
from singledispatch import singledispatch @singledispatch def json_encoder(obj): raise TypeError(repr(obj)+ ' is not JSON serializable') @json_encoder.register(date) @json_encoder.register(datetime) def encode_date_time(obj): return obj.isoformat() json.dumps(board.to_dict(), default=json_encoder)
除了转换函数,还可以转换方法:
from functools import update_wrapper def methdispatch(func): dispatcher=singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register=dispatcher.register update_wrapper(wrapper, func) return wrapper
现在给 Board 添加两个泛型方法:
@methdispatch def get(self, arg): return getattr(self, arg, None) @get.register(list) def_(self, arg): return [self.get(x) for x in arg]
现在就可以对 get 方法传入不同的参数获取不同类型的结果了:
In : print(board.get('name')) board_1 In : print(board.get(['id', 'create_at'])) [1, datetime.datetime(2016, 5, 17, 4, 14, 29, 857598)]
suppress
Python 3 的 contextlib 模块中,suppress 通过 with 语句忽略指定的异常,Python 2 的移植版本如下:
from contextlib import contextmanager @contextmanager def suppress(*exceptions): try: yield except exceptions: pass with suppress(OSError): os.remove('/no/such/file')
上述的删除不存在对文件的操作,所以不会抛出异常。
redirect_stdout/redirect_stderr
有时候为了调试之类的目的,希望把输出重定向到某个文件。之前的做法是:
with open('help.txt', 'w') as f: oldstdout=sys.stdout sys.stdout=f try: help(pow) finally: sys.stdout=oldstdout
Python 3 的 contextlib 提供了更优雅的方式,Python 2 的移植版本如下:
@contextmanager def redirect_stdout(fileobj, std_type='stdout'): oldstdout=getattr(sys, std_type) setattr(sys, std_type, fileobj) try: yield fileobj finally: setattr(sys, std_type, oldstdout) redirect_stderr=partial(redirect_stdout, std_type='stderr')
可以通过如下方式使用它们:
with open('help_out.txt', 'w') as out, open('help_err.txt', 'w') as err: with redirect_stdout(out), redirect_stderr(err): msg='Test' sys.stdout.write('(stdout) A:{!r}\n'.format(msg)) sys.stderr.write('(stderr) A:{!r}\n'.format(msg))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论