计算物理(五)Python 基础
fengxiaot Lv4

在 UC Berkeley 交换期间,发现这里的人做科学计算都用 Python ,因此从第五篇文章开始用 Python 重写计算物理的笔记。Python 的模块众多,作为更具有通用性的编程语言,语法糖也比 MATLAB 繁杂,因而创建一个速查手册是必要的。

Python 基础语法

Python 数据类型

Number 数字

在 Python 中,变量声明不需要声明类型,我们所说的类型是变量所指的内存中对象的类型。

  1. bool 类型:TureFalse ,首字母大写,逻辑运算符为 and , ornot /* 注:bool 类型是 int 的子类 */
  2. int, float 类型:科学计数法不区分 e 大小写 1.602e-19, 6.626E-34 均可;
  3. complex 类型:直接使用 a+bj 创建复数,也可以使用 complex(real, imaginary) 函数。

数字的运算包括加法 +,减法 - ,乘法 *,浮点除 /,整除 //,取模 %,幂方 ** 。

List 列表

列表使用方括号 [...] 创建,逗号分隔元素。也可以使用 list() 将元组转换为列表。

1
colorlist = ['red', 'green', 'blue', 'yellow', 'white', 'black']

索引 列表索引从 0 开始,末位为 -1,取左不取右。

1
2
3
4
5
6
print(colorlist[1]) # green
print(colorlist[0:2]) # ['red', 'green']
print(colorlist[0:-1]) # ['red', 'green', 'blue', 'yellow', 'white']
print(colorlist[:-1]) # ['red', 'green', 'blue', 'yellow', 'white']
print(colorlist[2:]) # ['blue', 'yellow', 'white', 'black']
print(colorlist[-2:0]) # []

复制 与变量不同,使用 = 复制列表仅仅复制了其内存地址,即引用了原列表。改变原列表后,被复制的列表也一同改变。使用 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
2
if msg is None:
# statements

Python 函数

函数

函数代码块以 def 关键词开头,不同于 C++ 或 MATLAB,Python 代码中函数定义必须放在调用其之前。

1
2
3
def functionname(param, default = value, *args, **kwargs):
function_body
return expression

/* 注:argument为实参,parameter为形参,不过自C99以后不再区分 */

必选参数 必选参数 (required arguments) 是调用函数时必须传入的参数。

默认参数 默认参数 (default arguments) 在调用函数时可选择传入与否,若不传入则取默认值。

1
2
3
4
def f(a, b = 1):
return a + b
a = 1
print(f(a),f(a,2),f(a,b = 2)) # Output: 2 3 3

可选参数 可选参数 (arbitary arguments) 接受0个或任意数量的参数,这些参数在函数内部被封装成一个 tuple 来使用。

1
2
3
4
def tell(str,*args):
print(str,args[2]) # Output: I love Yaki
print(args) # Output: (0,1,'Yaki',3,4)
tell('I Love',0,1,'Yaki',3,4)

关键字参数 关键字参数 (keyword arguments) 接受 0 个或任意数量含参数名的参数,这些参数在函数内部被封装成一个 dict 来使用。

1
2
3
4
def tell(str,**kwargs):
print(str,kwargs['name']) # Output: I love Yaki
print(kwargs) # Output: {'name': 'Yaki', 'age': 21, 'date': '03/14/2023'}
tell('I Love', name='Yaki', age=21, date='03/14/2023')

匿名函数

python 使用 lambda 来创建匿名函数,语法为

1
lambda args: expression

例如创建一个二元函数

1
f = lambda x,y: x + y

Python 列表推导

1
[expr for element in list if condition]

列表推导式亦可嵌套,效果同从左向右嵌套的 for 循环

1
2
3
L = [(i,j) for i in range(3) if i > 0 
for j in range(3) if j > 0]
# L = [(1, 1), (1, 2), (2, 1), (2, 2)]

Python 类

类 (class) 是对象 (object) 的抽象化,对象是类的实例化。对象的特征称为属性 (attribute),它所具有的行为称为方法 (method)。

1
2
3
4
5
6
7
class ClassName:
class_attr = <attribute of class>
def __init__(self,args):
self.attr = args
# define an attribute <obj.attr> of object
def method(self):
<method of object>

例如定义复数类 mycomplex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class mycomplex:
type = 'MyComplex'

def __init__(self,realpart,imagpart):
self.r = realpart
self.i = imagpart

def norm(self):
return np.sqrt(self.r**2 + self.i**2)

def conjugate(self):
return mycomplex(self.r,-self.i)

def __str__(self):
if self.i > 0:
return str(self.r) + '+' + str(self.i) + 'i'
else:
return str(self.r) + str(self.i) + 'i'

以上代码实现的功能有

  • 类 mycomplex,类属性 type ,任意 mycomlex 对象的 type 属性均为字符串 'MyComplex'
  • 构造方法 __init__() ,类实例化时自动调用,即 self = mycomplex(realpart,imagpart)
  • 将实例化时获得的参数 realpart 和 imagpart 赋给对象的 r 属性和 i 属性,不同的 mycomplex 对象此属性的值不同
  • 定义类方法 norm 和 conjugate,即定义对复数对象的两种操作,分别给出模和共轭
  • 字符串化方法 __str__() ,给出 str(object) 的值
1
2
3
4
5
a = mycomplex(3,4)
b = mycomplex(1,-1)
print(a.norm(),str(a.conjugate()))
print(a.type,b.type)
print(str(b))
1
2
3
5.0 3-4i
MyComplex MyComplex
1-1i

Python 格式化 I/O

string.format()

Python 格式化输入输出使用字符串类的 format() 方法,语法为

1
string.format(*args,**kwargs)

可以向 format() 传入不定长参数或关键字参数,不定长参数被依次传递到花括号构成的占位符中,如 txt1 所示。

如前所述,不定长参数在内部被封装成一个 tuple 来使用,因此我们也可以采用数字序号,如 txt2 所示。

若传入关键字参数,则占位符花括号内需要包含 key,输出时将被相应的 value 替换,如 txt3 所示。

1
2
3
4
txt1 = "My name is {}, I'm {}".format("John",36)
txt2 = "My name is {0}, I'm {1}".format("John",36)
# txt2 = "My name is {1}, I'm {0}".format(36,"John")
txt3 = "My name is {name}, I'm {age}".format(name = "John", age = 36)

转义规则

默认情况下,转义字符由 \ 引导。例如换行符为 \n

当使用 string.format() 方法时,额外的转义规则 {{}} -> {} 被激活,双花括号将转义为单花括号。

1
2
3
4
5
print('I love {0}') # Output: I love {0}
print('I love {{0}}') # Output: I love {{0}}
print('I love {0}'.format('Yaki')) # Output: I love Yaki
print('I love {{0}}'.format('Yaki')) # Output: I love {0}
print('I love {{{0}}}'.format('Yaki')) # Output: I love {Yaki}

从第四行程序的运行结果可以看出,程序首先将字符串中所有的双花括号转义为单花括号,然后不再继续调用 format() 方法,即使转义后仍然余下一个 {0}

前缀 r

前缀 r 阻止转义字符 \ 的转义,但不阻止使用 string.format() 方法时 {{}} -> {} 的转义。

1
2
print(r'I love {{0}}'.format('Yaki')) # Output: I love {0}
print(r'I love {{{0}}}'.format('Yaki')) # Output: I love {Yaki}

LaTeX

根据以上特性,可以总结出在 Python 中使用 LaTeX 语法的技巧。

当不需要将变量传入字符串,即不需要用到 string.format() 方法时,只需在字符串前加 r 以避免 TeX 指令 (通常以 \ 引导) 的转义

1
print(r'$\frac{2}{3}$') # Output:$\frac{2}{3}$

当需要将变量传入字符串时,需在字符串前加 r 以避免 TeX 指令的转义,同时将 LaTeX 指令的每个花括号变成双花括号。因此,一个 \frac 中的变量往往套着三层括号。

1
2
print(r'$\omega_{0} = \frac{{E_{0}}}{{\hbar}}$'.format('i'))
# Output: $\omega_i = \frac{E_i}{\hbar}$

同时,为了避免元组索引 {0} 和 LaTeX 本身的数字上下标 ^{0} 混用,使用关键字参数传入变量更好。因此,以下代码比上面更优

1
print(r'$\omega_{sub} = \frac{{E_{sub}}}{{\hbar}}$'.format(sub = 'i'))

最后,放一个噩梦般的代码,你能理解它吗?

1
2
for i in range(3):
print(r'$\omega^{{0}}_{{{iter}}} = \frac{{E^{{0}}_{{{iter}}}}}{{\hbar}}$'.format(iter = str(i)))

format_spec

格式化参数 (format specification) 是用来指定值的输出格式的参数,就像 C 中的 %.2f

Python 指定值的输出格式的方法是在花括号内、变量 (可省略) 后使用冒号

1
2
"{[format_variable]:[format_spec]}".format()
format_spec := [fill][align][width]["."precision][type]
参数 解释
fill 任意字符,默认为空格 指定[width]时的占位符
align 左对齐 <,右对齐 >,居中为 = 指定[width]时的对齐方式
width 整数 字符的打印宽度
precision 整数 浮点型精度,小数点后位数
type 变量类型 整型'd',浮点型'f',科学计数'e'

Numpy

数组

数组类

Numpy 提供数组类 ndarray ,其定义为

1
class numpy.ndarray

ndarray 对象包括以下重要属性

  • ndarray.ndim - 数组的维数,例如矩阵的 ndim 为 2 ;
  • ndarray.shape - 数组的维度/形状,以整数型元组给出。nnmm 列矩阵的 shape 为 (n,m)
  • ndarray.size - 数组的元素个数;
  • ndarray.dtype - 数组中元素的数据类型;

数组创建

  • numpy.array(array_like_obj, dtype = None) 将列表、元组或其他序列对象,转化并创建为 ndarray 对象;

  • numpy.zeros(shape, dtype = None) 接收数组维度 (shape tuple),并创建全 0 数组,例如 2×22\times 2 零矩阵 np.zeros((2,2))

  • numpy.eye(N, M = None, k = 0) 创建 NNMM 列 ( MM 默认等于 NN ) 单位对角矩阵,对角元上移 kk 个元素;

  • 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
    3
    np.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]

基本算术

数组上的算术运算符会应用到元素级别,包括加法 +,减法 - ,乘法 *,浮点除 /,整除 //,取模 %,幂方 ** 。

NumPy 提供在数组上按元素进行运算的通函数 ufunc ,例如 np.sin(), np.exp(), np.exp() 等等。

NumPy 提供矩阵乘法运算符 @ 。处理向量与矩阵的乘法时,@ 将根据乘法顺序,自动将一维数组识别为行向量或列向量。

形状操纵

堆叠 使用 np.hstack np.vstacknp.column_stack 来进行堆叠。

/* 为什么没有 row_stack? 略加思考将会发现 row_stack 和 vstack 功能一致! */

1
2
3
4
5
a = np.array([1,2])
b = np.array([3,4])
print(np.hstack((a,b)))
print(np.vstack((a,b)))
print(np.column_stack((a,b)))
1
2
3
4
5
6
7
[1 2 3 4]

[[1 2]
[3 4]]

[[1 3]
[2 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
2
plt.plot(x1, y1, **kwargs)
plt.plot(x2, y2, **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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
import matplotlib.pyplot as plt

def f(t):
return np.exp(-t) * np.cos(2*np.pi*t)

t1 = np.hstack((np.arange(0.0, 1.0, 0.05),np.arange(1.0, 5.0, 0.2)))
t2 = np.arange(0.0, 4*np.pi, 0.02)

plt.subplot(2,2,1) # numrows, numcols, fignum
plt.scatter(t1, f(t1), marker="*")

plt.subplot(2,2,2) # numrows, numcols, fignum
plt.plot(t1, f(t1), 'k--')

plt.subplot(2,2,3) # numrows, numcols, fignum
plt.plot(t2, np.cos(t2),color="steelblue",label="Cos")
plt.plot(t2, np.sin(t2),color="lightskyblue",label="Sin")
plt.legend()

plt.subplot(2,2,4) # numrows, numcols, fignum
plt.plot(t2, np.log(t2+1), color="darkturquoise", linestyle='-.')

plt.show()

面向对象风格

绘图元素

待更

参数设置

待更