问题

你想在一个文件里每次读入固定大小的字节,比如每次读入4个字节并转成int,或者每次读入x个字节并进行结构化,例如:

l = [5, 2, 4, 1, 2, 4, 5, 6, 8]
with open('test', 'wb') as fd:
    for num in l:
        fd.write(num.to_bytes(4, 'big'))

解决方案

可以简单的用while循环来完成

with open('test', 'rb') as fd:
    r = fd.read(4)
    while r:
        print(int.from_bytes(r, 'big'), end=' ')
        r = fd.read(4)

输出为5 2 4 1 2 4 5 6 8

但更优雅的做法是结合使用iterfunctools.partial

from functools import partial
with open('test', 'rb') as fd:
    for r in iter(partial(fd.read, 4), b''):
        print(int.from_bytes(r, 'big'), end=' ')

输出同样是5 2 4 1 2 4 5 6 8

讨论

functools.partial的作用是对一个函数进行包装(可以将一些参数的值固定)并生成一个新的签名,例如:

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18

functools.partial的大致实现如下:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

iter的作用是当只有一个参数时,对这个对象进行迭代,所以这个对象必须实现__iter()____getitem()__方法,例如:

>>> l = [1, 2, 3, 4]
>>> for i in iter(l):
...     print(i, end=' ')
...
>>> 1 2 3 4

但当有两个参数时,第一个参数必须是可调用的(比如函数),第二个参数是终止的值,当调用第一个参数返回的结果等于第二个参数时,迭代就停止了,例如:

with open('mydata.txt') as fp:
    for line in iter(fp.readline, ''):
        process_line(line)

所以综合两个方法来看上面的iter(partial(fd.read, 4), b''),就是每次调用fd.read(4)直到返回一个空的bytes

来源

Python Cookbook

关注

欢迎关注我的微信公众号:python每日一练


python每日一练
351 声望754 粉丝