访问博客查看 本文 最新内容,排版更美观ヾ(•ω•`)o 如有错误欢迎指出~
Python 系列学习笔记:
- Python笔记 #1 基础语法
- Python笔记 #2 NumPy
- Python笔记 #3 Matplotlib
跟着贵系的暑培过了一遍语法,然而写代码时还是感到乏力,总觉得要打一遍才能记住,于是有了这篇博客。
本文部分内容参考了清华 AYF 同学的教程,部分参考了 Python 官方的一份 Tutorial,函数用法参考了 xyfJASON 的博客。本文将持续更新。
Python 特性
相比于 C 的编译型、弱类型、静态类型特点,Python 则是一种解释型、强类型、动态类型的语言。
交互式 vs 脚本
作为一种解释型语言,Python 不需要像 C 一样编译运行,它可以逐行运行代码,因此又分为交互式窗口运行与脚本运行两种模式:
- 交互式窗口:在 CLI 输入
python
,即可进入交互式窗口,输入一行代码即可运行。本文使用的交互式环境是 IPython,输入ipython
即可呼出。 - 脚本:在 CLI 输入
python xxx.py
,就会依次执行整个脚本文件。本文使用的 IDE 是 PyCharm。
Hello World
>>> print('Hello, world!') # 打印输出
Hello, world!
在交互式窗口,>>>
作为提示符,在 IPython 中则是 In [1]:
。执行 exit()
或者按下 Ctrl+D
就能退出窗口。
注意到,这行代码中没有 ;
分号,可以直接运行而无需编译,字符串用了单引号,注释用 #
开始【与 C 不同】。
简单数据类型
变量类型
【与 C 不同】,Python 不用声明变量类型,解释器自动解释。
-
int
:变长整数,默认是 4 字节,有需要时自动增长,用于高精度运算,还支持十六进制、八进制和二进制表示。 -
complex
:自带的复数类型,表示为real + imag*1j
的形式,虚部为 1 的时候不可省略 1,可以用j
也可以用J
。实虚部分别为一个float
。 -
float
:8 字节浮点数,【相当于 C 的 double】。 -
bool
:True 和 False,注意首字母大写,用作数值计算时与 C 一样视作 0 和 1。 -
NoneType
: None,空值,常用于返回值、特判。
需要单独说明的是,Python 会存储所有的 -5 到 256 的整数,其他任何变量是这些值时,会被指向这个预先开好的内存,因此任何两个值为 5 的 int 变量都指向同⼀内存地址。
尽量用小写变量名(下划线法),这是 Python3 的主流命名方式。
运算符
这里列出常见的运算符,运算符可以重载,重载需要修改其对应的定义函数。
算术运算符:
-
+
-
*
【与 C 相同】 -
%
:35 % 4 == 3
,-35 % 4 == 1
,35 % -4 == -1
,-35 % -4 == -3
,【与 C 不同:负数对正数取模时返回正余数,而非像 C 那样返回负余数】 -
/
:__trudiv__
,真除,得到float
结果 -
//
:__floordiv__
,除后向下取整(不是舍弃小数),得到int
结果 -
**
:__pow__
,幂运算
比较运算符:
-
<
<=
>
>=
==
!=
【与 C 相同】
位运算符:
-
&
|
^
~
<<
>>
【与 C 相同】
赋值运算符:
-
=
:赋值号,不能被重载 -
+=
-=
*=
/=
%=
【与 C 相同】 - 注意 Python 中没有
++
--
的运算符,只能通过+= 1
实现
逻辑运算符:
-
and
or
not
【类似 C 中的&&
||
!
】 - 对于
and
和or
,通常用于条件分支bool
的判断,如果非要连接int
变量,得到的结果不会直接转换为bool
,而是返回能够得出结果的最后一个变量【与 C 中的短路类似】
三目运算符:
-
a if cond else b
:相当于 C 中的cond ? a : b
,注意其参数顺序【与 C 不同】,但更贴近自然语言
特殊条件运算符:
-
in
:被包含于,详见下文「容器」,返回 bool -
not in
:in
的否定,返回 bool -
is
:判断两个变量的地址是否相同,不可重载,返回 bool -
is not
:判断两个变量地址是否不同,不可重载,返回 bool
字符串
Python 将字符串封装成了基本类型并处理了多种运算,带来许多便利,注意基本类型本身是不可修改的,所谓修改其实是将重新生成另一个字符串,再将其赋值给目标。
此外,Python 中没有单独的字符类型,单个字符将被视为长度为 1 的字符串。【与 C 不同】,可以用 ""
或者 ''
括起字符串。
下面是一些常用函数,设 str
是一个字符串:
-
str.title()
:返回单词首字母大写,其余字母小写(不管以前是不是大写)的字符串 -
str.upper()
、str.lower()
:返回全大/小写的字符串 -
str1 + str2
:返回用加号拼接的字符串【与 C++ 的string
类似】 -
str * 3
:返回重复三遍拼接的字符串 - 制表符
\t
,换行符\n
【与 C 相同】 -
str.lstrip()
、str.rstrip()
、str.strip()
: 返回删除开头/末尾/两端空白的字符串 -
str.replace(str1, str2)
:将字符串中的单词str1
全部替换成str2
-
str.split()
:以空格为分隔符把字符串拆成一个个单词,并返回包含这些单词的列表
此外还有一种跨行字符串,用 ’‘’
或 ”“”
括起来,由于脚本中顺序执行时不会输出变量值,这类字符串常用于跨行注释,特别是函数头注释。
输入输出与编码
此处说明几个重要函数:
-
len(obj)
:获取obj
的长度,常用于获取字符串(注意是 Unicode,因此中文和英文字符都占 1 位)、字节串、容器的长度 -
str(a)
: 把数字(整型或浮点型)a
转换成字符串 -
chr(0x4f60)
:将整型变量i
转化成单个字符 -
ord('你')
:获取单个字符的编码(Unicode),注意在 Unicode 中,英文字母的编码有意设置与 ASCII 码一致
最常用的输出语句 print
,本质上是将变量转化为字符串输出,在末尾自动换行。该函数可以有多个变量,变量间用 ,
分隔,输出时会用空格隔开。
如果要在一句话中插入许多变量,这条 print
语句可能会很丑陋,因此 Python 中有三种格式化字符串的方法。
-
%
:如print('I am %d' % (age))
,【与 C 类似】 -
str.format()
:如print('hello, {}'.format(name))
-
f-string
:如print(f'hello,{name}.I am {age}')
其中 f-string
是 Python3.6 的新特性,最为直观便利。
此外,input('Press ENTER to continue')
是常见的一种输入语句,会显示对应的提示内容,读入内容以回车键结尾。
字节串
bytes
即是 Python 中的字节串,它表示最纯粹的二进制数据,【类似 C 的 unsigned char *
】,但是在显示上它仅支持 ASCII 显示,因此用肉眼看显得有些不伦不类,通常它只存在于数据的处理过程中。
bytes
的构造与字符串类似,但是要加一个 b
做前导,如 print(b'\x41')
。
容器
Python 提供了一系列内置容器,它们如同 C++ 的 STL ⼀样,不过比 STL 的用法灵活得多。
同样先介绍几个重要函数:
-
type(obj)
:可以获取参数obj
的类型 -
isinstance(obj, class_or_tuple)
:可以判断obj
是不是类的实例 -
id(obj)
:获取obj
的地址,a is b
等价于id(a) == id(b)
列表 | List
列表(list)是很常用的容器,常被看作 Python 中的数组,但实际上【与 C++ 的 vector 类似】。设 lst
是一个列表:
基础操作
- 定义列表:
lst = [a, b, c, d]
,其中a,b,c,d
等是列表的元素,类型可以不同 - 构造空列表:直接写
[]
或list()
- 打印列表:
print(lst)
(会将列表中的元素列出,括在方括号里) - 访问元素:
lst[3]
, 下标从 0 开始。此外,还支持负数索引,-1
表示倒数第一个,-2
倒数第二个【与 C 不同】
修改、添加、删除元素
- 修改:直接访问元素并赋值
-
lst.append(x)
:在列表末尾添加元素x
-
lst.insert(idx, x)
:在列表索引idx
处插入一个元素x
(插入后,x
的索引是idx
,其后的元素后移一格) -
del lst[3]
:删除指定元素(删除后,其后元素前移一格) -
lst.pop()
:弹出并返回最后一个元素 -
lst.pop(idx)
:弹出并返回指定元素 -
lst.remove(x)
:删除第一个值为x
的元素
组织列表
-
lst.sort()
、lst.sort(reverse = True)
:对列表排序,永久性修改顺序 -
sorted(lst)
、sorted(lst, reverse = True)
:返回排序后的列表,但不改变列表原有顺序 -
lst.reverse()
:翻转列表,永久性修改顺序 -
len(lst)
:返回列表长度,即元素个数(不论类型)
遍历列表
从头到尾遍历列表:for i in lst:
循环表达式
【与 C 不同】i
是列表元素,不是索引;循环结束后 i
停留为最后一个元素。
若要检查列表是否为空,可以用 if lst:
条件表达式,返回 bool
列表切片
lst[l:r]
:返回一个列表,元素依次是lst
列表的索引在左闭右开区间内的元素,省略l
或r
则默认从头开始或到尾结束lst[l:r:step]
:指定步长为step
切片,step
为 -1 时返回倒序可以用循环遍历列表切片:
for i in lst[l:r]:
复制列表:在切片中同时省略
l
和r
,即返回从头到尾的列表,如lst2 = lst1[:]
,而非lst2=lst1
,后者lst1
和lst2
实质是同一个列表【类似 C 的引用】
元组 | Tuple
元组就是元素值不可修改(弱意义上的,其中元素的元素可以被修改)的列表。设 tpl
是一个元组:
基础操作
- 定义元组:
tpl = (a, b, c, d)
,把列表定义中的方括号改成圆括号()
即可 - 定义时的小括号有时候可以省略,可以直接用
,
逗号构造元组【与 C 不同,没有逗号运算符】 - 构造单元组:
(1)
会被理解成表达式,要用(1,)
- 构造空元组,直接写
()
或tuple()
,但(,)
会报错 - 访问元素:
tpl[3]
, 下标从 0 开始。
遍历元组
for i in tpl:
和列表一样
修改元组
元组中元素的值不能修改,但是元组变量本身可以被赋值,这点与字符串类似。此外,如果元组的中的元素是可修改的,如 List,
元组解包
经典的 Python 交换赋值代码:a, b = b, a
,利用了解包和逗号构造
集合 | Set
集合由一组无序、互不重复的元素构成(在数学上也是如此),在内部用哈希实现。设 st
是一个集合:
基础操作
- 定义集合:
st = {1, 2, 3}
,把列表定义的方括号改成花括号{}
即可 - 构造空集合:只能用
set()
,因为{}
的表达被空字典占用了 - 由列表转集合:
st = set([1, 1, 1, 2, 2, 3])
,会自动去重,常用于去重列表的遍历for i in set(lst):
- 注意集合的元素必须是可 hash 的,不能为 list 这种可变容器变量
添加、删除元素
-
st.add(4)
:添加一个元素 -
st.remove(2)
:删除一个元素
字典 | Dictionary
字典是一系列「键值对」,用于存储一组有穷映射,可将任何 Python 对象作为值,【类似于更高端版本的 C++ 的 map】。
基础操作
- 定义字典:
dic = {'name': '张三', 'age': 18}
,花括号括起一系列键值对,键与值之间冒号:
分隔,键值对之间逗号,
分隔。 - 访问元素:
d['name']
,用键访问 - 注意字典的键必须是可 hash 的,不能为 list 或 set 这种可变容器变量,但可以是 tuple
添加、修改、删除
- 添加:直接赋值即可(即使键本来不存在),如:
dic['x'] = 0
- 修改:
dic['age'] = 18
,直接赋值即可 - 删除:
del dic['age']
遍历字典
- 遍历所有键值对:
for k, v in dic.items():
,其中items()
返回键值对的列表 - 遍历所有键:
for k in dic.keys():
,其中keys()
返回键的列表,可省略 - 由于
keys()
的本质是列表,各种对列表的操作也适用,如:for k in sorted(dic.keys()):
或if 'age' in dic.keys():
- 遍历所有值:
for v in dic.values():
,其中values()
返回值的列表
迭代器
前文提到的所有容器,包括字符串、字节串都是可迭代的,这意味着它们可以用 for
循环来遍历,也可以用生成式构造(下面介绍)。
但最为特殊的迭代器是 range
类型,作为数值列表,与列表有相似之处,但它实际上不占用内存(用于大循环时很节省空间)。下面是一些常用方法:
-
range(l, r)
:依次生成左闭右开区间中的整数【类似于 C++ 的for(int i = l; i < r; i++)
】,如果省略左端,会默认以 0 开始 -
range(l, r, step)
:指定步长为step
【类似于 C++ 的for(int i = l; i < r; i += step)
】 -
min(range(l, r))
、max(range(l, r))
、sum(range(l, r))
:返回数值列表的最小值、最大值、总和 - 在
for i in range(10):
的循环中改变了i
的值,如i += 1
, 不会影响循环的次数,因为迭代器中的数值列表是不可修改的【与 C 不同】
生成式
使用生成式构造容器是非常常见、高效的操作,下面举几个例子:
-
a = list(range(10))
:直接转化数值列表为基本列表 -
lst = [i ** 2 for i in a]
:列表生成式 -
st = {x % 7 for x in a}
:集合生成式 -
dic = {x ** 2: x for x in a}
:字典生成式,保留:
来分隔键与值 -
tpl = tuple(x + 1 for x in a)
:元组生成式,直接用()
会被当成表达式
综上所述,生成式表达可以用三段式:表达式 for 循环变量 in 迭代对象 if 筛选条件
,其中最后的筛选条件不一定要。
流程控制
Python 中不用大括号来显式地分块,而是用冒号配合缩进。代码块与代码块之间至少隔着一个空行表示结束。当一个代码块必须存在,又不想写任何语句的时候,可以写一个 pass
作为占位符。
条件分支
a = 1
if a == 1:
print('a is 1')
elif a == 0:
print('a is 0')
else:
print('wtf is a?')
注意,elif
和 else
后面也要有冒号配合缩进,如果有多个条件,用 and
和 or
逻辑运算符连接。
for 循环
前面所列的容器、数值列表都可以用于 for
循环迭代,比较特别的是字符串也可以迭代:
a = 'hello world'
for c in a:
print(c) # 竖着打印出 hello world,因为 print 自带换行
此外,如果在数值列表的迭代中用不到迭代变量 i
,仅作为迭代次数使用,可以用 _
变量表达。
while 循环
a = 10
while 2 <= a < 20: # 语法糖,这种写法是被建议使⽤的
print(a)
a -= 1 # 注意 Python 中没有⾃增 1 和⾃减 1 运算符
跳出循环可以用 break
和 continue
【与 C 相同】
函数
基础函数
def fib(n):
"""函数文档注释"""
current, then = 0, 1
for _ in range(n):
current, then = then, current + then
return current
fib(10) # 调用函数
函数传参
函数传参本质上和赋值号是一样的,都是浅复制(指针指向)【类似 C 的形参】。
传入参数的数量可以不固定,但是必须指定默认值;也可以调换顺序,但必须指明对象。
def add(a, b = 0):
print(a + b)
add(1) # 参数 b 采用默认 0
add(b = 2, a = 1) # 调换顺序,指明对象
当然,也可以传递列表等容器,但传递的也是列表的地址,在函数中修改同样会改变原列表,如果不想修改原列表可以用 [:]
传递切片。
传递任意数量的参数
- 在形参前加星号
*
,Python3 会创建一个该形参名称的元组 - 在形参前加双星号
**
,Python3 会创建一个该形参名称的字典
返回值
作为动态类型语言,函数返回值可以不固定,可以多个 return
在不同情况下返回不同值,或者没有 return
(等价于 return None
)。
函数模块调用
函数可以被存储在模块中被调用,模块是扩展名为 .py 的文件,包含函数的代码【类似于 C 的头文件】
- 导入整个模块:使用
import pizza
导入,调用时使用.
句点,如:pizza.make(16, 'green peppers')
- 导入模块中特定函数:使用
from pizza import make, eat
,调用时无需句点,直接用函数名 - 导入特定函数别名:使用
from pizza import make as mk
,调用时无需句点,直接用别名 - 导入模块中所有函数:使用
from pizza import *
,调用时无需句点,但是会污染命名空间,不建议使用
所有 import
都放在程序开头【类似于 C++ 的 #include<>
】。
Lambda 匿名函数
和很多语言⼀样,Python 中可以使用 Lambda 匿名函数完成一些简单的逻辑,但比较特殊的地方在于,Python 中匿名函数必须只由单个表达式构成,这个表达式的值也就是匿名函数的返回值。
lambda
关键字可以用来创建一个匿名函数,紧跟其后的是参数列表和用冒号 :
分割开的单个表达式。如,lambda x: 2 * x
是将任何输入的数乘 2,而 lambda x, y: x+y
是计算两个数字的和。
使用匿名函数的经验准则是保持简单以及只在本地使用一次。一种常见却不推崇的做法是将其作为简单函数的另一种声明方式,赋值给变量,如:
>>> doubler = lambda x: 2 * x
>>> doubler(5)
10
>>> type(doubler)
对 lambda 函数命名的唯一作用可能是出于教学目的,其问题在于这使得调试不那么直观——错误信息只会提示某个
lambda
函数存在问题,但不会提示哪个函数。
正确的做法应该是将其作为参数传递,如 .sort
函数、sorted
函数等,此时单个表达式会被计算为一个值并且参与后续的计算。
>>> integers = [(3, -3), (2, 3), (5, 1), (-4, 4)]
>>> sorted(integers, key=lambda x: x[-1])
[(3, -3), (5, 1), (2, 3), (-4, 4)]
>>> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> nums.sort(key=lambda a: abs(5-a))
>>> nums
[5, 4, 6, 3, 7, 2, 8, 1, 9]
文件操作
读取文件
一种【与 C 相似】的操作是:
file = open('temp.txt', 'r') # 打开一个文件,返回表示文件的对象
contents = file.read() # 读取整个文件
print(contents)
file.close() # 关闭文件
此外,还有一种更推崇的操作方式,使用 with
上下文管理器,将打开的文件的对象存储在 as
后的变量,这样可以避免由于忘记关闭文件导致丢失数据:
with open('temp.txt', 'r') as file:
contents = file.read()
print(contents)
回到读取本身,方法 read()
读取整个文件的内容并返回单个字符串,并包含每个 \n
换行符。
但实际操作中读入一个长字符串是丑陋而且难以处理的,我们更倾向于逐行读入,用 line.rstrip()
消除行末的 \n
:
with open('temp.txt', 'r') as file:
for line in file: # file 也可作为一个迭代器
print(line) # 注意每一行末都会有一个 \n 被打印,而 print 本身又自带换行
另一种逐行读入是创建一个包含文件各行内容的列表,每个元素是一行内容的字符串,包含行尾的 \n
:
with open('temp.txt', 'r') as file:
line1 = file.readline() # 读入第一行作为字符串
lines = file.readlines() # 读入第二行以后的行(文件指针被移动了)
写入文件
with open('temp.txt', 'w') as file:
file.write("I love programming.\n") # wirte 不会换行,要手动加
open()
函数的第二个实参 w
表示写入(自动创建或覆盖原内容),r
表示只读,a
表示附加(自动创建或添加到文件末尾),r+
表示读写。如果不加第二个实参,则默认为 r
只读。
方法 write()
表示将字符串写入文件。如果要写入数值,应先用 str()
将其转化为字符串。
类
Python 中的类基础用法比 C/C++ 更简洁一些,待补充。