1 流式读取数G大文件
使用read方法,指定每次指定读取固定大小的内容。下面的代码每次只读取8K的数据
方法一
def read_from_file(filename,block_size= 1024*8):
with open(filename,'r') as fp:
while True:
chunk = fp.read(block_size)
if not chunk:
break
yield chunk
方法二
借助辅助函数
from functools import partial
def read_from_file(filename,block_size=1024*8):
with open(filename,'r') as fp:
for chunk in iter(partial(fp.read,block_size)):
yield chunk
2 利用自带的缓存机制提高代码的运行效率
缓存是一种将定量数据加以保存,以备迎合后续需求的处理方式,旨在加快数据获取的速度。在实际的工程中数据的生成过程中可能需要经过计算,规整、远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获取得得数据缓存下来,会加快后续得数据获取需求。为了实现该需求,python中提供了一个方法,使用functool模块中得lru_cache装饰器
@functools.lru_cache(maxsize=None, typed=False)
参数解读:
- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 2 的幂时,性能最佳
- typed:若为 True,则不同参数类型的调用将分别缓存。
from functools import lru_cache
@lru_cache(None)
def add(x,y):
print('calculating:%s+%s'%(x,y))
return x+y
print(add(1,2))
print(add(1,2))
print(add(7,2))
输出:
calculating: 3 + 3
3
3
calculating: 7 + 2
9
从上面得代码可以看出,第一次调用时执行了函数,而第二次并没有执行函数,而是直接返回缓存得结果。第三次当传入新的数据时开始调用函数。
测试该方法的运行速度。
import timeit
def fib(n):
if n < 2:
return n
return fib(n - 2) + fib(n - 1)
#使用timeit测试运行的时间,单位为s(秒)
print(timeit.timeit(lambda :fib(40), number=1))
# output: 31.2725698948
import timeit
from functools import lru_cache
@lru_cache(None)
def fib(n):
if n < 2:
return n
return fib(n - 2) + fib(n - 1)
print(timeit.timeit(lambda :fib(500), number=1))
# output: 0.0004921059880871326
由上可以发现,在使用lru_cache速度加快很多。
3 print函数将输出写入日志中
print函数可以将结果打印在控制台中,同时也可以将结果输入文件中。
with open('aa.log','w') as f:
print('hahahah',file=f,flush=True)
参数解读:
flush:
功能:把缓冲区的数据强行输出,清空缓冲区。
作用:
- 在该问题下:在读写流中数据先被读到内存(相当于缓存区),再写入,中途用close()方法关闭读写流,缓存数据会丢失。
- 用flush()方法:刷新缓存区,强制把其中数据写入文件,清空缓冲区,避免数据丢失。
- 在print里flush默认是False,写成True后每次调用执行该语句时,都会把语句的内容放到文件中(如果有用file方法的话)。
4 将嵌套循环改写成单循环
在实际中我们常常使用嵌套循环,for
import numpy as np
a = np.arange(1,10)
b = np.arange(1,10)
c = np.arange(1,10)
for i in a:
for j in b:
for k in c:
print(k+j+i)
该方法很丑,可以使用itertools中的product进行替换
from itertools import product
import numpy as np
a = np.arange(1,10)
b = np.arange(1,10)
c = np.arange(1,10)
for i,j,k in product(a,b,c):
print(i+j+k)
输出结果
3
4
5
6
7
8
.
.
.
关闭异常自动关联上下文
当你在处理异常时,由于处理不当或者其他问题,再次抛出另一个异常时,往外抛出的异常也会携带原始的异常信息。如下所示。
try:
print(1 / 0)
except Exception as exc:
raise RuntimeError("Something bad happened")
从输出可以看到两个异常信息
Traceback (most recent call last):
File "demo.py", line 2, in <module>
print(1 / 0)
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "demo.py", line 4, in <module>
raise RuntimeError("Something bad happened")
RuntimeError: Something bad happened
如果在异常处理程序或 finally
块中引发异常,默认情况下,异常机制会隐式工作会将先前的异常附加为新异常的```context``属性。这就是 Python 默认开启的自动关联异常上下文。
如果你想自己控制这个上下文,可以加个 from
关键字(from
语法会有个限制,就是第二个表达式必须是另一个异常类或实例。),来表明你的新异常是直接由哪个异常引起的。
try:
print(1 / 0)
except Exception as exc:
raise RuntimeError("Something bad happened") from exc
输出如下
Traceback (most recent call last):
File "demo.py", line 2, in <module>
print(1 / 0)
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "demo.py", line 4, in <module>
raise RuntimeError("Something bad happened") from exc
RuntimeError: Something bad happened
当然,你也可以通过with_traceback()
方法为异常设置上下文__context__
属性,这也能在traceback
更好的显示异常信息。
try:
print(1 / 0)
except Exception as exc:
raise RuntimeError("bad thing").with_traceback(exc)
最后,如果我想彻底关闭这个自动关联异常上下文的机制?有什么办法呢?
可以使用 raise...from None
,从下面的例子上看,已经没有了原始异常
$ cat demo.py
try:
print(1 / 0)
except Exception as exc:
raise RuntimeError("Something bad happened") from None
$
$ python demo.py
Traceback (most recent call last):
File "demo.py", line 4, in <module>
raise RuntimeError("Something bad happened") from None
RuntimeError: Something bad happened
(PythonCodingTime)