跳到主要内容

L2-Python语法基础

预备知识

  • Python 中的语句可以直接执行
  • # 表示从此开始到行尾都是注释
  • 段落注释采用''' (三引号中间的是注释掉的部分) '''
  • 变量赋值不用事先声明、不用写类型
    • 数组都是 object*[] 类型,所以可以混着存任意的对象
  • type(x) 函数获取对象类型,用 dir(x) 函数获取对象的方法,用 id(x) 函数获取对象的 ID,(其实是对象的地址)
# 例子:
a = 43 # 在内存中申请一个空间存放 43,再用 a 指向它,id(a) 为 6
b = a # 将 b 指向 a 指向的东西,id(b) 为 6
  • print(x) 函数用于输出指定内容,例如 print(123),也可以传入多个值,如 print(1, 2, 3)

1. 常见数据类型

类型名称例子
int整数-100
complex复数1 + 2j
bool布尔型只有 TrueFalse
float浮点数3.1416
str字符串'hello', "abc"
list列表[1, 0.3, 'hello']
tuple元组('hello', 10)
set集合{1, 2, 3}
dict字典{'dogs': 5, 'pigs': 3}
NoneType只有 None

注意:

  • int 没有大小限制,可以做任意大的整数运算
  • float 是 64 位浮点数,相当于 C/C++ 中的 double 类型
  • bool 类型只有首字母大写的 TrueFalse
  • NoneType 是特殊的类型,它只有 None 这一个值/对象实例,通常被用来表示空值

1.1 数值运算

# 基本算术运算
1 + 2 # 3
1 - 2 # -1
2 * 3 # 6
4 / 2 # 2.0 注意:无论结果在数学上是否是整数,一定是 float 类型
5 // 2 # 2 向下取整除法
5 % 2 # 1 取余
2**10 # 1024 幂次

# 原地运算
a = 1
a += 2 # a 变为 3

# 逻辑运算
True and False # False
True or False # True
not True # False

# 数学函数
abs(-3) # 3 绝对值
min(3, 4) # 3
max(3, 4) # 4
int(-3.9) # -3 向 0 取整
round(-3.9) # -4 就近取整

# 科学计数法、进制表示
1e-6, 0xFF, 0o67, 0b1110 # (1e-06, 255, 55, 14)

1.2 比较操作

1 == 2         # False 等于
1 != 2 # True 不等于
1 < 2 # True 小于
1 <= 2 # True 小于等于
1 > 2 # False 大于
1 >= 2 # False 大于等于
1 == 1.0 # True
[1, 2, 3] == [1.0, 2.0, 3.0] # True

==!= 是基于值做判断的;is 是基于 id 判断的,即判断两个东西是不是同一个对象

a = 1
b = 1.0
a == b # True
a is b # False

a = [1, 2, 3]
b = [1, 2, 3]
a == b # True
a is b # False

注意:"不是" 一般写作 a is not b

注意:关于 None 的判断用 isis not 而不是 ==!=,理由是 None 这个对象是唯一的,我们只需要根据 id 判等,而不是根据值判等

a = None
b = 1
c = None
a is b # False
a is c # True

python 中的比较操作可以串着写

1 < 2 < 3      # True,等价于 1 < 2 and 2 < 3

# 可以串任意比较操作、任意多个
1 < 2 != 3 < 4 > 0 == 0 # True
# 等价于 1 < 2 and 2 != 3 and 3 < 4 and 4 > 0 and 0 == 0

1.3 字符串

可以写成单引号或者双引号:'abc'"abc"

特殊字符需要转义操作\'123\n456'

单引号字符串中的单引号、双引号字符串中的双引号,也需要转义操作:'123\'456'

但单引号字符串中的双引号、双引号字符串中的单引号不需要转义操作,所以一般来说写成下面这样:"123'456"

使用 3 个单/双引号的字符串可以跨行:

'''第一行
第二行
第三行'''

字符串操作:(注意:字符串是不可变的,任何操作都会返回新的字符串对象)

  • 拼接:'ab' + 'cd' -> 'abcd'
  • 重复:'ab' * 3 -> 'ababab'
  • 分割:a.split(b),以字符串 b 为界分割 a
"1 2 3".split(" ")       # ['1', '2', '3']
  • 连接:a.join(b),以字符串 a 来将字符串序列 b 中元素连接起来
" ".join(["1", "2", "3"])    # '1 2 3'
  • 替换:a.replace(b,c),将字符串 a 中的 b 都替换为 c
"1,2,3".replace(",", "@")    # '1@2@3'
  • 大小写转化:upper()、lower()
"abcd123ABCD".upper()        # 'ABCD123ABCD'
"abcd123ABCD".lower() # 'abcd123abcd'

字符串转换:

  • str(a): 将 a 转化为字符串
  • hex(a),oct(a),bin(a): 将整数 a 转化为 16、8、2 进制字符串
  • int(str,b): 将字符串 str 转化为 b 进制整数
  • float(str): 将字符串 str 转化为浮点数
str(3.4), hex(234), int("FA", 16), float("5.78")
# ('3.4', '0xea', 250, 5.78)

Raw 字符串:在引号前面加 r,表示不将 \ 视为转义。

r"C:\abc\def"    # 'C:\\abc\\def'

模板字符串:在前面加 f,在需要用到变量值时非常方便,只需要用 {} 括起来

a = 1
b = 2
f"a 的值是 {a},b 的值是 {b}" # 'a 的值是 1,b 的值是 2'

# 除了变量名,也可以是其他表达式
f"{2**10 + a}" # '1025'

可以用类似 C 中 printf 的语法对浮点数格式化:

a = 1.2345678
f"a ≈ {a:.3f}" # 'a ≈ 1.235' 3位小数

如果需要用到单纯的{或者}字符,需要写两遍表示转义:

a = "b"
f"{{ a }} vs {a}" # '{ a } vs b'

1.4 索引和切片

索引:
对于一个有序序列,可以通过索引的方法来访问对应位置的值。字符串便是一个有序序列的例子,使用[]来对有序序列进行索引。
索引是从 0 开始的,索引 0 对应与序列的第 1 个元素,当索引值大于最大值时,就会报错。
python 中还有负索引值,其为从后向前计数。

str = "abcdefghijk"
print(str[8], str[-2]) # i j

切片:
可以从序列中取出想要的子序列,其用法为: var[lower:upper:step]
左闭右开,即包括 lower,不包括 upperstep 表示取值的步长。

这三个参数可以省略一部分,例如

  • a[:]:从头到尾,即创建副本
  • a[1:]:从 1 到结尾
  • a[:3]a[:-2]:从 0 到 3、从 0 到倒数第二
  • a[::-1]:逆序
str = "abcdefghijk"
print(str[0:4], str[0:7:2]) # abcd aceg

1.5 列表

列表在Python中非常有用,列表是一个有序的序列。列表用一对 [] 生成,中间的元素用 , 隔开,其中的元素不需要是同一类型,同时列表的长度也不固定。也可以利用list()[]来生成空的列表

a = [1, 2.0, "abc"]
a, type(a), list() # ([1, 2.0, 'abc'], list, [])

列表的操作同字符串类似,如下:

  • len():输出列表的长度
  • +:拼接
  • *:重复
a = [1, 2]
b = [4, 5]
a + b, len(a + b), a * 3 # ([1, 2, 4, 5], 4, [1, 2, 1, 2, 1, 2])

数组的索引和切片可以读取也可以赋值

a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
a[0], a[7], a[3:6], a[::3] # (1, 8, [4, 5, 6], [1, 4, 7])

# 切片赋值
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
a[5:7] = ["a", "b", "c", "d"]
print(a) # [1, 2, 3, 4, 5, 'a', 'b', 'c', 'd', 8, 9]
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
a[5:7] = []
print(a) # [1, 2, 3, 4, 5, 8, 9]

还可以用 del 删除

a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
del a[0:3]
print(a) # [4, 5, 6, 7, 8, 9]
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
del a[::3]
print(a) # [2, 3, 5, 6, 8, 9]
  • 测试从属关系:in
  • 计数和索引:l.count(a) 返回列表 l 中 a 的个数;l.index(a) 返回列表 l 中 a 第一次出现的索引位置,不存在会报错
  • 添加,插入和移除:
    • l.append(a) 在列表最后加入 a
    • l.extend(a) 如果 a 是一个序列,相当于将序列中的每个元素依次 append
    • l.insert(idx,a) 在列表的 idx 索引处插入 a
    • l.remove(a) 移除第一次出现的 a,不存在会报错
  • 排序和反向: a.sort() 对 a 排序,但是改变 a 中的值;sorted() 返回排序的索引,不改变原有值;reverse() 列表反向

1.6 元组

  • 与列表相似,元组也是个有序序列,可以索引和切片,但是其是不可变的
  • 空元组:()tuple()
  • 注意:单元素元组应该写为 (1,),以避免和普通括号表达式产生歧义
t = (1, 3, 4, 6, 7, 56, 83, 3)
print(t[0], t[2:5], t[::2]) # 1 (4, 6, 7) (1, 4, 7, 83)

type((12,)), type((12)) # (tuple, int)

一般的,元组可以用来作为函数的多返回值,同时也可以用来对多变量进行赋值:

w, x, y, z = 1, 2, 3, 4
x = 1, 2, 3, 4
print(x, type(x)) # (1, 2, 3, 4) <class 'tuple'>

1.7 字典

字典,是一种由键值对组成的数据结构,常用于处理一对多的函数映射关系。

基本操作

创建字典: 可以通过 {} 或者 dict() 来创建空字典,同样也可以在创建时使用 key:value 这样的结构来初始化。

a = {}
b = dict()
c = {"1": "a1", "2": "a2"}
print(type(a), type(b), c["1"]) # <class 'dict'> <class 'dict'> a1
b[1] = "b1"
b["2"] = "b2"
print(b) # {1: 'b1', '2': 'b2'}

注意:

  • 字典的键必须是不可变的类型,比如整数,字符串,元组等,而值可以是任意的 python 对象
  • 一般不使用浮点数来作为键,因为其存在存储精度问题

字典方法

  • 取值: d.get(key),相比于使用 d[key],其在字典中键不存在是不会报错,而是返回 None
  • 删除元素: d.pop(key),删除并返回键为 key 的键值对,如果没有返回 None。同样可以使用 del d[key] 来进行删除。
  • 更新字典: d.update(d'),将字典 d' 中元素更新到 d 中。
  • 查询字典: a in d, 查询键 a 是否在字典 d 中。

注意:"不在" 一般写作 a not in d 而不是 not a in d

  • keys 方法:d.keys(), 返回所有键构成的序列。
  • values 方法:d.values(), 返回所有值构成的序列。
  • items 方法:d.items(), 返回所有键值对元组构成的序列。

1.8 集合

集合 set 是一种无序的序列,故当其中有两个相同的元素,只会保留一个;并且为了保证其中不包含相同元素,放入集合中元素只能是确定性对象。
集合的生成可以通过 {} 或者 set() 进行创建,但是在创建空集合,只能通过 set() 创建,因为 {} 表示空字典。

a = set()
b = {}
c = set([1, 2, 3, 2]) # 自动去除相同的元素
d = {1, 2, 3}
print(type(a), type(b), c, d) # <class 'set'> <class 'dict'> {1, 2, 3} {1, 2, 3}

集合操作:

  • 并: a.union(b) 或者 a | b
  • 交: a.intersection(b) 或者 a & b
  • 差: a.difference(b) 或者 a - b;在 a 中不在 b 中的元素
  • 对称差: a.symmetric_difference(b) 或者 a ^ b;在 ab 中,但是不同时在 ab 中的元素

2. 流程控制

2.1 if

  • 基本结构是 if <条件>:
  • 不像 C 语言一样需要 {},而是用缩进来区分层级
  • elif 是 else if 的含义
  • False,None,0,空字符串,空列表,空字典,空集合,都会被当做 False
a = 1
if a < 0:
if a < -1:
print("a < -1")
elif a == -1:
print("a = -1")
else:
print("-1 < a < 0")
else:
print("a >= 0") # 输出:a >= 0
# 因为缩进是必须的,而缩进后的内容又不能为空,所以我们通常写个 pass,它不会做任何事
# 在编写代码时,想要暂时先不写某个分支,可以用 pass 临时占位
if True:
pass

2.2 while

  • 基本格式是 while <条件>:
  • 可以用 break 跳出,用 continue 进入下一次循环
i = 0
total = 0
while i < 1000000:
total += i
i += 1
print(total) # 499999500000

2.3 for 和 range / zip / enumerate

  • 基本格式是for <变量> in <可迭代对象>,可迭代对象如列表、字符串、range、打开的文件等
  • range 用于创建一串等差数列
    • range(3) -> 0, 1, 2
    • range(1, 5) -> 1, 2, 3, 4
    • range(1, 10, 2) -> 1, 3, 5, 7, 9,从 1 开始到 10,步长为 2
  • 同样可以用 breakcontinue
  • 可选:可以接 else,当循环正常退出而非 break 退出时会执行
a = [1, 2, 3]
for i in a:
if i == 1:
print("a 里有 1")
break
else:
print("a 里没有 1") # 不会执行,因为break了

python 中的一个编程习惯是用 _ 变量表示不使用的值,比如我们想循环 3 次但不需要循环变量:

for _ in range(3):
print(233)

列表推导式

比 for-append 更快地创建列表

a = []
for i in range(10):
a.append(i * i)

# 等价于
a = [i * i for i in range(10)]

可以有 if

a = []
for i in range(10):
if i % 2 == 0:
a.append(i * i)

# 等价于
a = [i * i for i in range(10) if i % 2 == 0]

可以有多重循环

a = []
for i in range(10):
for j in range(i, 10):
a.append(i + j)

# 等价于
a = [i + j for i in range(10) for j in range(i, 10)]

zip

用于同时迭代多个可迭代对象,迭代次数以最短的那个为准

for x, y, z in zip("1234567", "abcdefg", "ABCDE"):
print(x, y, z)
# 输出:
# 1 a A
# 2 b B
# 3 c C
# 4 d D
# 5 e E

enumerate

用于同时迭代下标和值

a = "abcdefg"
for i, j in enumerate(a):
print(i, j)
# 输出:
# 0 a
# 1 b
# 2 c
# 3 d
# 4 e
# 5 f
# 6 g

3. 函数

  • 格式:def 函数名(参数列表):
  • 参数列表直接写变量名称,同样不用写类型
  • = 定义关键字参数,类似 C++ 中的默认参数
  • return 返回返回值,可以是任意类型
# 什么都不做的函数
def f():
pass

# 加法
def add(x, y):
a = x + y
return a

add(1, 2) # 3

python 不会检查传入的参数类型,这样既有灵活性,又有隐患:

add("abc", "def")    # 'abcdef'
add("abc", 123) # 报错:TypeError

两种传参模式:

  • 位置模式:按照位置传入参数
  • 关键字模式:使用 a=123 这样显式的指定参数名

可以混合这两种模式,但是位置模式必须在关键字模式之前。

print(add(x=2, y=3))    # 5
print(add(y="foo", x="bar")) # barfoo
print(add(2, y=3)) # 5

设定参数默认值

def quad(x, a=1, b=0, c=0):
return a * x**2 + b * x + c

print(quad(2.0)) # 4.0
print(quad(2.0, b=3)) # 10.0

接收不定长参数

*args 表示将多余的位置参数作为元组传给 args

def add(x, *args):
total = x
for arg in args:
total += arg
return total

print(add(1, 2, 3, 4)) # 10
print(add(1, 2)) # 3

**kwargs 表示将多余的关键字参数作为字典传给 kwargs

def add(x, **kwargs):
total = x
for arg, value in kwargs.items():
print("adding ", arg)
total += value
return total

print(add(10, y=11, z=12, w=13)) # 46

返回多个值

def f():
return 1, 2, 3

a, b, c = f()
print(a, b, c) # 1 2 3

4. 输入输出

input 函数获取输入:

a = input("请输入:")  # 在 vs code 里运行时会在上方弹出一个输入框

输出用 print:

print(123)
a = 123
print("a 的值是:", a) # a 的值是: 123

更复杂的输出可以利用模板字符串:

a = 3
b = 3.1415926535
print(f"a 的值是 {a:03d},b 的值是 {b:.2f}") # a 的值是 003,b 的值是 3.14

5. 文件操作

# 写文本文件
with open("123.txt", "w", encoding="utf8") as f:
f.write("这是一行\n")
f.write("这是另一行\n")

with open("123.txt", "a", encoding="utf8") as f:
f.write("再给你加一行\n")

# 读取文本文件
with open("123.txt", "r", encoding="utf8") as f:
for i in f: # 迭代每一行
print(i)

with open("123.txt", "r", encoding="utf8") as f:
a = f.read() # 返回一个字符串

with open("123.txt", "r", encoding="utf8") as f:
a = f.readlines() # 返回每一行的字符串的列表

6. 包

类似 C/C++ 中的 #include 语句,我们可以用 import 导入我们希望用的包

import math
math.sin(math.pi / 4) # 0.7071067811865475

# 只导入部分函数
from math import sin
sin(1) # 0.8414709848078965

# 导入多个函数
from math import sin, cos
cos(1) # 0.5403023058681398

# 给导入的函数换个名字
from math import sin as a_ba_a_ba
a_ba_a_ba(1) # 0.8414709848078965

# 导入包中的全部内容(不推荐)
from math import *
sin(pi / 2) # 1.0

7. 类

基本格式如下

class A:
def __init__(self):
self.a = 1

def f(self, x):
self.b = 1
return self.a + x
  • python 中以 __ 开头和结尾的是特殊的函数或变量
  • 这里 __init__ 函数会在对象创建时执行,可以理解为构造函数
  • 不以 ___ 开头的函数都是 public
  • 非静态类成员函数在被调用时会将类对象作为第一个参数传入,一般约定用 self 作为参数名
  • 在任何函数中,都可以通过 self.xxx = xxx 的方式给对象添加新的属性

如果需要继承,基本格式如下

class B(A):
def __init__(self):
super().__init__()
...

这里 super() 会返回它所继承的父类(即 A),super().__init__() 表示调用父类的 __init__ 函数。

8. 异常处理

基本格式如下

try:
...
except ...:
...
else: # 可省略
...
finally: # 可省略
...
  • try 后面接可能会出错的代码
  • except 后面接要捕获的错误类型,不接等价于 except Exception,能捕获大部分错误类型;里面写出错后要执行的代码
  • else 后面是不出错时执行的代码
  • finally 后面是无论出不出错都会执行的代码
  • raise 抛出一个指定类型的错误
  • assert 在断言为假时抛出一个 AssertionError 类型的错误