在 UC Berkeley 交换期间,发现这里的人做科学计算都用 Python ,因此从第五篇文章开始用 Python 重写计算物理的笔记。Python 的模块众多,作为更具有通用性的编程语言,语法糖也比 MATLAB 繁杂,因而创建一个速查手册是必要的。
Python 基础语法
Python 数据类型
Number 数字
在 Python 中,变量声明不需要声明类型,我们所说的类型是变量所指的内存中对象的类型。
- bool 类型:
Ture
或False
,首字母大写,逻辑运算符为and
,or
和not
; /* 注:bool 类型是 int 的子类 */ - int, float 类型:科学计数法不区分 e 大小写 1.602e-19, 6.626E-34 均可;
- complex 类型:直接使用
a+bj
创建复数,也可以使用complex(real, imaginary)
函数。
数字的运算包括加法 +,减法 - ,乘法 *,浮点除 /,整除 //,取模 %,幂方 ** 。
List 列表
列表使用方括号 [...]
创建,逗号分隔元素。也可以使用 list()
将元组转换为列表。
1 | colorlist = ['red', 'green', 'blue', 'yellow', 'white', 'black'] |
索引 列表索引从 0 开始,末位为 -1,取左不取右。
1 | print(colorlist[1]) # green |
复制 与变量不同,使用 = 复制列表仅仅复制了其内存地址,即引用了原列表。改变原列表后,被复制的列表也一同改变。使用 list.copy
进行浅拷贝,仅对嵌套列表的第一层复制值本身;使用 copy
模块中的 copy.deepcopy()
进行深拷贝,对嵌套列表中的所有层进行列表值的复制。
/* 注:Python 中的赋值语句应该始终先读右边。对象在右边创建或获取,在此之后左边的变量才会绑定到对象上 */
连接 加号 + 是列表的连接符, 星号 * 表示复制当前列表,并附加在后面。
函数 通过 len()
获取列表元素个数,通过 list.insert(index, obj)
将 obj 插入到第 index 个位置,通过 list.sort(reverse = False)
排序,默认为升序,通过 list.append(element)
将元素添加到列表的末尾。
Tuple 元组
元组使用圆括号 (...)
创建,逗号分隔元素。也可以使用 tuple()
将列表转换为元组。
元组中的元素值是不允许修改的,但我们可以对元组进行连接组合,语法与列表类似。
注意:若要定义单元素元组,元素后面需要加逗号。 /* 例:(1,) 正确(1) 错误 */
String 字符串
Python中的字符串用单引号 ’ 或双引号 " 括起来,同时使用反斜杠 \ 转义特殊字符。
字符串和数字可以通过 int()
, float()
和 str()
或 repr()
互相转换。
Dict 字典
元组使用花括号 {...}
创建,逗号分隔键值对,冒号分隔每对键值。
1 | dictionary = {key1 : value1, key2 : value2, key3 : value3} |
键必须是唯一的,但值则不必。值可以取任何数据类型,但键必须是不可变的,如字符串,数字。
NoneType 空值
None 是 NoneType 数据类型的唯一值。它不表示 0,False,也不表示空字符串或空对象,None 就是空值本身。
None 常用于 assert、判断以及函数无返回值的情况,可以使用 is
提升代码速度
1 | if msg is None: |
Python 函数
函数
函数代码块以 def
关键词开头,不同于 C++ 或 MATLAB,Python 代码中函数定义必须放在调用其之前。
1 | def functionname(param, default = value, *args, **kwargs): |
/* 注:argument为实参,parameter为形参,不过自C99以后不再区分 */
必选参数 必选参数 (required arguments) 是调用函数时必须传入的参数。
默认参数 默认参数 (default arguments) 在调用函数时可选择传入与否,若不传入则取默认值。
1 | def f(a, b = 1): |
可选参数 可选参数 (arbitary arguments) 接受0个或任意数量的参数,这些参数在函数内部被封装成一个 tuple 来使用。
1 | def tell(str,*args): |
关键字参数 关键字参数 (keyword arguments) 接受 0 个或任意数量含参数名的参数,这些参数在函数内部被封装成一个 dict 来使用。
1 | def tell(str,**kwargs): |
匿名函数
python 使用 lambda 来创建匿名函数,语法为
1 | lambda args: expression |
例如创建一个二元函数
1 | f = lambda x,y: x + y |
Python 列表推导
1 | [expr for element in list if condition] |
列表推导式亦可嵌套,效果同从左向右嵌套的 for
循环
1 | L = [(i,j) for i in range(3) if i > 0 |
Python 类
类 (class) 是对象 (object) 的抽象化,对象是类的实例化。对象的特征称为属性 (attribute),它所具有的行为称为方法 (method)。
1 | class ClassName: |
例如定义复数类 mycomplex
1 | class mycomplex: |
以上代码实现的功能有
- 类 mycomplex,类属性 type ,任意 mycomlex 对象的 type 属性均为字符串
'MyComplex'
- 构造方法
__init__()
,类实例化时自动调用,即self = mycomplex(realpart,imagpart)
- 将实例化时获得的参数 realpart 和 imagpart 赋给对象的 r 属性和 i 属性,不同的 mycomplex 对象此属性的值不同
- 定义类方法 norm 和 conjugate,即定义对复数对象的两种操作,分别给出模和共轭
- 字符串化方法
__str__()
,给出 str(object) 的值
1 | a = mycomplex(3,4) |
1 | 5.0 3-4i |
Python 格式化 I/O
string.format()
Python 格式化输入输出使用字符串类的 format()
方法,语法为
1 | string.format(*args,**kwargs) |
可以向 format()
传入不定长参数或关键字参数,不定长参数被依次传递到花括号构成的占位符中,如 txt1 所示。
如前所述,不定长参数在内部被封装成一个 tuple 来使用,因此我们也可以采用数字序号,如 txt2 所示。
若传入关键字参数,则占位符花括号内需要包含 key,输出时将被相应的 value 替换,如 txt3 所示。
1 | txt1 = "My name is {}, I'm {}".format("John",36) |
转义规则
默认情况下,转义字符由 \ 引导。例如换行符为 \n
。
当使用 string.format() 方法时,额外的转义规则 {{}} -> {}
被激活,双花括号将转义为单花括号。
1 | print('I love {0}') # Output: I love {0} |
从第四行程序的运行结果可以看出,程序首先将字符串中所有的双花括号转义为单花括号,然后不再继续调用 format() 方法,即使转义后仍然余下一个 {0}
。
前缀 r
前缀 r 阻止转义字符 \ 的转义,但不阻止使用 string.format() 方法时 {{}} -> {}
的转义。
1 | print(r'I love {{0}}'.format('Yaki')) # Output: I love {0} |
LaTeX
根据以上特性,可以总结出在 Python 中使用 LaTeX 语法的技巧。
当不需要将变量传入字符串,即不需要用到 string.format() 方法时,只需在字符串前加 r 以避免 TeX 指令 (通常以 \ 引导) 的转义
1 | print(r'$\frac{2}{3}$') # Output:$\frac{2}{3}$ |
当需要将变量传入字符串时,需在字符串前加 r 以避免 TeX 指令的转义,同时将 LaTeX 指令的每个花括号变成双花括号。因此,一个 \frac
中的变量往往套着三层括号。
1 | print(r'$\omega_{0} = \frac{{E_{0}}}{{\hbar}}$'.format('i')) |
同时,为了避免元组索引 {0} 和 LaTeX 本身的数字上下标 ^{0}
混用,使用关键字参数传入变量更好。因此,以下代码比上面更优
1 | print(r'$\omega_{sub} = \frac{{E_{sub}}}{{\hbar}}$'.format(sub = 'i')) |
最后,放一个噩梦般的代码,你能理解它吗?
1 | for i in range(3): |
format_spec
格式化参数 (format specification) 是用来指定值的输出格式的参数,就像 C 中的 %.2f
。
Python 指定值的输出格式的方法是在花括号内、变量 (可省略) 后使用冒号
1 | "{[format_variable]:[format_spec]}".format() |
参数 | 值 | 解释 |
---|---|---|
fill | 任意字符,默认为空格 | 指定[width]时的占位符 |
align | 左对齐 <,右对齐 >,居中为 = | 指定[width]时的对齐方式 |
width | 整数 | 字符的打印宽度 |
precision | 整数 | 浮点型精度,小数点后位数 |
type | 变量类型 | 整型'd' ,浮点型'f' ,科学计数'e' |
Numpy
数组
数组类
Numpy 提供数组类 ndarray
,其定义为
1 | class numpy.ndarray |
ndarray
对象包括以下重要属性
ndarray.ndim
- 数组的维数,例如矩阵的 ndim 为 2 ;ndarray.shape
- 数组的维度/形状,以整数型元组给出。 行 列矩阵的 shape 为(n,m)
;ndarray.size
- 数组的元素个数;ndarray.dtype
- 数组中元素的数据类型;
数组创建
-
numpy.array(array_like_obj, dtype = None)
将列表、元组或其他序列对象,转化并创建为ndarray
对象; -
numpy.zeros(shape, dtype = None)
接收数组维度 (shape tuple),并创建全 0 数组,例如 零矩阵np.zeros((2,2))
; -
numpy.eye(N, M = None, k = 0)
创建 行 列 ( 默认等于 ) 单位对角矩阵,对角元上移 个元素; -
numpy.full(shape, fill_value, dtype = None)
接收数组维度创建数组,并将 fill_value 赋给数组的每个元素; -
numpy.zeros_like(array_like_obj)
创建一个形状和数据类型与 array_like_obj 均相同的全零数组 -
numpy.arange(start, stop, step, dtype)
生成区间 [start, stop) ,步长为 step 的数组 -
numpy.linspace(start, stop, num = 50, endpoint = True)
生成 [start, stop],样本数量为 num,按照 endpoint 为 True/False 包含/不包含 stop point 的数组,参见以下示例1
2
3np.linspace(0,1,num = 2, endpoint=True) # Output: [0,1]
np.linspace(0,1,num = 3, endpoint=True) # Output: [0,0.5,1]
np.linspace(0,1,num = 2, endpoint=False) # Output: [0,0.5]
注:dtype = None
是许多 Numpy 数组创建函数时的默认参数,但它并不意味着将 Numpy 数组中的每个元素都指定为 Nonetype 数据类型。当使用dtype = None
时,Numpy 将根据传入数据自动确定 ndarray 的 dtype 。这使得程序运行速度稍慢。
当然,如果你传入了一个 None 列表 np.array([None,None,None])
,那么 dtype 就真的是 NoneType 了吗?也不是,因为 Numpy 不支持将 NoneType,或者其他用户自定义类作为数据类型传入 ndarray 。在尝试将数据解释为 int, float, complex, bool 等 Numpy 内置类失败后,整个 array_like_obj 将被视作一个整体,创建只有一个元素的一维数组,dtype 为 Python object 。
基本算术
数组上的算术运算符会应用到元素级别,包括加法 +,减法 - ,乘法 *,浮点除 /,整除 //,取模 %,幂方 ** 。
NumPy 提供在数组上按元素进行运算的通函数 ufunc
,例如 np.sin(), np.exp(), np.exp()
等等。
NumPy 提供矩阵乘法运算符 @
。处理向量与矩阵的乘法时,@ 将根据乘法顺序,自动将一维数组识别为行向量或列向量。
形状操纵
堆叠 使用 np.hstack
np.vstack
或 np.column_stack
来进行堆叠。
/* 为什么没有 row_stack? 略加思考将会发现 row_stack 和 vstack 功能一致! */
1 | a = np.array([1,2]) |
1 | [1 2 3 4] |
切片 切片仅仅提供原数组的一个视图 (view),即切片数据仍然链接到原数组数据。若要实现深拷贝,需要切片时同时应用 ndarray.copy()
方法。
Matplotlib
MATLAB风格
绘图元素
单曲线 曲线使用 matplotlib.pyplot.plot(*args, **kwargs)
绘制,语法同 MATLAB 的 plot()
函数。
若要控制曲线的颜色和线型,最基本的方法是
1 | plt.plot(x, y, [fmt]) |
其中 [fmt]
为控制曲线样式的字符串,格式为 [fmt] = '[marker][line][color]'
。
也可以使用关键字参数
1 | plt.plot(x, y, color='green', marker='o', linestyle='dashed', linewidth=2, markersize=12) |
当 [fmt] 和关键字参数同时存在时,关键字参数将会覆盖 [fmt] 指定的样式,例如以下代码绘制的曲线最终是绿色的。
1 | plt.plot(x, y, 'r', color='green') |
多曲线 多曲线最简单的绘制方法是多次调用 plt.plot()
方法
1 | plt.plot(x1, y1, **kwargs) |
当然,也可以写在同一个 plt.plot()
中
1 | plt.plot(x1, y1, [fmt1], x2, y2, [fmt2], ..., **kwargs) |
但要注意,此时只允许将关键字参数放在末尾传入,且 **kwargs
指定的绘图样式将应用于所有曲线,而不仅仅是最后一条。当 [fmt] 和关键字参数发生冲突时,关键字参数仍将覆盖 [fmt] 所指定的样式。
轴标签 使用 plt.xlabel(str)
和 plot.ylabel(str)
添加轴标签,其中 str 为字符串。
Matplotlib 支持 LaTeX ,只需按照 Python 格式化 I/O 中定义的规则传入字符串即可。
标题 使用 plt.title(str)
创建绘图标题,其中 str 为字符串。
图例 使用 plt.legend()
为图像添加图例。
- 当不传入参数时,
plt.legend()
自动获取曲线的 label 并作为图例 - 当传入字符串列表时,
plt.legend([list of string])
按顺序为每条曲线添加图例
子图
plt.subplot(numrows, numcols, fignum)
numrows 和 numcols 是总行数和总列数,对每个子图值都相同;fignum 指示当前绘图的序号。
1 | import numpy as np |
面向对象风格
绘图元素
待更
参数设置
待更