【杂记】Python 3 Getting Started

37 minute read

强推菜鸟教程。本文由其整理而来。

§ 1 最基础语法

§ 1.1 注释与输入输出

  • 基本样例

     1a = input("Input an interger, fucking kids!\n") # input
     2
     3print("The integer you just inputed is", a, "!") # output
     4
     5'''
     6在这里写多行注释。
     7'''
     8
     9"""
    10在这里写多行注释。
    11"""
    
  • 输出相关

    如果想在输出中加入变量,或者「一块一块」地输出,我们可以使用逗号进行分隔。例如:

    1a = "apples"
    2b = 23
    3print("There are", b, a, "on the table.")
    

    这个程序段将输出 There are 23 apples on the table.

    此外,在 python 3.x 中,我们可以在 print() 函数中添加 end = "???" 参数,使得输出的末尾为 ???。

    Python 3 中,print 函数的 end 默认值为 \n,这也是为什么一般的 print 函数最后会默认换行。故而,我们可使用 print("Content", end = "") 语句达到不换行效果。

    「end 参数」可以与「逗号分隔」搭配使用,不过 end 参数只能出现在最后。

§ 1.2 语句的换行、压行和缩进

和 C/C++ 的使用分号表示语句结尾不同,一般而言,Python 一行自成一条语句。

  • 如果需要在一行里写多条语句(压行),可以使用分号 ; 进行分隔。在这种情况下,一行里面的最后一条语句最后依然不能有分号。例如:

    1f = b + c; g = d + e; h = f + g
    

    等价于

    1f = b + c
    2g = d + e
    3h = f + g
    
  • 如果需要让一条语句拓展到多行(反压行),可以使用 +\ 连接。例如:

    1e = b +\
    2    c +\
    3    d
    

    等价于

    1e = b + c + d
    
  • 与在 C/C++ 中的用大括号表示层次不同,Python 中用缩进表示层次。例如:

    1a = 1
    2if a = 1:
    3    do thing A
    4if a = 2:
    5    do thing B
    

§ 2 基本数据类型

§ 2.1 Python 变量简介

  • Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。

    在 Python 中,变量就是变量,它没有类型,我们所说的"类型"是变量所指的内存中对象的类型。

    1counter = 100          # 整型变量
    2miles   = 1000.0       # 浮点型变量
    3name    = "runoob"     # 字符串
    4
    5print (counter)
    6print (miles)
    7print (name)
    
  • 在 Python 中,允许以下两种特殊的同时赋值:

    • 连续等号赋值

      1a = b = c = 1
      

      运行上述语句后,三个变量 a, b, c 的值均为 1。

    • 逗号分隔赋值

      1a, b, c = 1, 2, "runoob"
      

      运行上述语句后,a = 1, b = 2, c = runoob

  • Python3 中有六个标准的数据类型:

    NumberStringListTupleSetDictionary
    数字字符串列表元组集合字典

    其中:Number / String / Tuple 为 不可变数据。List / Set / Dictionary 为 可变数据

    当该数据类型对应的变量的值发生了变化时,如果它对应的内存地址不发生改变,那么这个数据类型就是 可变数据类型。当该数据类型对应的变量的值发生了变化时,如果它对应的内存地址发生了改变,那么这个数据类型就是 不可变数据类型(事实上,这一说法也并不完全准确。参见 2.10 节:Python 变量的本质)

    接下来依次介绍这六个数据类型。

§ 2.2 Number(数字)

Python 数字数据类型用于存储数值。Number 类型是 不允许改变 的:这就意味着如果改变数字数据类型的值,将重新分配内存空间。

  • Number 类型的创建和删除

    你只需要对一个 Number 进行初始化来进行创建。例如:

    1num1 = 1
    2num2 = 3.0
    

    对一个 Number 进行删除可以使用 del 语句。例如:

    1del num1, num2
    
  • Number 的三种类型

    Number 具有三种类型,分别是 int(整型)、float(浮点型)、complex(复数)。以下是合法的三种类型的具体数值。

    Number 类型示例
    int10, 100, 0x69, 080
    float-3.2, 2.3e100, 70.2E-12
    complex3.14j, 2 + 0.6j

    在 complex 中,用 j 来表示虚根 $i$。

  • Number 的强制类型转换

    使用 int(x) 将 $x$ 转换为一个整数。

    使用 float(x) 将 $x$ 转换到一个浮点数。

    使用 complex(x) 将 $x$ 转换到一个复数,实数部分为 $x$,虚数部分为 $0$。

    使用 complex(x, y) 将 $x$ 和 $y$ 转换到一个复数,实数部分为 $x$,虚数部分为 $y$。$x$ 和 $y$ 是数字表达式。

  • Number 执行运算的方式

    使用 +, -, *, / 对 Number 进行简单的四则运算。

    需要特别注意的是,/ 的结果 永远会返回一个 float。如果你希望返回整除的结果,可以使用整除运算符 //。另外,// 返回的不一定是 int,当分子和分母有一个为 float 的时候,// 将返回 float。

    1a = 6 // 4
    2print(a) # output 1
    3b = 6.0 // 4
    4print(b) # output 1.0
    

    此外,Python 可使用 ** 进行幂运算。例如:

    1a = 3 ** 3
    2print(a) # output 27
    3b = 2 ** 0.5
    4print(b) # output 1.414...
    

    不同类型的数混合运算时会将整数转换为浮点数。

  • 交互模式下的下划线

    在交互模式中,最后被输出的表达式结果被赋值给变量 _ 。例如:

    1>>> tax = 12.5 / 100
    2>>> price = 100.50
    3>>> price * tax
    412.5625
    5>>> price + _
    6113.0625
    7>>> round(_, 2)
    8113.06
    

    此处, _ 变量应被用户视为只读变量。

§ 2.3 String(字符串)

  • 你需要知道的

    • Python 中 单引号和双引号 都可以用以定义字符串,这两者没有区别。
    • Python 中没有 单字符类型(类别 C/C++ 的 char),取而代之的是长度为 $1$ 的字符串。
  • 字符串的基本用法

    • 创建:直接为变量分配一个初始值即可。

      1str1 = "avs"
      2str2 = 'dsafd'
      
    • 访问:一种是通过 str[i] 访问下标为 $i$ 的字符(字符串的下标从 $0$ 起始),另一种是通过 str[l:r] 访问下标在 $l \sim (r - 1)$ 内的子字符串(左闭右开)。

      特别地,下标还可以进行负索引。例如 str[-2] 表示的是 str 的倒数第二个字符。例如:

      str[]earth
      顺索引01234
      负索引-5-4-3-2-1

      在这个实例中,str[1] = str[-4] = 'a'str[0:3] = str[-5:3] = "ear"

    • 拼接:使用字面意思的 +* 符号对字符串进行拼接。例如:

      1str = "earth"
      2print(str[-5:3] * 2 + 'e' + "a") # output: earearea
      3  ```
      
  • 字符串转义

    下面仅列举出一些常用的字符串转义符。

    转义字符含义
    \(在行尾)续行符
    \\反斜杠符号
    \'单引号
    \"双引号
    \n换行符

    [Attention] 如果希望字符串中的转义全部失效,只需要在字符串的前引号前加上 r 或者 R 即可。例如:

    1str = r"\n\n\n"
    2print(str) # output: \n\n\n
    
  • 字符串格式化

    类似于 C/C++ 中的 %d%s 等参数,我们可以用这些小玩意儿作为其他变量的代替。

    与 C/C++ 中不同的是,分割用的逗号变成了百分号;此外,我们甚至可以把结果赋到一个字符串上。例如:

    1str = "abc %d %lf def%s" % (114, 514.0, 'www')
    2print(str)
    

    这个程序段将输出 abc 114 514.000000 defwww

  • 字符串内建函数

    以下是 String 类型常用的一些函数。

    函数意义
    count(sub, beg = L, end = R)返回 sub 在 String 里面出现的次数,如果 beg 或者 end 指定则返回指定范围内 sub 出现的次数
    find(sub, beg = L, end = R)检测 sub 是否包含在字符串中,如果指定范围 beg 和 end,则检查是否包含在指定范围内,如果包含返回开始的索引值,否则返回 -1
    isalnum(S)如果字符串至少有一个字符并且所有字符都是【字母】或【数字】则返回 True,否则返回 False
    isalpha(S)如果字符串至少有一个字符并且所有字符都是【字母】或【中文字符】则返回 True,否则返回 False
    isdigit(S)如果字符串只包含【数字】则返回 True,否则返回 False
    len(S)返回字符串长度
    replace(old, new[, max])将字符串中的 old 替换成 new。如果 max 指定,则替换不超过 max 次。

§ 2.4 List(列表)

列表是最常用的 Python 数据类型。你可以将其与 C/C++ 中的数组类比。但有所不同的是,Python 中的列表是一个链式存储结构——在后面的操作中,你将会有更深的体会。

Python 中的列表的下标从 $0$ 开始。列表的数据项不一定具有相同的类型。

  • 基本操作

    • 初始化

      1list1 = [1926, 817]
      2list2 = ["2132", 'rre']
      3list3 = ["342", 2434.0]
      

      如果你希望建立一个长度为 $n$ 的全为数字 $0$ 的列表,你可以这样初始化(这本质上是 列表推导式,本节最后提及)。

      1a = [0 for i in range(100)]
      
    • 访问

      与 String 的索引方式一致。不管是访问特定下标,还是某个子列表。例如:

      1list = [1, 2, 3, 4, 5]
      2a = list[3]
      3b = list[4]
      4c = list[-2]
      5d = list[1: -2]
      6print(a) # output 4
      7print(b) # output 5
      8print(c) # output 4
      9print(d) # output [2, 3]
      
    • 更新

      通过 list[x] = val 将列表中第 $x$ 项的值改为 $val$。请注意:$x$ 必须小于数组的长度!

      通过 list.append(val) 向列表的末尾增加 $val$ 这个元素。

      通过 del list[x] 删除列表中第 $x$ 项的元素。

      例如:

      1a = [1, 2, 3, 4, 5, 6, 7]
      2a[3] = 10
      3a.append(20)
      4del a[4]
      5print(a) # output [1, 2, 3, 10, 6, 7, 20]
      
    • 拼接

      和 String 类似地,你可以使用 +* 符号对列表进行拼接。

      表达式结果
      [1, 2, 3] + [4, 5][1, 2, 3, 4, 5]
      [3, "22"] * 2[3, "22", 3, "22"]
  • 列表的嵌套

    和 C/C++ 不同的是,在 Python 中没有「二维数组」这一说了——取而代之的是通过列表嵌套实现类似的功能。例如:

    1array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    2print(array[1][2]) # output 6
    
  • 列表的函数与方法

    • 函数:

      函数作用
      len(list)返回列表长度
      max(list)返回列表元素最大值
      min(list)返回列表元素最小值
    • 方法:

      方法作用
      list.append(v)在列表末尾添加元素 $v$
      list.count(v)统计 $v$ 在列表中出现的次数
      list.index(v)找到 $v$ 在列表中的最小索引
      list.insert(x, v)在列表下标 $x$ 处插入元素 $v$,插入后 $list_x=v$
      list.remove(v)删除列表中第一个匹配到的 $v$
      list.pop([index = x])删除列表中下表为 $x$ 的元素,若为空则删除最后一个元素
      list.reverse()反转列表
      list.sort(cmp=None, key=None, reverse=False)reverse 为 False 表示升序(默认),为 True 表示降序;cmp 表示自定义排序方法;key 主要用于表示进行比较的元素
      list.clear()清空列表
  • 列表的比较

    引入 operator 模块的 eq 方法可以实现列表的比较。如果两个列表相等,这个方法返回 True,否则返回 False

    1import operator
    2
    3a = [1, 2]
    4b = [2, 3]
    5c = [2, 3]
    6print(operator.eq(a,b)) # False
    7print(operator.eq(c,b)) # True
    

§ 2.5 Tuple(元组)

Python 的元组与列表类似,不同之处在于 元组的元素不能修改

元组使用小括号 ( ),列表使用方括号 [ ]

元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。

§ 2.6 Set(集合)

Set 是一个无序的不重复元素序列。

  • 基本操作

    • 创建

      可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 用来创建一个空字典。例如:

      1setA = {114, 514, "apple"}
      2setB = set({0, 1, 2, 3})
      3setC = set(range(4)) # setB == setC
      4setEmpty = set()
      
    • 添加元素

      使用 S.add(x) 将元素 $x$ 添加到集合 $S$ 中。

      此外可以使用 S.update(x) ($x$ 是列表 / 元组等)将多个元素添加到集合 $S$ 中。例如:

      1S = {1, 2, 3}
      2S.add(4)
      3print(S) # output {1, 2, 3, 4}
      4S.update([1, 5], [10, 20])
      5print(S) # output {1, 2, 3, 4, 5, 10, 20}
      
    • 删除元素

      第一种方式是使用 S.remove(x) 将元素 $x$ 从 $S$ 中移除。如果元素不存在,则会发生错误。

      第二种方式是使用 S.discard(x) 将元素 $x$ 从 $S$ 中移除。且如果元素不存在,不会发生错误。

    • 获取集合元素数目

      使用 len(S) 获取集合 $S$ 的元素数目。

      1S = {1, 10, 10, 100}
      2print(len(S)) # output 3
      
  • 其它常用方法

方法含义
S.clear()移除集合中的所有元素
S1.intersection(S2,S3...)求 $S_1, S_2, S_3…$ 的交集
S1.intersection(S2,S3...)求 $S_1, S_2, S_3…$ 的并集
x.difference(y)求在集合 $x$,但不在集合 $y$ 的元素集合 (差集)
  • 列表不可作为集合中的元素。

§ 2.7 Dictionary(字典)

从数学的角度看,Dictionary 建立了若干映射关系。从 C/C++ 的角度看,Dictionary 的功能和 STL 中的 Map 非常类似。

请注意:

1)键必须是唯一的,但值则不必。 可以类比数学中,映射中不可以同时出现两个对应关系,它们的自变量相同。

2)值可以取任何数据类型,但键必须是不可变的。 不可变的数据类型包括 Number / Tuple / String

  • 基本用法

    • 声明

      1d = {key1 : value1, key2 : value2, key3 : value3}
      2d_empty1 = {} # Use {} to create an empty dictionary
      3d_empty2 = dict() # You can also create an empty dict in this way
      
    • 访问

      使用 dic[key] 来获取 kay 对应的 value。

    • 更新

      使用 dic[key] = new_value 来修改 key 对应的值。当在该字典中 key 没有对应的值,字典将自动创建一个新的键值对 key -> new_value

      删除字典中的某个键值对可以使用 del dic[key];清空字典中的所有键值对可以使用 dic.clear();删除字典的话使用 del dic

  • 其它字典的常用内置函数 / 方法

    函数或方法含义
    len(d)返回字典长度
    d.clear()删除字典内所有函数
    d.pop(key[,default])返回 key 对应的值并删除 key 对应的键值对;如果 key 不在字典中则不删除任何键值对,并且返回 default(如果没有设置 default,当 key 不在字典内将会触发 error)
    key in d如果 key 在字典 d 内返回 True,否则返回 False

§ 2.8 数据类型转换

Python 中的数据类型转换可以分为两种:

  • 隐式类型转换:自动完成类型转换。
  • 显式类型转换:需要使用特定函数帮助完成转换。

1)隐式类型转换

例如,当把一个 int 和一个 float 相加时,Python 会自动给将 int 转化为更高的 Number 类型 float。最终得数的返回亦是 float 类型。

1a = 1
2b = 1.4
3c = a + b
4print(c) # output 2.4
5print(type(c)) # output <class 'float'>

2)显式类型转换

从规律上看,Python 一般只能在较为直观的情形进行「隐式类型转换」。像比如 42 + "10" 这种表达式,Python 是肯定无法把 "10" 转化为 10 的。不过这个时候,我们可以考虑「显式类型转换」。

隐式类型转换借助 函数 完成。一般可能用到以下函数:

函数作用
int(x [,base])将 $x$ 转换为一个整数,$base$ 表示进制数,默认为十进制
float(x)将 $x$ 转换到一个浮点数
complex(real [,imag])创建一个复数
str(x)将对象 $x$ 转换为字符串
tuple(s)将序列 $s$ 转换为一个元组
list(s)将序列 $s$ 转换为一个列表
set(s)转换为可变集合
dict(d)创建一个字典,$d$ 必须是一个 (key, value) 元组序列

§ 2.9 Python 推导式

常用的推导式有如下几种:

  • 列表 (list) 推导式
  • 字典 (dictionary) 推导式
  • 集合 (set) 推导式
  • 元组 (tuple) 推导式

下依次介绍。本部分建议结合实例理解。

1) List 推导式

[out_exp_res for value in collection if condition]
  • out_exp_res:列表中元素的表达式,可以是有返回值的函数。
  • for value in collection:迭代 collectionvalue 传入到 out_exp_res 表达式中。
  • if condition:条件语句,可以过滤列表中不符合条件的值。此部分为可选,下同。

【实例 1】

1a = [i * 2 for i in [1, 2, 3] if i != 2]
2print(a) # output [2,6]

【实例 2】

1def fun(x):
2  if x < 2:
3      return x
4  else:
5      return x * 10
6
7a = [fun(i) for i in range(5)]
8print(a) # output [0, 1, 20, 30, 40]

2) Dictionary 推导式

{ key_expr: value_expr for value in collection if condition }

类比 List 推导式即可。其中 key_exprvalue_expr 一般是关于 value 的表达式。

3)Set 推导式

{ expr for value in collection if condition }

注意区分 Set 推导式和 Dictionary 推导式的格式差别。

4)Tuple 推导式

( expr for value in collection if condition )

Tuple 推导式又名生成器表达式。

§ 2.10 Python 变量的本质

当你运行以下的代码,你可能会发现结果出乎你的意料。

1y = [1, 2, 3, 4]
2x = y
3y.append(5)
4print(x)
5print(y)

输出了两行 [1, 2, 3, 4, 5]

这是因为在 Python 中,六种基本变量的本质都是指针

在 C/C++ 中,一般的赋值语句 x = 3 再进行修改 x = 4 的内在逻辑如下:

  • 开辟一个内存空间,代号为 x
  • 将数字 $3$ 放入这个内存空间;
  • 修改时,将同一个内存空间的值改为 $4$。

但在 Python 中不一样。

  • 赋值时,将 x 指向 $3$ 这个 Number 类型的实例;
  • 修改时,将 x 指向 $4$ 这个 Number 类型的实例。

因此,我们便可以解释上述代码——

  • 首先,将 y 指向列表 $[1, 2, 3, 4]$;
  • xy 指向同一个列表;
  • y 所指的列表 $[1, 2, 3, 4]$ 末尾加上一个 $5$;
  • 输出 xy,它们均为 $[1, 2, 3, 4, 5]$。

Python 中的变量分为 可变不可变 的。细心的话可以发现,Python 中所有的不可变变量(Number/Tuple/String)都有不可拓展的性质;反之,所有可变变量(List/Set/Dict)都是可以拓展的。

  • 对于一切的 不可变数据类型 var,其发生的更新可以抽象为一个赋值语句 var = new_val。 假设 var 初始时指向的是 old_val,进行这一赋值后它重新将指向 new_val。这便是为什么我们说,当不可变数据类型对应的值发生了变化时,它对应的内存地址也会改变。因为 old_valnew_val 显然不在同一内存区域。

  • 对于一切的 可变数据类型 var,若其发生的更新是针对其「子内容」的(例如列表的 a[x] = val ,集合的 s.add(x) 和字典的 d[x] = val),则 var 指向的内存地址不会发生改变。这是因为从大的整体来看,列表/集合/字典 它们自身并没有什么变化,变的仅仅是其内部内容。

    但是请注意,如果对可变数据进行整体性修改(如对于一个已创建的列表 a 执行整体赋值 a = [1, 2, 3]),则 a 指向的地址仍会发生改变。

    需要厘清的是,如果将 可变数据类型 var 的「子内容」也看成数据类型的话,这些「子内容」的本质亦为指针。若某个「子内容」是不可变数据,那么修改「子内容」后,「子内容」的指向也将发生变化。

    例如,列表元素具有指针本质。

    1a = [1, 2, 3, 4]
    2b = [1, 2, 3, 4]
    3b[0] = 2
    4b[2] = 4
    

    元组元素也具有指针本质。

    1a = [1, 2, 3]
    2b = (233, a, a)
    3a[0] = 10
    4print(b)
    

    上述代码输出 (233, [10, 2, 3], [10, 2, 3])

    有意思的是,(你可以尝试)在执行 a[0] = 10 之后, b 的内容发生了改变,但 id(b)(b 的地址)并未发生改变,这似乎和「Tuple 是不可变数据」有所相悖。

    我个人的理解是,当我们把元组的元素看作指针之后,我们发现指针实际上并未发生改变,因而 id(b) 不变也是可以理解的。


Quiz:解释下述几个程序的输出。

  • 代码块 1:

    1a = [1, 2, 3, 4, 5]
    2b = a
    3b[3] = 10
    4print(a)
    

    输出 [1, 2, 3, 10, 5]

  • 代码块 2:

    1a = 3
    2b = a
    3a = 7
    4print(b)
    

    输出 3

  • 代码块 3:

    1a = 1919
    2b = [1, 1, 4, 5, 1, 4]
    3c = (a, b)
    4del b[5]
    5a = 1919810
    6print(c, a)
    

    输出 (1919, [1, 1, 4, 5, 1]) 1919810

  • 代码块 4:

    1x = 4
    2a = [1, 2, 3]
    3b = [x, 5, 6]
    4a, b = b, a
    5a[0], b[0] = b[0], a[0]
    6print(a, b)
    

    输出 [1, 5, 6] [4, 2, 3]

§ 3 流程控制

§ 3.1 条件

Python 中选择结构一般如下:

1if condition_1:
2    statement_block_1
3elif condition_2:
4    statement_block_2
5else:
6    statement_block_3 

值得关注的是,Python 中用 elif 代替了 else if

注意每个条件后的 冒号,以及代码的 缩进

如果 statement_block 只包含一个语句,可以将其和 if/elif/else 放在同一行(压行)。

  • match... case 语句

    类比 C/C++ 中的 switch case 语句。

    match 后的对象会依次与 case 后的内容进行匹配,如果匹配成功,则执行匹配到的表达式,否则直接跳过,_ 可以匹配一切。

    例如,下述程序输出 wtf

    1a = 3
    2
    3match a:
    4    case 1:
    5        print("a = 1")
    6    case 2:
    7        print("a = 2")
    8    case _:
    9        print("wtf")
    

    match case 语句只在 Python 3.10 版本及以后可以使用。

§ 3.2 循环

  • 常用循环体

    Python 中的循环语句有 forwhile。接下来将进行介绍。

    • while 循环

      1while condition:
      2  statement
      

      例如,以下代码块输出 55

      1a = 1
      2sum = 0
      3
      4while a <= 10:
      5    sum += a
      6    a += 1
      7
      8print(sum)
      

      特别地,我们可以在 while 语句后连接一个 else 语句。其作用是在跳出循环体后执行一次 else 子句内的内容。例如:

      1a = 1
      2sum = 0
      3
      4while a <= 10:
      5    sum += a
      6    a += 1
      7else:
      8    print(sum + 1) # output 56
      

      如果 while 循环体中的 statement 仅包含一条语句,可以将其和 while 放在同一行(压行)。

    • for 循环

      1for variable in sequence:
      2    statements
      3[else:
      4    statements]
      

      此处的 variablesequence 比较抽象。sequence 可以是多种类型,例如:

      1arr = ["fu", 1.0, [1, 2, 3]]
      2for i in arr:
      3    print(i, end = " ")
      

      sequence 可以是一个列表。输出 fu 1.0 [1, 2, 3]

      1seq = "bilibili"
      2for i in seq:
      3    print(i, end = " ")
      

      sequence 可以是一个字符串。输出 b i l i b i l i

      1for i in range(1, 4):
      2    print(i, end = "@")
      

      sequence 可以是一个 range 函数。输出 1@2@3@

  • 循环结构常用辅助语句

    • breakcontinue 语句

      break 语句可以跳出循环体。如果使用 break 从循环中终止,任何对应的循环 else 块将不执行

      continue 语句被用来告诉程序跳过当前循环块中的剩余语句,然后继续进行下一轮循环。

    • pass 语句

      这是一个空语句,不执行任何操作。

    • range() 函数

      第一种是 range(l, r),内含 $[l,r)$ 内的所有整数。例如:

      1for i in range(1, 10):
      2  print(i + 1)
      

      输出 $2 \sim 10$(每个数字都换行)。

      第二种是 range(r),这个等同于 range(0, r)

      第三种是 range(l, r, step)。step 表示步长,指生成的数的公差。例如:

      1for i in range(1, 10, 3):
      2  print(i, end = " ")
      

      输出 1 4 7

      除了常用于 for 循环的范围限制之外,range() 函数还可以用于生成 List/Tuple/Set。 例如:

      1a = list(range(5))
      2b = set(range(4))
      3c = tuple(range(3))
      4
      5print(a) # output [0, 1, 2, 3, 4]
      6print(b) # output {0, 1, 2, 3}
      7print(c) # output (0, 1, 2)
      

§ 3.3 函数

基本操作:

1def func_name(argument table):
2    statements
3    statements
4    statements
  • 参数的传递问题

    在继续阅读之前,请回顾 2.10 节 - Python 变量的本质。

    参数传递遵循这样一个规则:当函数开始执行时,形参被设定为和实参指向同一个地方。或者说,从地址上看,形参是实参的拷贝。

    例如:

    1def fun(x):
    2    print(x, id(x)) # output 5 140720516166568
    3    x = 10
    4    print(x, id(x)) # output 10 140720516166728
    5
    6a = 5
    7print(a, id(a)) # output 5 140720516166568
    8fun(a)
    9print(a, id(a)) # output 5 140720516166568
    

    我们不难从这个代码块发现,a(实参)与 x(形参)指向的地址是一致的。因而,一些教程说「对于不可变变量,函数传参时是值传递」,个人感觉有失妥当。

    在进行 x = 10 的赋值后,x 所指的 Number 从 5 变成了 10,相应地,其地址也必定发生变化。然而,a 指向的地址并不会发生改变。

    再如(这个例子比较重要!):

     1def fun1(x):
     2  print(x, id(x)) # output [1, 2, 3] 2339008125888
     3  x.append(10)
     4  print(x, id(x)) # output [1, 2, 3, 10] 2339008125888
     5
     6def fun2(x):
     7    print(x, id(x)) # output [1, 2, 3, 10] 2339008125888
     8    x = [1, 1, 4, 5, 1, 4]
     9    print(x, id(x)) # output [1, 1, 4, 5, 1, 4] 2339008997632
    10
    11a = [1, 2, 3]
    12print(a, id(a)) # output [1, 2, 3] 2339008125888
    13fun1(a)
    14print(a, id(a)) # output [1, 2, 3, 10] 2339008125888
    15fun2(a)
    16print(a, id(a)) # output [1, 2, 3, 10] 2339008125888
    

    我们首先声明了一个列表 [1, 2, 3],并让 a 指向这个列表。执行第一个函数时,x 作为形参也指向同一个 [1, 2, 3]。然后执行 append 方法,致使 x 指向的列表最后增加了一个 10。回到主干上,由于 ax 指向同一个列表,故而它们都变成了 [1, 2, 3, 10]

    接着执行第二个函数。第二次 x 作为形参依然指向同一个 [1, 2, 3,10]。当执行赋值操作时,x 整体发生了改变,故而 x 被指向了一个新的列表 [1, 1, 4, 5, 1, 4],而注意,x 原来指向的列表(也是 a 指向的列表)并未发生改变。故而回到主干上时,a 指向的依旧是 [1, 2, 3, 10]

  • 参数的设定

    调用函数时可使用的正式参数类型主要包含四种:必需参数,关键字参数,默认参数,不定长参数。

    • 必需参数

      必需参数须以正确的顺序传入函数。调用时的数量和顺序必须和声明时的一样。就是你一般写函数时,所使用的参数设定方式。例如:

      1def fun(a, b, c):
      2    d = a + b + c * 2
      3    return d
      4
      5a, b, c = 1, 2, 3
      6print(fun(a, b, c)) # output 9
      
    • 关键字参数

      使用关键字参数允许函数调用时参数的顺序与声明时不一致。例如:

      1def fun(x, y, z):
      2    res = x + y + z * 2
      3    return res
      4
      5a, b, c = 1, 2, 3
      6print(fun(x = c, y = b, z = a)) # output 7
      
    • 默认参数

      在「关键字参数」的基础上,如果某一形参具有默认值,当调用时实参没有指定值时,将采取默认值。例如:

      1def fun(x = 11, y = 45, z = 14):
      2    res = x + y + z * 2
      3    return res
      4
      5a, b, c = 1, 2, 3
      6print(fun(x = c, y = b)) # output 33
      
    • 不定长参数

      当你不确定函数的参数数量时,你可能需要不定长参数。

      1def fun(x, y, *t):
      2    print(x, y, t)
      3
      4fun(1, 2, 3, 4, 5) # output 1 2 (3, 4, 5)
      

      加了 * 的形参会以 元组 的形式导入,这便实现了参数的不定长需求。

  • lambda 函数

    Python 使用 lambda 来创建匿名函数。

    所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

    Attention:

    • lambda 的主体是一个表达式,而不是一个代码块。仅仅能在 lambda 表达式中封装有限的逻辑进去。

    • lambda 函数拥有自己的命名空间,且 不能访问自己参数列表之外或 global 里的参数

    lambda 函数的语法只包含一个语句:

    1lambda [arg1 [,arg2,.....argn]]: expression
    

    例如:

    1sum = lambda arg1, arg2: arg1 + arg2
    2print(sum(10, 20)) # output 30
    

    之所以说函数是匿名的,是因为更多时候我们并不一定要给予其名字。例如:

    1print((lambda a, b: a ** b)(2, 3)) # output 8
    

    你甚至可以把 lambda 函数封装在一个函数内。例如:

    1def func(n):
    2  return lambda x: x ** n
    3
    4print(func(2)(3)) # output 9
    5print(func(3)(3)) # output 27
    6F = func(4)
    7print(F(3)) # output 81
    

§ 4 第一阶段:杂项

§ 4.1 作用域

初学 Python 时,一个令人恼火的问题是——我该怎么知道这个变量是全局的还是局部的?本节我们将探讨这个问题。

Python 的作用域分为四种:

§ 4.2 文件操作

§ 4.3 再探输入与输出

§ 4.4 迭代器与生成器

§ 4.5 Python 内置数据结构

§ 4.6 Python 标准库

§ 5 Import

§ 6 面向对象编程