some posts about python
*
的使用command line argument parse
使用 [click] 作为解析库,可以方便的实现子命令操作。整理一些使用中遇到的问题。
- 对 argument 进行注释
使用多行注释的方式""" xxx """
。但是 click 默认是现在在统一行,去除掉了换行的操作。所以在有多参数的情况下显示比较乱。解决方式是:
@click.command()
@click.argument('gt', type=click.Path(exists=True))
@click.argument('prefix', type=click.Path(exists=True))
def execute(gt, prefix):
"""
\b
explain the command usage
gt: xxxx
prefix: xxxxx
"""
progess bar
使用 [tqdm] 搭配各种场景使用,比如在 requests 中显示现在的进度时,可以有如下的方式:
r = requests.get(url, stream=True, allow_redirects=True)
... # status code check
path = pathlib.Path(filename).expanduser().resolve()
path.parent.mkdir(parents=True, exist_ok=True)
desc = filename.ljust(22, ' ') # 22 is the lenght bigger than filename, should change
r.raw.read = functools.partial(r.raw.read, decode_content=True) # Decompress if needed
with tqdm.tqdm.wrapattr(r.raw, "read", total=file_size, desc=desc) as r_raw:
with path.open("wb") as f:
shutil.copyfileobj(r_raw, f)
同时也可以在命令行中使用:
find . -name '*.py' -type f -exec cat \{} \; \
| tqdm --unit loc --unit_scale --total 857366 >> /dev/null
100%|█████████████████████████████████| 857K/857K [00:04<00:00, 246Kloc/s]
更多使用参考文档 [tqdm documention] 。
requirements.txt
这个文件可以用于python项目初始化时安装依赖使用。可以通过两种方式获取到:
# 获取完整的依赖环境
$ pip3 freeze > requirements.txt
# 获取必要的依赖
$ pip install pipreqs
$ pipreqs .
# 使用
$ pip install -r requirements.txt
tar file
获取 tar.gz 文件中的顶层目录的名称,使用下面简单的方式:
archive = tarfile.open(filepath, mode='r')
print os.path.commonprefix(archive.getnames())
zip file
使用 zipfile 库,更加灵活的打包 zip 包。
from zipfile import ZipFile
with ZipFile('target.zip', 'w') as newzip:
newzip.write('directory_name')
newzip.write('file_name')
使用 shutil 中的 make_archive 函数生成的包有点奇怪(?),不如使用 zipfile 来的灵活。
subprocess
使用 subprocess 时,往往需要添加子进程中的环境变量,可以使用:
import subprocess, os
my_env = os.environ.copy()
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)
sh
sh is a full-fledged subprocess replacement for Python 2.6 - 3.8, PyPy and PyPy3 that allows you to call any program as if it were a function.
import sh
# like command run in bash. $ sed -i 's/a/A/g' filename
sh.sed(['-i', f's/a/A/g', filename]}'])
format
python 中用于格式的操作,在格式化数字的时候,可以方便的控制小数点后的面位数。具体的可以参考[string format]
"{:.2f}".format(13.949999999999999)
listdir
python 中遍历目录有好几种方式,不同的方式满足于不同的场景。
- os.listdir
列举出当前目录下所有的文件,同时我们可以通过文件类型去进行过滤。比如:
import os
files = [f for f in os.listdir('.') if os.path.isfile(f)]
dirs = [d for d in os.listdir('.') if os.path.isdir(d)]
- os.walk
walk 一般会递归去获取当前目录下所有的文件包含子目录,需要通过指定一些配置来满足我们的需求。简单的使用如下:
import os
for root, dirs, files in os.walk('.', topdown=True):
dirs.clear()
for file in files:
print(file)
dirs.clear()
用于不递归遍历当前目录下的子目录。 如果不删除,则会显示当前目录下所有的文件包含子目录里面的文件。这需要结合具体的场景。
- find(shell command with subprocess)
这种方式是结合了 shell 的一些特性,一般不得以的情况下才使用。
chunk
def chunk(iterable, n):
d = {}
for i, x in enumerate(iterable):
d.setdefault(i//n, []).append(x)
return list(d.values())
或者使用more_itertools
这个库, 可以更加简单的实现。
import more_itertools as mit
list(mit.chunked(iterable, n))
atexit
atexit 在注册时传递参数:
def goodbye(name, adjective):
print('Goodbye %s, it was %s to meet you.' % (name, adjective))
import atexit
atexit.register(goodbye, adjective='nice', name='Donny')
OrderedDict
将字典 key 转换成 list,可以按照如下的方式:
>>> from collections import OrderedDict
>>> a = OrderedDict({'a': 1, 'b':2})
>>> a.keys()
odict_keys(['a', 'b'])
>>> b = [*a]
>>> b
['a', 'b']
>>>
单纯的 keys 是odict_keys
类型,而不是list
类型。所以通过解引用直接获取生成 key 的 list 。
logging
在使用 logging 时,我们在自定义输出格式时,使用如下的方式:
logging.basicConfig(format='%(asctime)s,%(msecs)03d %(levelname)-8s [%(pathname)s:%(lineno)d in function %(funcName)s] %(message)s', datefmt='%Y-%m-%d:%H:%M:%S', level=logging.DEBUG)
注意这边的%(msecs)03d
这个是用来保留 3 位毫秒数的,方便后续的处理。
list 间隔差
t = [1,2,4,6]
v = [t[i+1]-t[i] for i in range(len(t)-1)]
# [1,2,2]
统计数据分布
from itertools import groupby
def histogram(data, step):
for k, g in groupby(sorted(data), key=lambda x: x//step):
print('{}-{}: {}'.format(k*step, (k+1)*step-1, len(list(g))))
随机采样
import random
import glob
pbfiles = [f for f in glob.glob("trt_data_pb/*.pb")]
random_pb = random.sample(pbfiles, 1000)
print(random_pb[:10])
采用random.sample
这个可以在一组 list 中,随机获取指定数量的内容。 如果只是想选一个,可以使用random.choice
pprint
正常使用 print 的打印效果一般,不够 pretty, 所以我们可以使用 pprint 库来完成更好的打印,正常情况下我们会使用到一些基本的参数来控制打印的效果。同时可以通过自定义统一的打印风格。
>>> from pprint import PrettyPrinter
>>> custom_printer = PrettyPrinter(
... indent=4,
... width=100,
... depth=2,
... compact=True,
... sort_dicts=False
... )
...
>>> user={"name": "jesse", "age": 1, "address": {"street": "xxx", "city": "xxxxx"}}
>>> custom_printer.pprint(user)
{'name': 'jesse', 'age': 1, 'address': {'street': 'xxx', 'city': 'xxxxx'}}
实际上我们可以通过单独设置的方式来打印这些内容,比如:
>>> from pprint import pprint
>>> pprint(users, indent=4, depth=2)