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