1 命令行及环境配置

1.1 PIP参数

  • 波浪线tilde equals (~=),选择包的当前大版本的最高子版本 (Stackoverflow) example: mock-django~=0.6.10

    It will select the latest version of the package, greater than or equal to 0.6.10, but still in the 0.6.* version, so it won’t download 0.7.0 for example.

1.2 检查安装的模块版本

  • 代码
import sys
import pkg_resources
 
if __name__ == '__main__':
    m_name = sys.argv[1]
    try:
        v = pkg_resources.get_distribution(m_name).version
        print("{}'s version is: {}".format(m_name, v))
    except:
        print("No such a module installed.")

2 基本语法

2.1 Ellipsis (”…“)

  • singleton Ellipsis object A placeholder for not-yet-written code,个人认为是比pass更好用的平替

2.2 Python中的Null类型 — None/NoneType

  • Python中的Null类型为None,可以使用if foo is None来判断

2.3 ','的拆包

2.3.1 拆包符号的使用

2.3.2 拆包符号的运算优先级

  • 拆包中不能放入连续的运算,','前后的变量是同时完成运算(可以认为是并行),没有先后运算的时序关系,如
    a, b = 1, 2
    a, b = 2, a+1
    输出结果为a=2b=2,进行b的运算时并没有采用更新值,而是用之前初始化的值

2.4 判断列表是否存在,或是否为空

x=Nonex=[]x=[1,2,3]
if x is NoneTrueFalseFalse
if not xTrueTrueFalse
if not (x is None)FalseTrueTrue
if x is not NoneFalseTrueTrue
  • NoneFalse,空字符串""0,空列表[],空字典{},空元组()都相当于False,因此if not x不能对其进行区分
  • if x is not [] 不能用来判断空列表!

2.5 列表list及相关使用细节

多维列表根据某个位置的元素进行排序

  • 使用sort()函数和lambda
    • reverseFalse为升序排列,True为降序排列
your_list = [[1,3],[32,5],[23,2],[48,1],[48,4],[32,0]]
your_list.sort(reverse=True, key=lambda x: x[-1])

2.5.1 两个list变量相互赋值,浅拷贝shallow copy与深拷贝deep copy

  • 浅拷贝shallow copy对应C语言中按地址传递。深拷贝deep copy对应C语言中按值传递
  • 如果声明了一个list类型变量a,再通过b=a声明b时,是浅拷贝,之后对a的修改也会反应到变量中
  • 类似情况如声明全零二维列表
  • 如果在a的基础上,声明一个列表,则为深拷贝,之后对a的修改,对于b没有影响,如
    a = [1,2,3]
    b = a+[0]
    ## b = [1,2,3,0]
    a[1] = [10]
    ## a = [1,10,3]
    ## b = [1,2,3,0]

2.5.2 批量修改列表中元素(如一个列表中每个元素均为np.array)

  • 使用__setitem__()方法
    a = [np.eye(4) for i in range(7)]
    [a[i].__setitem__((0, 3), 0.006) for i in range(3)]

2.5.3 声明全零二维列表

2.5.3.1 错误方法:
a = [[0]*m]*n

这个列表中每一行[0]*m都指向同一实例,改变其中一个元素的值将同时改变每一行的值 这个方法相当于先声明了[0,0,0,...,0],再将其复制n

2.5.3.2 正确方法
a = [ [0]*m for _ in range(n)]

此二维列表中每个元素都是独立的 这个方法相当于每次都重新声明[0,0,0,...,0],共重复n

2.5.4 Range()

  • 在一个区间进行计数
    • range(x1,x2)的计数从x1开始,到x2-1结束,若生成列表,则为[x1, x1+1, x1+2, ..., x2-1]
  • 倒序计数
    • range(n-1, -1, -1)n-1反向计数至0

2.6 跳出多层循环

  • 将多层循环包装为一个函数,使用return
  • 抛出异常
    class GetOutOfLoop(Exception):
        pass
     
    try:
        for i in ...:
            for j in ...:
                if ...:
                    pass
                else:
                    raise GetOutOfLoop
    except GetOutOfLoop:
        pass

2.7 用Ctrl+c跳出程序的主循环

  • 一般方法
try:
    while True:
        do_something()
except KeyboardInterrupt:
    pass
 
  • ROS
while not rospy.is_shutdown():
    ...

2.8 格式化输出

2.8.1 print/file.write的格式化输出

  • 使用print("your content here with {}".format(data))
  • 指定整数或者小数的位数print("data: {:3.4f}".format(your_float))
  • print("data: {:3.4}".format(your_float))若没有f,则为使用科学计数法表示

2.8.2 NumPy默认输出为科学计数法

2.9 构建数据类型(使用dataclass)

  • 需要引用dataclassesdataclassfield
  • 如果为多层嵌套,其内部所嵌套的每层声明应当用field进行初始化,否则会导致声明指向同一变量(Python stores default member variable values in class attributes.), 参考
    • 正确示例 输出为12
    from dataclasses import dataclass, field
     
    ## inner most data
    @dataclass
    class Lv0:
        x: float=0
    ## the structure in the middle
    @dataclass
    class Lv1:
        y0: Lv0 = field(default_factory=lambda: Lv0()) # not y0: Lv0 = Lv0()
     
    ## outer most data
    @dataclass
    class Lv2:
        z0: Lv1 = field(default_factory=lambda: Lv1())
     
    if __name__ == '__main__':
        a = Lv2()
        b = Lv2()
        a.z0.y0.x = 1
        b.z0.y0.x = 2
        print(a.z0.y0.x) ## get 1
        print(b.z0.y0.x) ## get 2
    • 错误示例,ab.z0.y0指向了同一变量,输出为22
    from dataclasses import dataclass, field
     
    @dataclass
    class Lv0:
        x: float=0
     
    @dataclass
    class Lv1:
        y0: Lv0 = Lv0()
     
    @dataclass
    class Lv2:
        z0: Lv1 = field(default_factory=lambda: Lv1())
     
    if __name__ == '__main__':
        a = Lv2()
        b = Lv2()
        a.z0.y0.x = 1
        b.z0.y0.x = 2
        print(a.z0.y0.x) ## get 2
        print(b.z0.y0.x) ## get 2

3 如何导入在其他文件夹中的模块/同一子文件夹中的模块

  • 假设当前脚本的结构为:
      |->main.py
      |->utils/
              |->vision/
                  |->rgb_process.py
                  |->post_process.py
              |->control/
      |->others/
    
    要在post_pocess.py中引用rgb_process中的object_mask
sys.path.append('../../')
from utils.vision.rgb_process import object_mask

4 添加命令行参数

  • 示例
import argparse
 
if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('-init', '--init', action = 'store_true', help = 'initializing')     # add_argument()指定程序可以接受的命令行选项
    args = parser.parse_args()      # parse_args()从指定的选项中返回一些数据
    print(args)
    print(args.init)
 
  • action='store_true' 上例中args.init的值将被设置为True

5 文件操作

5.1 获取文件夹下所有特定扩展名的文件名

  • 示例
def get_files(path, extention = '.txt'):
    find_files = []
    f_list = os.listdir(path)
    for i in f_list:
        if os.path.splitext(i)[-1] == extention:
            find_files.append(i)
 
    return find_files

6 时间

6.1 计时

  • 方法一:单位为秒,代码示例
import timeit
 
start = timeit.default_timer()
## DO SOMETHING
stop = timeit.default_timer()
print("spent {}".format(stop-start))
  • 方法二:单位为秒,代码示例
import time
start = time.perf_counter()
## DO SOMETHING
stop  = time.perf_counter()
print("Runtime: {}ms".format((stop-start)*1000)

用时间戳作为文件名

  • 使用函数time.strftime,代码示例
    filename = time.strftime("%m-%d_%H-%M-%S", time.localtime(time.time()))