命令行及环境配置

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.

检查安装的模块版本

  • 代码
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.")

虚拟环境venv

  • 如果有多个Python版本(如Ubuntu20.04有默认的Python 3.8并额外安装Python 3.12),对于3.12版需安装sudo apt install python3.12-venv。以下以3.12为例
  • 创建虚拟环境
python3.12 -m venv myenv
  • Windows下激活虚拟环境的脚本
@echo off
cmd /k ".\myenv\Scripts\activate.bat"
  • Linux下激活虚拟环境的脚本
source ./myenv/bin/activate
  • 如果创建了虚拟环境后,默认的Python就为对应版本
  • 停用虚拟环境 deactivate
  • 安装OpenCV pip install opencv-contrib-python

基本语法

Ellipsis (”…“)

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

Python中的Null类型 — None/NoneType

  • Python中的Null类型为None,可以使用if foo is None来判断,注意is==的区别,参考[[4.software/programming_language/python/00.python_basic#-和is用于比较|==is用于比较]]

','的拆包

拆包符号的使用

拆包符号的运算优先级

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

==is用于比较

  • a==b用于比较两个的值是否相等,a is b比较a是否就是b(简单理解就是和b完全一致,不存在0也可以认为是False1也可以认为是True的情况),举例:
a的值运算结果
a=0a==FalseTrue
a=0a is FalseFalse
a=1a==FalseFalse
a=1a==TrueTrue
a=1a is TrueFalse
a=2a==TrueFalse

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

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 [] 不能用来判断空列表!

列表list及相关使用细节

遍历列表并删除满足某个条件的数据

  • 从后向前遍历,使用del()
a = [1,2,3,4,5,6]
for i in range(len(a)-1,-1,-1):
    if a[i] %2 == 1:
        del(a[i])

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

  • 使用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])

输出为[[32, 5], [48, 4], [1, 3], [23, 2], [48, 1], [32, 0]]

两个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]

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

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

声明全零二维列表

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

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

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

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

Range()

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

跳出多层循环

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

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

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

格式化输出

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,则为使用科学计数法表示

NumPy默认输出为科学计数法

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

  • 假设当前脚本的结构为:
      |->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

配置与参数

Python的自带参数

调试Flag

  • Python的__debug__为调试flag,使用-O参数时执行优化(python -O test.py参数必须在中间),从而忽略__debug__中的内容,若无参数,则执行debug中的内容
if __debug__:
    print('Debug ON')
else:
    print('Debug OFF')

添加命令行参数

  • 示例
import argparse
 
if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('-init', '--init', default=False, help = 'initializing')     # add_argument()指定程序可以接受的命令行选项
    args = parser.parse_args()      # parse_args()从指定的选项中返回一些数据
    print(args)
    print(args.init)
 
  • action:如果命令中包含参数-init,则其值将被设置为True(例如parser.add_argument('-init', '--init', action="store_true"),命令为python main.py -init
  • default:参数的默认值

跨文件的全局参数

  • 参考python - Using global variables between files? - Stack Overflow
    • 将所有全局变量移至一个文件,如settings.py,这个文件里定义并且初始化所有全局变量
    # settings.py
     
    def init():
        global myList
        myList = []
    • subfile中导入全局变量
    # subfile.py
     
    import settings
     
    def stuff():
        settings.myList.append('hey')
    • init()只需要在main.py中调用,不需要在subfile或者其它文件中调用
    # main.py
     
    import settings
    import subfile
     
    settings.init()          # Call only once
    subfile.stuff()         # Do stuff with global var
    print settings.myList[0] # Check the result

This way, you achieve your objective while avoid initializing global variables more than once.

文件操作

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

  • 方法一
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
  • 方法二
folder_path = "../../debug"
npz_files = [
    f for f in os.listdir(folder_path)
    if f.endswith(".npz") and os.path.isfile(os.path.join(folder_path, f))
]
 

时间、计时器与程序效率

用时间戳作为文件名

  • 使用函数time.strftime,注意time只能提供到秒,代码示例
    filename = time.strftime("%m-%d_%H-%M-%S", time.localtime(time.time()))
  • 使用datetime
    from datetime import datetime
    filename = datetime.now().strftime("%m-%d_%H-%M-%S-%f")[:-3]

计时

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

程序效率

  • print()耗时的数量级为0.01ms
  • 尽量少使用循环,尤其是嵌套循环(如访问二维矩阵)