点进来复习
OpenCV
cv2.waitKey()
1 | k = cv2.waitKey(30) & 0xff |
cv2.waitKey()的参数(代表milliseconds)是0时,无返回值,否则返回这段时间内的按键ASCII码,超过时间无按键,返回 -1。
& 0xff
防止在某些系统中键盘返回的不是ASCII(比如SCII),就提取最后一个字节。
python内置函数ord()返回ASCII码,27是Esc
的ASCII码。
判断数据类型
判断变量a是否是列表:
1 | if isinstance(a, list): |
判断变量b是否是数字:
1 | import numbers |
也可以用if isinstance(b, float):
,但是numbers.Number
可以针对整数、小数、复数。
1 | import numbers |
判断c是否是ndarray类型:
1 | if isinstance(c, np.ndarray) |
也可以用来判断是否是几种特定类型中的一个:
1 | assert isinstance(output_size, (int, tuple)) |
判断output_size
是否是int
类型或者tuple
类型。
解压操作
1 | # * 号解压 |
deque
1 | from collections import deque |
global和nonlocal关键字
同一个文件(模块)中,定义在函数外面的变量可以当做全局变量使用。
1 | def test(): |
会输出5
,即使a的定义在函数定义后面也可以。
但是如果函数中出现了同名的变量定义,则函数外的变量定义失效:
1 | a = 5 |
报错UnboundLocalError: local variable 'a' referenced before assignment
,如果函数中的a=3
放在print(a)
前面则输出3,很好理解。
再上一个例子中的函数最前面加一句global
:
1 | a = 5 |
就可以正常输出5
(之后被改成了3).
所以,当函数中对全局变量进行了修改,最好加上global
.
Python 3.x引入了nonlocal关键字,可以用于标识外部作用域的变量。
局部作用域里的代码可以读外部作用域(包括全局作用域)里的变量,但不能更改它。一旦进行更改,就会将其当成是局部变量。而如果在更改前又进行了读取操作,则会抛出异常。
nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量:
1 | def make_counter(): |
路径操作
os.path.sep
: Linux下路径分隔符
os.path.altsep
: Windows下路径分隔符
os.path.curdir
: 当前目录,Linux下是‘ . ’,可以用os.path.abspath(os.path.curdir)
os.path.pardir
: 父目录,Linux下是‘ .. ’,同样可以结合os.path.abspath(path)
os.path.abspath(path)
: 绝对路径
os.path.join()
: 常用来链接路径
os.path.split(path)
: 把path分为目录和文件两个部分,以列表返回
os.path.basename
: 返回最后一个/
后面的目录或文件名
os.listdir(path)
: 获取文件夹路径下的所有目录及文件名,结果的文件顺序并不是按文件的显示顺序
join(os.path.join)
1 | #对序列进行操作(分别使用' '与':'作为分隔符) |
以上代码出自:https://www.cnblogs.com/jonm/p/8281032.html
python文件路径
test.py文件内容:
1 | import os |
输出:
1 | C:\Test_framework\test |
而os.path.dirname(x)
是用来获取x所在的目录(x可以是文件,也可以是目录,目录即文件):
the os.path.dirname( ) function simply removes the last segment of a path.
但是由于运行py文件所处的位置不同,__file__
可能也不同,所以想获得py文件所在目录,最好加上绝对路径:
1 | os.path.dirname(os.path.abspath(__file__)) |
class
class中函数的第一个参数不一定非要是self
,改成其他,只要保持统一就行。
lambda
对字典按value排序:
1 | dict1 = {'x': 90, 'y': 98, 'z': 84, 'a': 78, 'b': 72, 'c': 96, 'd': 99} |
sorted
的参数key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。
注意输出的结果已经不再是字典。
要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True
。
Numpy
np.where
先了解numpy.nonzero,对于文档中的这个例子:
1 | 1,0,0], [0,2,0], [1,1,0]]) x = np.array([[ |
对应的两个输出表示的是两个维度,对应值组成一组索引。
再看np.where. 就能理解里面的例子了:
1 | 9.).reshape(3, 3) x = np.arange( |
对于上面最后一个例子,当输入是np.where(condition, x, y)
时,当条件为True,生成对应值是x,否则是y。
np.newaxis
1 | import numpy as np |
输出:
1 | [[1] |
np.full
1 | np.full((2, 2), 10) |
np.array[1, …]
1 | 1],[2],[3]], [[4],[5],[6]]]) x = np.array([[[ |
省略所有的冒号来用...
代替,x[:,:,0]
和x[...,0]
一样。
np.clip
numpy.clip(a, a_min, a_max, out=None)
1 | 10) a = np.arange( |
np.random
np.random.laplace
随机变量服从Laplace分布(又叫双指数函数分布/ double exponential distribution), probability density function:
$$
f(x;\mu,\lambda) = \frac{1}{2 \lambda} e^{-\frac{\vert x –\mu \vert}{\lambda}}
$$
The position, $ \mu$, of the distribution peak. Default is 0.
$\lambda$, the exponential decay. Default is 1.
The Laplace distribution is similar to the Gaussian/normal distribution, but is sharper at the peak and has fatter tails. It represents the difference between two independent, identically distributed exponential random variables.
1 | import matplotlib.pyplot as plt |
plt.hist(s, 30, density=True)
,30为显示的柱数,density=True是为了保证接下来画的两条线能够在同一张图上显示。
效果图:

直方图为随机的按Laplace分布的变量。
permutation vs shuffle
二者区别是shuffle是in-place操作
np.prod
计算乘积,可以按axis
np.squeeze
https://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.squeeze.html
1 | 0], [1], [2]]]) x = np.array([[[ |
np.fliplr
https://docs.scipy.org/doc/numpy/reference/generated/numpy.fliplr.html
1 | A = np.random.randn(2,3,5) |
注意,是在第二个维度进行翻转。
假设现在tImage
图片数据的维度格式是[batch_size, time_steps, height, width, channel],想将所有图片进行左右翻转,可以借助np.fliplr
:
1 | tImage = np.fliplr(tImage.transpose(2,3,4,0,1)).transpose(3,4,0,1,2) |
应该也可以用np.flip
,可以指定axis参数。
BGR和RGB顺序转换
1 | imageRGB = image[:,:,::-1] |
将第三个维度进行转换(逆序),上面是numpy array类型,其实对list类型也适用(list类型逆序还可以用reverse)。
为什么这样可以做到逆序?看几个例子:
1 | a = [1, 2, 3, 4, 5, 6, 7, 8] |
a[1:7:2]
就是按索引取1-6,间隔为2。那么a[::-1]
就很好理解了,第三个-1代表逆序间隔为1,第一个:
两边什么都没有代表取全部内容。
还可以用OpenCV中的函数:
1 | image_BGR = cv2.cvtColor(image_RGB, cv2.COLOR_RGB2BGR) |
np.mean
重点是axis
参数,如果是想求每个通道的均值(对于图片数据 H×W×3来说),就要写axis=(0, 1)
,这样的结果才会有3个数(均值)。
np.errstate
numpy.errstate, 参数参考numpy.seterr
作为上下文管理器,可以处理一些python不会提示的意外情况:
1 | 3) / 0. np.arange( |
np.unravel_index
unravel: 拆解
可以搭配argmax()
来使用:
1 | # A是一个二维矩阵 |
由于A.argmax()
是将A
展开,再寻找最大值的索引,返回的是一个整型数。所以想要找到最大值对应于A的位置坐标(r_max, c_max),需要用unravel_index()
进行转换.
还可针对更复杂的维度或者多个index进行转换,详见官方demo。
np.meshgrid
对于输入一维,输出二维情况,相当于是构建了一个坐标矩阵(两个返回值分别存放x和y坐标)。
collections — Container datatypes
namedtuple
1 | from collections import namedtuple |
个人理解是这个东西类似与一个类(精简版),但是只用来存放变量,没有方法。也类似于字典,相当于一个字典模板,将里面存放的东西给定义好。
网上找的两个列子:
类似于字典,有”key”,通过’’.”访问(和访问类中的成员变量一样),注意这里直接用
obj.name
访问字符串'name'
对应的值。1
2
3
4
5
6
7
8
9
10
11
12import collections
MyTupleClass = collections.namedtuple('MyTupleClass',['name', 'age', 'job'])
obj = MyTupleClass("Tomsom",12,'Cooker')
print(obj.name)
print(obj.age)
print(obj.job)
# 输出
Tomsom
12
Cooker更像类的一个例子,可以用这个模板来创建多个“对象”
1
2
3
4
5
6
7
8
9
10
11
12
13from collections import namedtuple
Friend=namedtuple("Friend",['name','age','email'])
f1=Friend('xiaowang',33,'xiaowang@163.com')
print(f1)
print(f1.age)
print(f1.email)
f2=Friend(name='xiaozhang',email='xiaozhang@sina.com',age=30)
print(f2)
name,age,email=f2
print(name,age,email)
修改值
1 | import collections |
注意:
selection1
的值未变,_replace()
方法返回新的实例x
不需要加引号
将字典转换为namedtuple
1 | from collections import namedtuple |
输出 0.01
排序函数
列表排序
list排序(存在子列表,也按子列表的0索引值排序):
1 | list1 = [[3, 4, 2], [4, 1, 7], [2, 3, 7]] |
输出:
1 | [[2, 3, 7], [3, 4, 2], [4, 1, 7]] |
发现列表的sort操作有key参数,如果要按key排序,可以:
1 | # 获取列表的第二个元素 |
输出:
1 | [(4, 1), (2, 2), (1, 3), (3, 4)] |
ndarray排序
argsort()
参考:https://stackoverflow.com/questions/5047407/python-numpy-array-sorting
想进行如下排序:
1 | array([[0, 2, 4, 7], |
而不是在某个维度里单独排序 ndarray.sort(axis=0) ,结果就会变成下面这样(每一列都排序了):
1 | [[0 2 3 1] |
可以这样操作:
1 | ndarray[ndarray[:,0].argsort()] |
lexsort()
1 | 1,5,1,4,3,4,4] # First column a = [ |
装饰器
装饰器 (decorator) 的本质——闭包 。
闭包
如果在一个内嵌函数里,对在外部函数内(但不是在全局作用域)的变量进行引用,那么内嵌函数就被认为是闭包(closure)。
1
2
3
4
5
6 > def outer():
> x = 1
> def inner():
> print x
> return inner
>
总结一下,创建一个闭包必须满足以下几点:
- 必须有一个内嵌函数
- 内嵌函数必须引用外部函数中的变量
- 外部函数的返回值必须是内嵌函数
涉及到变量的生存周期,下面的outer( )就是一个装饰器
1 | def outer(some_func): |
输出:1
2before some_func
2
增强foo( )函数:
1 | foo1 = outer(foo) |
原本的函数foo并没有发生改变.
@ 标识符
1 |
|
这样写相当于:
1 | foo = outer(foo) |
上面的装饰器只能应用于某一个(某一类方法),如何设计一个适用于更多方法的装饰器(比如一个函数运行时的计数器)?
*args
网上例子:
1 | def one(*args): |
可以看出来,*args
可以接受多个(不定长)参数,放入一个元组中。*本身是解压操作,参考前面笔记。
例子2:
1 | def add(x, y): |
这个例子中两种调用方式相同
**kwargs
关键字参数
例1:
1 | def foo(**kwargs): |
例2:
1 | dct = {'x': 1, 'y': 2} |
*args 表示多个位置参数(positional arguments ),它本质是一个 tuple
**kwargs 表示关键字参数(keyword arguments ),它本质上是一个 dict
如果同时使用 *args 和 **kwargs 时,args 参数列要在 kwargs 之前,因为positional arguments必须位于keyword arguments之前 。
所以需要 *args 和 **kwargs 配合,才能同时接受所有参数(非关键字参数和关键字参数)。
举例:
1 | def foo(*args, **kwargs): |
装饰器作用所有函数
1 | def logger(func): |
1 |
|
为什么kwargs是空字典?注意了,如果将调用改成foo1(5, y=4)
,kwargs就不是空字典了!
一个计算函数运行时间的装饰器:
1 | import time |
result是为了接func的返回值。
functools.wraps
将一个装饰器作用在某个函数上,这个函数的重要的元信息比如名字、文 档字符串、注解和参数签名都会丢失。对上面计算函数运行时间的装饰器来说,如果有:
1 |
|
输出的不是prime_number里的注释信息,而是装饰器中的函数信息:
1 | Help on function wrapper in module __main__: |
解决方法:使用 functools 库中的 @wraps 装饰器来注解底层包装函数。如下
1 | import time |
这样,再使用help(prime_number)
的结果就看到了原函数的注释信息:
1 | Help on function prime_number in module __main__: |
解除一个装饰器
直接访问未包装的原始函数在调试、内省和其他函数操作时是很有用的。如果装饰器是通过 @wraps 来实现的,那么你可以通过访问 __wrapped__
属性来访问原始函数:
1 |
|
参考资料:
相见恨晚的技巧
raise
from here.
1 | import sys |
输出’fail!!!!’,结束程序。也可以直接写成:
1 | import sys |
同样效果,默认返回1。
1 | raise Exception("This user is not allowed to get food") |
在父类中,如果存在子类一定要重写的方法,可以这样:
1 | class A(object): |
这样,就可以保证调用B.func2()
不会出错(如果B
中没有重写func1()
,就会报错)。
assert
1 | assert condition |
等同于
1 | if not condition: |
添加异常解释:
1 | assert expression [, arguments] |
比如:
1 | assert len(unique_ids) > 1, 'unique_ids must be at least 2 elements' |
enumerate
enumerate(list_a, 1)
可以指定index从1开始
文件的x模式
'w'
覆盖写模式,文件不存在则创建,存在则完全覆盖;
'x'
创建写模式,文件不存在则创建,存在则返回异常FileExistError
;
list.pop()
列表的pop方法会返回弹出的值:
1 | a = list_1.pop() |
defaultdict
详细见collections
更方便的构造dict
demo1 - value type is list
不必为每一个key重复创建空列表(默认每一个新添加的key对应的value为空list)
1 | from collections import defaultdict |
输出:
1 | defaultdict(<class 'list'>, {'number': [1, 2]}) |
demo2 - value type is int
默认每一个新添加的key对应的value为整数 0 )
1 | from collections import defaultdict |
输出:
1 | defaultdict(<class 'int'>, {'count1': 1, 'count2': -1}) |
默认空列表和整数0的原因是 defaultdict( ) 的参数是一个 mapping , list()
和int()
返回的就是空列表和整数0。对于demo2,如果想指定别的数作为默认值,可以:
1 | from collections import defaultdict |
输出为:
1 | defaultdict(<function set_value at 0x7fea5bb63f28>, {'count2': 0, 'count1': 2}) |
#往实例中添加方法
当创建了一个实例,想要往实例中添加额外的方法时,可以这样(法一):
1 | def my_print(a): |
也可以这样(法二):
1 | def set_age(self, age): # 定义一个函数作为实例方法 |
法一简单,但是添加的函数无法调用self
,法二可以。
只判断图片大小
出自Re3:
1 | def get_image_size(fname): |
能不调用opencv,就不调用opencv,只读取前32字节,节省资源。
打印函数名
函数内部打印函数名
1 | def func(): |
输出:
1 | func |
Python build-in functions
排列组合
1 | import itertools |
容易产生bug的地方
用列表作默认参数
1 | def func(a, b=[]): |
输出:
1 | a: 1 |
第一次调用的结果仍然保留了。
for … else
1 | for i in range (10): |
会执行else中的语句,当把continue改成break时,不会执行else中的语句。
即当没有break或者return打破for循环,就会执行else里的内容,注意两点:
在最后一次循环中使用了break,也不会执行else中语句。
可迭代内容为空时,仍会执行else语句,即使里面有break
1
2
3
4for i in []:
break
else:
print('items are exhausted')会执行print语句。
对while循环同理。
循环+else可以节省一个flag。
格式化字符串
% 格式化字符串
C语言风格
1 | print('%.1f' % 5.6) |
输出:
1 | 5.6 |
面对更复杂的情况,这是一种冗余的方式。对于python中的结构,字典和列表的处理也不够好。
str.format
更强大的格式化函数,参数和demo参考:Format Specification Mini-Language
还是冗余,但是字典可以进行解压缩:.format(**dict)
f-string
python 3.6 + 环境中,PEP 498 – Literal String Interpolation
1 | a = 'hello' |
输出:hello, 3
1 | def upper(s): |
输出:HELLO, 3
注意,引号不要重复了:
1 | comedian = {'name': 'Eric Idle', 'age': 74} |
内容中有单引号,外面要用双引号。
对于lambda表达式:
1 | f'{lambda x: x*2 (3)}' |
会产生错误,因为有:
号,应该写成f'{(lambda x: x*2)(3)}'
,结果为'6'
sacred
Facilitates automated and reproducible experimental research
Python中的多任务
有关进程(process)和线程(thread)的区别可以参考:
多进程
参考:
※ What’s the difference between ThreadPool vs Pool in Python multiprocessing module
多线程
参考:
目前(2019.03.05)仍缺少multiprocessing.pool.ThreadPool
的文档,只能看源码。
1 | from multiprocessing.pool import ThreadPool |
异步IO
参考:
类
super
在子类中调用父类的某个已经被覆盖的方法,可以:
1 | class Person: |
可以在__init__
方法中使用super
:
1 | class Animal: |
使用super
的方法比下面这种更好:
1 | class Cat(Animal): |
具体原因见python3 cookbook.
对于多继承中的super
,可以参考:Python: super 没那么简单,注意MRO.
类的特殊方法
这一节基本是 廖雪峰的python教程 - 面向对象编程(高级) 中的内容
如果要获得一个对象的所有属性和方法,可以使用dir()
函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
1 | 'ABC') dir( |
__len__()
在Python中,如果你调用len()
函数试图获取一个对象的长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法,所以,下面的代码是等价的:
1 | 'ABC') len( |
我们自己写的类,如果也想用len(myObj)
的话,就自己写一个__len__()
方法:
1 | class MyDog(object): |
类似的还有__str__()
等
__slots__()
可以在类外给实例绑定成员变量和成员方法:
1 | # ---绑定变量--- |
给一个实例绑定的方法,对另一个实例不起作用。为了给所有实例都绑定方法,可以给class绑定方法:
1 | def set_score(self, score): |
给class绑定方法后,所有实例均可调用:
1 | 100) s.set_score( |
通常情况下,上面的set_score
方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。
__slots__
用来限制实例的属性。如只允许对Student实例添加name
和age
属性,可以在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:
1 | class Student(object): |
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
。
其他
__str__, __iter__/__next__, __getitem__, __getattr__, __call__
,参考定制类
@property
类方法和静态方法
类中的方法可以分为:
- 实例方法:最常用的一种,因为其中往往要用到实例属性(变量)和实例方法,参数一定要带
self
。 - 类方法:方法用
@classmethod
装饰,参数不需要带self
。 - 静态方法:方法用
@staticmethod
装饰,参数不需要带self
。
在类中的函数,如果没有self
这样的参数,必须用@classmethod
或者@staticmethod
来修饰。
类方法
1 | class A(object): |
注意,类方法class_foo
的参数不是self
,用来代表类,调用时也不需要实际传入,可通过类名直接调用。
静态方法
1 | class A(object): |
静态方法可以被类调用,也可以被实例调用。
静态方法没有 self 和 cls 参数,相当于一个普通的函数。
PyCharm
功能
对选中的代码按Ctrl+Alt+T
可以进行分块操作。
设置
当在a.py中导入同目录下的b.py文件时:
1 | from . import b |
需要在目录下创建一个__init.py文件
如果直接写成:
1 | import b |
也是可以,但是在PyCharm编辑界面中会出现错误提示,实际上是可以运行的,涉及一个环境变量的问题。
- 如果在命令行直接运行a.py,系统会默认当前目录已经在环境变量中
- 在PyCharm中,需要将当前目录添加到环境变量中(右键make_directory as–>sources path将当前工作的文件夹加入source_path),编辑时才不会提示错误(运行时不会提示错误)。
搭配git
project栏目中文件名的含义:
绿色,已经加入版本控制暂未提交;
红色,未加入版本控制;
蓝色,加入版本控制,已提交,有改动;
白色,加入版本控制,已提交,无改动;
灰色:版本控制已忽略文件。
路径问题
当在终端里可以正确运行,在 pycharm 中提示找不到某个脚本文件,一般是 working directory 不对。
Run -> Edit Configuration -> working directory 设置和终端相同的目录。
一些错误
无返回值
print(list.reverse())
输出None,因为list.reverse()不返回任何值,应该写成如下形式
1 | list.reverse() |
图像显示错误
问题一:
显示全白图像,但是数据不都是255。
错误原因:数据类型错误,cv2.imshow()接收的ndarray类型是np.uint8!
解决方法:
1 | # float32 -> uint8: |
问题二:
RGB顺序颠倒。
解决方法:上面有(image[:,:,::-1]
).
导入包错误
1 | from ../utils import Calc |
显示错误,其实不需要/
,应该写成:
1 | from ..utils import Calc |