Python 语法

注释

Python 的注释风格:

# 行注释

'''
块注释1
'''

"""
块注释2
"""

注释中的内容将不会被执行。


标识符

  • 首字母必须是大写或小写的英文字母或者下划线 _

  • 其他部分由大写或小写的英文字母、数字和下划线组成。

  • 大小写敏感(区分大小写)。

Python3 中允许使用非 ASCII 标识符,即中文也可作为标识符:

>>> 变量 = 5
>>> print(变量)
5

关键字

Python 关键字keyword)不能作为标识符使用,关键字又称保留字

使用 keyword 模块输出 Python 的所有关键字:

>>> import keyword
>>> print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

变量的赋值

Python 中的变量并不需要声明,直接赋值后就可使用。还可以同时为多个变量赋值。

pai, e = 3.14, 2.72
a = b = c = 1

代码规范

Python 代码中的所有块都是使用空格缩进来表示。同一块中的所有语句都必须包含相同的缩进空格数,一般使用 4 个空格作为缩进,并且最好在代码编辑器中设置将 Tab 转化为空格。

total = 0
# 输出数字 1~9 的平方数
for n in range(1, 10):
    # 块的开始
    square = n * n
    total += square
    print(square)
  # 块的结束
# 输出数字 1~9 的所有平方数的总和
print(total)

缩进相同的一组语句构成一个代码块,或称之为代码组。


函数之间或类的方法之间用空行分隔,表示一段新的代码的开始。类和函数入口之间也用一行空行分隔,以突出函数入口的开始。

空行是程序代码的一部分,但并不是 Python 语法的一部分。空行的作用在于分割两段不同功能或含义的代码,便于日后代码的维护或重构,有无空行并不影响程序运行。


反斜杠 \ 作为续行符,表示下一行是上一行的延续。对于过长的语句,Python 使用反斜杠、换行再加上缩进来将长语句分为多行。

total = item_one + \
        item_two + \
        item_three

[],{}, 或 () 中的多行语句,不需要使用反斜杠 \ 来换行。

total = ['item_one', 'item_two', 'item_three',
        'item_four', 'item_five']

Python 可以在同一行中使用多条语句,语句之间使用分号 ; 分割。

>>> str = '123'; print(str)
123

标准数据类型

Python 支持 6 种基本数据类型:

  • Number —— 数字类型

  • String —— 字符串类型

  • List —— 列表

  • Tuple —— 元组

  • Set —— 集合

  • Dictionary —— 字典

List、Dictionary、Set 是 Python 内置的数据结构。

Python 变量分为可变和不可变数据类型,以下是它们的定义:

  • 不可变(immutable)数据类型:当该数据类型对应的变量的值发生了改变,它对应的内存地址也会发生改变(重新分配内存空间),如 Number、String、Tuple。即,数据中的元素不能被更改。

    # 测试 Number 类型是否是不可变数据类型
    n = 1      # 赋值
    print(id(n), type(n))
    
    n = 2      # 改变值
    print(id(n), type(n))
    
    # 可能输出:
    # 1562536992 <class 'int'>
    # 1562537024 <class 'int'>
    
  • 可变(mutable)数据类型:当该数据类型对应的变量的值发生了改变,它对应的内存地址不发生改变,如 List、Dictionary、Set。即,数据中的元素可以被更改。

可迭代对象:List、Tuple、Set 、 Dictionary 和 String。


Number类型

Python 支持的数字类型:

  • int —— 整数类型。

  • float —— 浮点数类型(实数类型),如 3.143E-2

  • bool —— 布尔类型(TrueFalse)。

  • complex —— 复数类型,如 1.1 + 2.2jcomplex(a, b)

Python 2 中有 long 型,Python 3 中没有。


数字函数

数字函数需要导入 math 模块:import math

函数 描述
abs(x) 返回数字 x(可以是复数)的绝对值(内置函数,不用导入 math 模块)。
ceil(x) 返回数字 x 的上入整数(向上取整)。
cmp(x, y) 如果 x < y 返回 -1,如果 x == y 返回 0,如果 x > y 返回 1。 Python 3 已废弃,使用 (x>y)-(x<y) 替换。
exp(x) 返回 ex 次幂 $e^x$。
fabs(x) 返回数字 x(不能是复数)的绝对值。
floor(x) 返回数字的下舍整数(向下取整)。
log(x[, y]) log(x) —— 返回 x 的自然对数; log(x, y) —— 返回以 y 为基数的 x 的对数:$\log_y{x}$。
log10(x) 返回以 10为基数的 x 的对数。
max(x1, x2,...) 返回给定参数的最大值,参数可以为序列。
min(x1, x2,...) 返回给定参数的最小值,参数可以为序列。
modf(x) 返回 x 的整数部分与小数部分,两部分的数值符号与 x 相同,整数部分以浮点型表示。
pow(x, y[, z]) pow(x, y) —— x**y 运算后的值; pow(x, y, z) —— 相当于 pow(x, y) % z。 通过内置方法调用,会把参数作为整型,而 math 会把参数转换为 float。
round(x [, n]) 返回浮点数 x 的四舍五入值,如给出 n 值,则代表舍入到小数点后的位数。 其实准确的说是保留值将保留到离上一位更近的一端。
sqrt(x) 返回数字 x 的平方根。

String类型

Python 的字符串(String)有以下特点:

  • 字符串常量可以使用单引号 ' 和双引号 " 指定。

    >>> str1 = 'Hello'
    >>> str2 = 'World'
    >>> print(str1, str2, '!')
    Hello World !
    >>> str1 = '打印 "双引号" '
    >>> str1 += "要用 '单引号' 将其括起来"
    >>> print(str1)
    打印 "双引号" 要用 '单引号' 将其括起来
    
  • 使用三个引号可指定一个多行字符串常量。

    >>> str1 = """这是一个段落,
    ... 可以由多行组成"""
    >>> print(str1)
    这是一个段落
    可以由多行组成
    
  • 转义符用 \ 开头。单个反斜杠依然可以作为续行符。

    >>> str = 'str\
    ... ing'
    >>> print(str)
    string
    
  • 使用 rR 可以让反斜杠不发生转义。这样的字符串被称为原始字符串。

    >>> print(r"this is a line with \n")
    this is a line with \n
    
  • 按字面意义级联字符串。

    >>> print('this ' 'is ' 'string')
    this is string
    
  • 字符串可以用 + 运算符连接在一起,用 * 运算符重复。

    >>> print('this ' + 'is ' + 'string')
    this is string
    >>> str = 'Hello World!' * 3
    >>> print(str)
    Hello World!Hello World!Hello World!
    
  • 有两种索引方式:从左往右以 0 开始,从右往左以 -1 开始。

  • 字符串的截取的语法格式:变量[头下标:尾下标:步长]。字符串被截取后返回一个包含所需字符的新字符串。遵循左闭右开原则。

    尾下标1 开始。

    步长是指输出字符串中第 $n$ 个字符后,接着输出第 $(n + 步长)$ 个字符,直至到达尾下标指定处。

    省略步长代表按字符串顺序输出,省略尾下标代表截取到字符串的最后一个字符,省略头下标代表从第一个字符开始截取。

    >>> str='123456789'
    >>> print(str[0])    # 输出字符串第一个字符
    1
    >>> print(str[2:5])    # 输出从第三个开始到第五个的字符
    345
    >>> print(str[0:-1])  # 输出第一个到倒数第二个的所有字符
    12345678
    >>> print(str[2:])    # 输出从第三个开始后的所有字符
    3456789
    >>> print(str[:3])    # 输出从第一个开始到第三个的字符
    123
    >>> print(str[1:5:2])  # 输出从第二个开始到第五个且每隔一个的字符(步长为2)
    24
    >>> print(str[:])    # 同时省略头下标和尾,创建了一个str的副本
    
  • 字符串不能改变。即,字符串的切片不能被赋值。

    >>> str = 'string'
    >>> str[0] = 'S'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'str' object does not support item assignment
    >>> str = 'string'
    >>> print(str)
    string
    >>> str = 'String'  # 修改字符串只能重新进行赋值
    >>> print(str)
    String
    
  • Python 没有单独的字符类型,一个字符就是长度为 1 的字符串。

在Python2中,普通字符串是以 8 位 ASCII 码进行存储的,而 Unicode 字符串则存储为 16 位 unicode 字符串。使用的语法是在字符串前面加上前缀 u

在Python3中,所有的字符串都是 Unicode 字符串。


转义符

转义字符 描述
\(在代码行尾时) 续行符。
\\ 反斜杠符号。
\' 单引号。
\" 双引号。
\a 响铃 print("\a")执行后电脑有响声。
\b 退格(Backspace)。
\000 空。
\n 换行。
\v 纵向制表符。
\t 横向制表符。
\r 回车,将 \r 后面的内容移到字符串开头,并逐一替换开头部分的字符,直至将 \r 后面的内容完全替换完成。
\f 换页。
\yyy 八进制数,y 代表 0~7 的字符,如 \012 代表换行。
\xyy 十六进制数,以 \x 开头,y 代表的字符,例 \x0a 代表换行。
\other 其它的字符以普通格式输出。

格式化字符串

print() 函数能够使用 % 来进行格式化字符串的输出。例如:

>>> print('%e' % 3.14)
3.140000e+00

字符串格式化符号:

符 号 描述
%c 格式化字符及其 ASCII 码。
%s 格式化字符串。
%d 格式化整数。
%u 格式化无符号整型。
%o 格式化无符号八进制数。
%x 格式化无符号十六进制数。
%X 格式化无符号十六进制数(大写)。
%f 格式化浮点数字,可指定小数点后的精度。
%e%E 用科学计数法格式化浮点数。 %e 中显示的字母为小写,%E 则为大写。
%g %f和%e的简写。
%G %f 和 %E 的简写。
%p 用十六进制数格式化变量的地址。

格式化操作符辅助符号:

符号 功能
* 定义宽度或者小数点精度。
- 用做左对齐。
+ 在正数前面显示加号。
<sp> 在正数前面显示空格。
# 在八进制数前面显示零 '0',在十六进制前面显示 '0x' 或者 '0X'(取决于用的是 'x' 还是 'X')。
0 显示的数字前面填充 '0' 而不是默认的空格。
% '%%' 输出一个单一的 '%'
(var) 映射变量(字典参数)。
m.n. m 是显示的最小总宽度,n 是小数点后的位数(如果可用的话)。

辅助符号在格式字符串 % 和符号中间使用,如需指定其它参数需要在字符串后的 % 使用括号 () 指定参数和要显示的值。如:

>>> print('%*d' % (10, 2))
         2

f-string

f-string 是 python3.6 之后版本添加的,称之为字面量格式化字符串,是新的格式化字符串的语法。f-string格式化字符串以 f 开头,后面跟着字符串,字符串中的表达式用大括号 {} 包起来,它会将变量或表达式计算后的值替换进去。

>>> name = 'World!'
>>> f'Hello {name}'  # 替换变量
'Hello World!'
>>> f'{1+2}'         # 使用表达式
'3'

str.format()

str.format() 是用于格式化字符串的字符串方法。旧式的格式化最终会从该语言中移除,应该更多的使用 str.format()

format()str 中的格式化字段({} 及其里面包含的字符)替换为 format() 中的参数。它的用法如下:

>>> print('{} and {}'.format('a', 'b'))      # 按照参数顺序
a and b
>>> print('{1} and {0}'.format('a', 'b'))    # 指定参数位置(从 0 开始)
b and a
>>> print('{} and {b}'.format('a', b = 'b'))  # 指定关键字
a and b

!a(使用 ascii()),!s(使用 str())和 !r(使用 repr())可以用于在格式化某个值之前对其进行转化:

>>> import math
>>> print('常量 PI 的值近似为: {}。'.format(math.pi))
常量 PI 的值近似为 3.141592653589793
>>> print('常量 PI 的值近似为: {!r}。'.format(math.pi))
常量 PI 的值近似为 3.141592653589793

可以使用 :,在右边指定输出形式,在 : 左边的是 format() 参数位置:

>>> import math
>>> print('常量 PI 的值近似为 {0:.3f}。'.format(math.pi))
常量 PI 的值近似为 3.142

: 后传入一个整数, 可以保证该域至少有这么多的宽度。

>>> table = {'a': 1, 'b': 2, 'c': 3}
>>> for name, number in table.items():
...     print('{0:10} ==> {1:10d}'.format(name, number))
...
a          ==>          1
b          ==>          2
c          ==>          3

传入一个字典,然后使用方括号 [] 来访问键值:

>>> table = {'a': 1, 'b': 2, 'c': 3}
>>> print('b: {0[b]:d}; a: {0[a]:d}; c: {0[c]:d}'.format(table))
b: 2; a: 1; c: 3

也可以通过在变量前使用 ** 来实现相同的功能:

>>> table = {'a': 1, 'b': 2, 'c': 3}
>>> print('b: {b:d}; a: {a:d}; c: {c:d}'.format(**table))
b: 2; a: 1; c: 3

字符串方法

方法 说明
str.capitalize() 将字符串的第一个字符转换为大写。
str.center(width[, fillchar]) 返回一个指定的宽度 width 居中的字符串。 fillchar 为填充的字符,默认为空格。
str.count(sub[, start= 0, end=len(string)]) 返回 substr 的指定范围里面出现的次数。 sub —— 搜索的子字符串。 start —— 字符串开始搜索的位置。默认为第一个字符(索引为 0)。 end —— 字符串中结束搜索的位置。默认为字符串的最后一个位置。
str.encode(encoding='UTF-8',errors='strict') encoding 指定的编码格式编码字符串,如果出错默认报一个ValueError 的异常,除非 errors 指定的是 'ignore' 或者 'replace'
bytes.decode(encoding="utf-8", errors="strict") 以指定的编码格式解码 bytes 对象。 encoding —— 要使用的编码,默认为 "utf-8"errors —— 设置不同错误的处理方案。默认为 "strict",意为编码错误引起一个 UnicodeError。 其他可能的值有 "ignore""replace""xmlcharrefreplace""backslashreplace" 以及通过 codecs.register_error() 注册的任何值。 Python3 中没有 decode() 方法,但可以使用 bytes 对象的 decode() 方法来解码给定的 bytes 对象,这个 bytes 对象可以由 str.encode() 来编码返回
str.endswith(suffix[, start=0, end=len(string)]) 检查字符串指定范围是否以 suffix 结束,如果是,返回 True,否则返回 Falsesuffix —— 可以是一个字符串或者是一个元素。 start —— 指定的开始位置。默认为字符串第一个字符(索引为 0)。 end —— 指定的结束位置。默认为字符串的最后一个字符。
str.expandtabs(tabsize=8) 把字符串 str 中的 Tab 符号 \t 转为空格。 tabsize —— Tab 符号的长度,默认为 8。
str.find(sub[, beg=0, end=len(str)]) 检测 sub 是否包含在字符串 str 的指定范围中,如果包含则返回开始的索引值,否则返回 -1sub —— 字符串。 beg —— 指定的开始位置。默认为字符串第一个字符(索引为 0)。 end —— 指定的结束位置。默认为字符串的最后一个字符。
str.index(sub[, beg=0, end=len(string)]) find()方法一样,只不过如果 sub 不在字符串中会报一个异常。
str.isalnum() 如果字符串至少有一个字符并且所有字符都是字母或数字则返 回 True,否则返回 False
str.isalpha() 如果字符串至少有一个字符并且所有字符都是字母或中文字则返回 True, 否则返回 False
str.isdigit() 如果字符串只包含数字则返回 True,否则返回 False
str.islower() 如果字符串中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True,否则返回 False
str.isnumeric() 如果字符串中只包含数字字符,则返回 True,否则返回 False
str.isspace() 如果字符串中只包含空白,则返回 True,否则返回 False
str.istitle() 如果字符串是标题化的(见 title()),则返回 True,否则返回 False
str.isupper() 如果字符串中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True,否则返回 False
str.join(sequence) 字符串 str 作为分隔符,将 sequence 中所有的元素(的字符串表示)合并为一个新的字符串。
len(string) 返回字符串长度。
str.ljust(width[, fillchar]) 返回一个原字符串左对齐,并使用 fillchar 填充至长度 width 的新字符串,fillchar 默认为空格。如果指定的长度小于原字符串的长度则返回原字符串。
str.lower() 转换字符串中所有大写字符为小写。
str.lstrip([chars]) 截掉字符串左边的空格或指定字符。 chars —— 指定截取的字符。
str.maketrans(intab, outtab) 创建字符映射的转换表。intab —— 字符串,表示需要转换的字符。 outtab —— 字符串表示转换的目标。
max(str) 返回字符串 str 中最大的字母。
min(str) 返回字符串 str 中最小的字母。
str.replace(old, new[, max]) 将字符串中的 old 替换成 new,如果 max 指定,则替换不超过 max 次。
str.rfind(sub[, beg=0,end=len(string)]) 类似于 find() 函数,不过是从右边开始查找。即,返回字符串最后一次出现的位置,如果没有匹配项则返回 -1
str.rindex(sub[, beg=0, end=len(string)]) 类似于 index(),不过是从右边开始。即,返回子字符串 sub 在字符串中最后出现的位置,如果没有匹配的字符串会报异常。
str.rjust(width[, fillchar]) 返回一个原字符串右对齐,并使用 fillchar(默认空格)填充至长度 width 的新字符串。
str.rstrip([chars]) 删除字符串末尾的指定字符 chars,默认为空格。
str.split([str1="", num=string.count(str1)]) 以参数 str1 为分隔符截取字符串,如果 num 有指定值,则仅截取 num+1 个子字符串
str.splitlines([keepends]) 按照行('\r''\r\n'\n')分隔,返回一个包含各行作为元素的列表。如果参数 keependsFalse,返回的列表则不包含换行符;如果为 True,则保留换行符。
str.startswith(substr[, beg=0, end=len(string)]) 检查字符串是否是以指定子字符串 substr 开头,是则返回 True,否则返回 False。如果 begend 指定值,则在指定范围内检查。
str.strip([chars]) 在字符串上执行 lstrip()rstrip()。即移除字符串头尾指定的字符 chars(默认为空格)。
str.swapcase() 将字符串中大写转换为小写,小写转换为大写。
str.title() 返回 “标题化” 的字符串,即转化为所有单词都是以大写开始,其余字母均为小写的形式。
str.translate(table[, deletechars=""]) 根据 str 给出的表(包含 256 个字符)转换 str 的字符,要过滤掉的字符放到 deletechars 参数中。
str.upper() 转换字符串中的小写字母为大写。
str.zfill (width) 返回长度为 width 的字符串,原字符串右对齐,前面填充 0
str.isdecimal() 检查字符串是否只包含十进制字符(只存在于 unicode 对象。),如果是返回 True,否则返回 False

encode() 和 decode() 用法实例:

>>> str = "Hello World!";
>>> str_utf8 = str.encode("UTF-8")
>>> str_gbk = str.encode("GBK")
>>> print(str)
Hello World!
>>> print("UTF-8 编码:", str_utf8)
UTF-8 编码 b'Hello World!'
>>> print("GBK 编码:", str_gbk)
GBK 编码 b'Hello World!'
>>> print("UTF-8 解码:", str_utf8.decode('UTF-8','strict'))
UTF-8 解码 Hello World!
>>> print("GBK 解码:", str_gbk.decode('GBK','strict'))
GBK 解码 Hello World!

maketrans() 的用法:

>>> intab = "aeiou"
>>> outtab = "12345"
>>> trantab = str.maketrans(intab, outtab)
>>> str = "this is string example....wow!!!"
>>> print (str.translate(trantab))
th3s 3s str3ng 2x1mpl2....w4w!!!

split() 的用法:

>>> str = "this is string example....wow!!!"
>>> print (str.split( ))       # 以空格为分隔符
['this', 'is', 'string', 'example....wow!!!']
>>> print (str.split('i',1))   # 以 i 为分隔符
['th', 's is string example....wow!!!']
>>> print (str.split('w'))     # 以 w 为分隔符
['this is string example....', 'o', '!!!']

byte样式字符串

在字符串前面加上字符 b,可以得到该字符串的字节类型对象:

b'Hello World!'

使用 16 进制 ASCII 码赋值:

>>> b'\x48\x65\x6C\x6C\x20\x57\x6F\x72\x6C\x64\x21'
b'Hell World!'
>> b"\x01\x02\x03"
b'\x01\x02\x03'

Python 会自动将可读的部分按照转换为文字。如果数据是不可读的,则使用 16 进制来表示。

byte 对象的元素是字节:

>>> bt = b'Hello World!'
>>> bt[2]
108

可以使用 bin() 查看每个字节在内存中的存储方式:

>>> bin(bt[2])
'0b1101100'

除字面量表示之外,还可以用 bytes() 函数来得到字节类型。


List

列表(List)由一系列按特定顺序排列的元素组成,它是写在方括号 [] 之间、用逗号分隔开的元素列表。列表其实更像是 Python 中的线性数据结构的实现,它具有绝大多数常见的线性数据结构的操作。

  • 列表中元素的类型可以不相同,它支持数字,字符串,甚至可以包含列表(嵌套)。

    >>> list = [1, '2', 3.4, [5, 6], (7, 8), {9, 10}]
    >>> print(list)
    [1, '2', 3.4, [5, 6], (7, 8), {9, 10}]
    
  • 有两种索引方式:从左往右以 0 开始,从右往左以 -1 开始。

  • 列表的截取的语法格式:变量[头下标:尾下标:步长]。遵循左闭右开原则。

    尾下标1 开始。

    步长是指输出字符串中第 $n$ 个元素后,接着输出第 $(n + 步长)$ 个元素,直至到达尾下标指定处。

    省略尾下标代表截取到字符串的最后一个字符,省略头下标代表从第一个字符开始截取。

    >>> list = [1, '2', 3.4, [5, 6], (7, 8), {9, 10}]
    >>> print (list[0])        # 输出列表第一个元素
    1
    >>> print (list[1:3])      # 从第二个开始输出到第三个元素
    ['2', 3.4]
    >>> print (list[2:])      # 输出从第三个元素开始的所有元素
    [3.4, [5, 6], (7, 8), {9, 10}]
    >>> print(list[:])        # 同时省略头下标和尾,创建了一个list的副本
    >>> tinylist = [123, 'list']
    >>> print (list + tinylist)    # 连接列表
    [1, '2', 3.4, [5, 6], (7, 8), {9, 10}, 123, 'list']
    >>> double = tinylist * 2    # 重复列表
    >>> print(double)
    [123, 'list', 123, 'list']
    >>> print(list[::-1])      # 步长为-1,表示逆向
    [{9, 10}, (7, 8), [5, 6], 3.4, '2', 1]
    >>> list[len(list):] = [6]    # 在列表末尾插入元素
    >>> print(list)
    [1, '2', 3.4, [5, 6], (7, 8), {9, 10}, 6]
    
  • 列表中的元素是可更改的,并且列表长度也是可改变的。List 内置了许多方法来支持这种特性。

    >>> list = [1, 2, 3]
    >>> list[1] = 6
    >>> print(list)
    [1, 6, 3]
    
  • 列表可以用 + 运算符拼接,用 * 运算符重复。

    >>> list1 = [1, 2, 3]
    >>> list2 = [4, 5, 6]
    >>> list = list1 + list2
    >>> print(list)
    [1, 2, 3, 4, 5, 6]
    >>> list = list1 * 2
    >>> print(list)
    [1, 2, 3, 1, 2, 3]
    

虽然列表和字符串有一些特性很相似,但它们是两个完全不同的类型。

删除列表或列表元素:

>>> list = [1, 2, 3]
>>> del list[3]
>>> print(list)
[1, 2, 3]

列表方法

方法 说明
list.append(obj) 在列表末尾添加新的对象 obj,无返回值。相当于 list[len(list):] = [x]
list.count(obj) 返回 obj 在列表中出现的次数。
list.extend(seq) 在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)。 seq —— 元素列表,可以是列表、元组、集合、字典,若为字典,则仅会将键(key)作为元素依次添加至原列表的末尾。
list.index(x[, start[, end]]) 从列表中找出某个值第一个匹配项的索引位置。如果没有找到对象则抛出异常。 x—— 查找的对象; start —— 可选,查找的起始位置; end —— 可选,查找的结束位置。
list.insert(index, obj) 将对象插入列表,无返回值。 index —— 对象 obj 需要插入的索引位置; obj —— 要插入列表中的对象。
list.pop([index=-1]) 移除列表中 index 指定的元素(默认最后一个元素),并且返回该元素的值。
list.remove(obj) 移除列表中值与 obj 相同的第一个匹配项,无返回值。
list.reverse() 反向列表中元素。
list.sort(key=None, reverse=False) 对原列表进行排序。 key —— 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序;↩ reverse —— 排序规则,reverse = True 降序, reverse = False 升序(默认)。
list.clear() 清空列表,类似于 del list[:]
list.copy() 返回复制后的新列表,类似于 list[:]

list.sort() 指定排序元素:

# 获取列表的第二个元素
def takeSecond(elem):
    return elem[1]

# 列表
random = [(2, 2), (3, 4), (4, 1), (1, 3)]
 
# 指定第二个元素排序
random.sort(key=takeSecond)
 
# 输出类别
print ('排序列表:', random)

# 输出:排序列表:[(4, 1), (2, 2), (1, 3), (3, 4)]

Python 列表的特性可以很方便地将其封装成其他线性数据结构。


列表推导式

列表推导式是指在方括号 [] 中使用 for 语句生成一串序列,这个 for 语句可以有零到多个 forif 子句。返回结果是一个根据表达从其后的 forif 上下文环境中生成出来的列表。

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> [str(round(355/113, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']

Tuple

元组(tuple)与列表类似,不同之处在于元组的元素可修改。元组写在小括号 () 里,元素之间用逗号隔开。

  • 元素的类型可以不相同。

  • 元组中只包含一个元素时,需要在元素后面添加逗号 , ,否则括号会被当作运算符使用。

  • 有两种索引方式:从左往右以 0 开始,从右往左以 -1 开始。

  • 列表的截取的语法格式:变量[头下标:尾下标:步长]。遵循左闭右开原则。

  • 元组元素不可修改是指元组中每个元素的指向永远不变。 无法对元组的元素进行增删操作,但并非所有类型的元素都不可修改。

    • 对于不可变数据类型的元素,的确不能修改。

    • 对于可变数据类型的元素,可以按照其规则修改。

      >>> tuple = (1, [1, 2, 3])
      >>> print(tuple)
      (1, [1, 2, 3])
      >>> tuple[1][0] = 2
      >>> print(tuple)
      (1, [2, 2, 3])
      
  • 可以用 + 运算符拼接,用 * 运算符重复。

元组的某些操作可以参照列表,除了增加和删除元素的操作。


元组支持推导式:

>>> tuple(x**2 for x in range(10))
(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)

Set

集合(set)是由一个或数个形态各异的大小整体组成的,构成集合的事物或对象称作元素或是成员。集合的基本功能是进行成员关系测试和删除重复元素。可以使用大括号 {} 或者构造函数 set() 创建集合。

  • 集合是一个无序不重复元素的集。

  • 元素的类型可以不相同。

  • 元素是可更改的。

  • 创建空集:

    使用不带参数的构造函数 set() 创建,不可使用 {} 创建({} 代表空字典)。

    >>> s = set()
    >>> print(s)
    set()
    
  • 不包含多余的重复元素。在集合中,不会多次出现的相同元素。

  • 不可使用索引或截取等操作。

  • 可以使用集合运算。

    >>> set1 = {1, 2, 3, 4, 5, 7}
    >>> set2 = {2, 4, 6, 7, 8}
    >>> print(set1 - set2)      # set1 和 set2 的差集
    {1, 3, 5}
    >>> print(set1 | set2)      # set1 和 set2 的并集
    {1, 2, 3, 4, 5, 6, 7, 8}
    >>> print(set1 & set2)      # set1 和 set2 的交集
    {2, 4, 7}
    >>> print(set1 ^ set2)      # set1 和 set2 的对称差集
    {1, 3, 5, 6, 8}
    

集合支持推导式:

>>> {x**2 for x in range(10)}
{0, 1, 64, 4, 36, 9, 16, 49, 81, 25}

集合的成员测试:

avengers = {'captain america', 'iron man',
            'thor', 'doctor strange', 'star-Lord',
             'hulk', 'black widow', 'black panther',
             'spider man'}

if 'spider man' in avengers:
    print('蜘蛛侠是复仇者')
else:
    print('蜘蛛侠不是复仇者')
if 'thanos' in avengers:
    print('灭霸是复仇者')
else:
    print('灭霸不是复仇者')

集合方法

方法 描述
set.add() 为集合添加元素。
set.clear() 移除集合中的所有元素。
set.copy() 拷贝一个集合。
set.difference(set1, set2, ...) 返回集合 $(set - set1)\cap(set - set2)\cap\cdots$。
set.difference_update(set1, set2, ...) 移除集合 set 中的元素,该元素在指定的集合 set1, set2, ... 也存在。 即,将集合 set 更新为集合 $(set - set1)\cap(set - set2)\cap\cdots$。
set.discard(value) 删除集合 set 中指定的元素 value。 与 remove() 不同的是,如果 value 不存在 set 中,并不会发生错误。
set.intersection(set1, set2, ...) 返回集合 $set \cap set1 \cap set2 \cap \cdots$。
set.intersection_update(set1, set2, ...) 将集合 set 更新为集合 $set \cap set1 \cap set2 \cap \cdots$。
set1.isdisjoint(set2) 判断集合 set1set2 是否包含相同的元素,如果没有返回 True,否则返回 False
set1.issubset(set2) 判断集合 set1 是否是集合 set2 的子集。如果是,则返回 True;否则返回 False
set1.issuperset(set2) 判断集合 set1 是否是集合 set2 的父集。如果是,则返回 True;否则返回 False
set.pop() 随机移除元素,并返回该元素。
set.remove(item) 移除指定元素 item。如果 item 不存在 set 中,会发生错误。
set1.symmetric_difference(set2) 返回集合 set1set2 的对称差集 $set1 \oplus set2$。
set1.symmetric_difference_update(set2) 将集合 set1 更新为 $set1 \oplus set2$。
set.union(set1, set2...) 返回集合 $set \cup set1 \cup set2 \cup \cdots$。
set.update(added_set) 添加新的元素或集合到当前集合中。 added_set —— 可以是元素或集合。

Dictionary

字典是一种映射类型,用 {} 标识,是一个无序的 key:value (键 - 值对)的集合。字典当中的元素是通过键来存取的,而不是通过偏移存取。字典还可以使用构造函数 dict() 创建。

# 一般方法:
dic = {
    'name':   'tom',
    'age':    20,
    'height': 185,    # cm
    'weight': 71      # kg
}
print(dic)
# 输出:{'name': 'tom', 'age': 20, 'height': 185, 'weight': 71}

# 构造函数:
# 1.
dic = dict(
    name =   'lucy',
    age =    18,
    height = 169,
    weight = 56
)
print(dic)
# 输出:{'name': 'lucy', 'age': 18, 'height': 169, 'weight': 56}
# 2.
dic = dict([
    ('name', 'jiessie'),
    ('age', 25),
    ('height', 178),
    ('weight', 63)
])
print(dic)
# 输出:{'name': 'jiessie', 'age': 25, 'height': 178, 'weight': 63}

# 使用 for 循环:
dic = {
    x: x**2 for x in (2, 4, 6)
}
print(dic)
# 输出:{2: 4, 4: 16, 6: 36}

更多构造字典的方法↩。

  • 键(key)必须使用不可变类型,且是唯一的。

    >>> dic = {1:20, 'a':50, (1, 2, 3):6}
    >>> print(dic)
    {1: 20, 'a': 50, (1, 2, 3): 6}
    >>> print(dic[1])
    20
    >>> print(dic['a'])
    50
    >>> print(dic[(1, 2, 3)])
    6
    
  • 值(value)可以是任何类型的元素。

  • 使用键作为索引。

  • 使用 in 操作符判断键是否存在字典中,如 key in dict ,如果键在字典dict里返回 True,否则返回 Falsenot in 操作符与 in 相反。


字典方法

方法 说明
radiansdict.clear() 删除字典内所有元素,无返回值。
radiansdict.copy() 返回一个字典的浅复制。
radiansdict.fromkeys(seq[, value]) 创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值(可选)。
radiansdict.get(key, default=None) 返回指定键 key 的值,如果键不在字典中返回 default 设置的默认值。
radiansdict.items() 以列表返回一个视图对象,视图对象中的元素以键-值对 (key, value) 的形式显示。 视图对象不是列表,不支持索引,其中的元素是键-值对。字典的视图对象都是只读的,但字典改变,视图也会跟着变化。
radiansdict.keys() 返回一个只包含键的视图对象。
radiansdict.setdefault(key, default=None) get() 类似,如果 key 存在于 radiandict 中,就返回其值;但如果 key 不存在于字典中,将会添加键并将值设为 default
radiansdict.update(dict2) 把字典 dict2 的键-值对更新到 dict 里,无返回值。
radiansdict.values() 返回一个只包含值的视图对象。
radiansdict.pop(key[, default]) 删除字典给定键 key 所对应的值,返回值为被删除的值。key 值必须给出。否则,返回 default 值。如果 key 不存在字典中,返回 default 值(default 值必须给出,否则会报错。
radiansdict.popitem() 随机返回并删除字典中的最后一对键和值 (key, value)。 如果字典已经为空,却调用了此方法,就报出 KeyError 异常。

函数操作

函数 说明
len(obj) 返回对象的元素个数
max(x1[, x2[, x3[, ...]]]) 返回参数中最大值,参数还可以是集合、列表或元组。
min(x1[, x2[, x3[, ...]]]) 返回参数中最小值,参数还可以是集合、列表或元组。

数据类型转换

将数据类型作为函数名即可进行数据类型的转换。以下内置的数据类型转换函数返回一个新的对象,表示转换的值。

函数 描述
int(x, base = 10) x 转换为一个整数。 x —— 字符串或数字; base —— 基数(进制数),默认 10。
float(x = 0.0) x 转换为一个浮点数。 x —— 字符串或数字,默认为 0.0
complex(real[, imag]) 创建一个值为 real + imag * j 的复数。 real —— 为字符串时不需要指定第二个参数,还可以为 int 或 float; imag —— int、float。
str(object = '') 将对象 object 转换为字符串,默认为空字符串。
repr(object) 将对象 object 转换为表达式字符串。
eval(expression[, globals[, locals]]) 用来执行字符串中的有效表达式,并返回表达式的值。 expression —— 表达式字符串; globals —— 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象; locals —— 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
tuple(iterable) 将序列 iterable 转换为一个元组。 iterable —— 可迭代对象,如列表、字典、元组等等。
list(seq) 将序列 seq 转换为一个列表。 seq —— 元组或字符串。
set([iterable]) iterable 转换为可变集合,默认创建空集。 iterable —— 可迭代对象。
dict(**kwarg)class dict(mapping, **kwarg) class dict(iterable, **kwarg) 创建一个字典,默认创建空集。**kwargs —— 关键字,如 key=valuemapping —— 元素的容器; iterable —— 可迭代对象。
frozenset([iterable]) iterable 转换为不可变集合,默认创建空集。 iterable —— 可迭代的对象。
chr(i) 将一个整数 iin rang(256))转换为一个字符。 返回值是当前整数对应的 ASCII 字符。
ord(c) 将一个字符 c 转换为它的整数值(ASCII 数值,或者 Unicode 数值)。 c —— 字符(长度为 1 的字符串)。 如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。
hex(x) 将整数 x 转换为一个十六进制字符串(以 0x 为前缀)。
oct(x) 将整数 x 转换为一个八进制字符串。 Python2.x 版本的 8 进制以 0 作为前缀表示; Python3.x 版本的 8 进制以 0o 作为前缀表示。

repr 的用法:

>>> dic = [1, 2, 3]
>>> repr(dic)
'[1, 2, 3]'

dict() 的用法:

>>>dict()                         # 创建空字典
{}
>>> dict(a='a', b='b', t='t')     # 传入关键字
{'a': 'a', 'b': 'b', 't': 't'}
>>> dict(zip(['one', 'two', 'three'], [1, 2, 3]))   # 映射函数方式来构造字典
{'one': 1, 'two': 2, 'three': 3}
>>> dict([('one', 1), ('two', 2), ('three', 3)])    # 可迭代对象方式来构造字典
{'one': 1, 'two': 2, 'three': 3}

其他操作

使用 Python 的 type() 函数查看变量的类型:

>>> a, b, c, d = 20, 5.5, True, 4+3j
>>> print(type(a), type(b), type(c), type(d))
<class 'int'> <class 'float'> <class 'bool'> <class 'complex'>

使用 isinstance 判断数据类型是否正确:

>>> a = 111
>>> isinstance(a, int)
True

isinstance()type() 的区别在于:

  • type() 不会认为子类是一种父类类型。

  • isinstance() 会认为子类是一种父类类型。即,子类和父类被认为是同一类型。

>>> class A:
...     pass
... 
>>> # B 是 A 的子类
>>> class B(A):
...     pass
... 
>>> isinstance(A(), A)
True
>>> type(A()) == A 
True
>>> isinstance(B(), A)
True
>>> type(B()) == A
False

Python3 中,bool 是 int 的子类,TrueFalse 可以和数字相加。

>>> True == 1
True
>>> False == 0
True

可以通过 is 来判断:

>>> True is 1
False
>>> flag = True
>>> flag is True
True

使用 del 删除对象:

del var        # 删除一个对象
del var_1, var_2  # 删除多个对象

print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end=""

>>> str1 = 'Hello '
>>> str2 = 'World!'
>>> print(str1); print(str2)
Hello
World!
>>> print(str1, end = ''); print(str2)
Hello World!

运算符

算术运算符

Operators Explains
+ 加 操作对象为字符串、列表或元组时,表示将两个对象拼接在一起。
-
* a * b,若 a 为字符串、列表或元组,b 为整数时,表示将 ba 拼接在一起。
/
% 取模
** 幂,如 a**b 返回 ab 次方
// 整除,向下取整

比较(关系)运算符

所有比较运算符返回 1(或 True)表示真,返回 0(或 False)表示假。

Operators Explains
== 相等
!= 不等于 Python 3 已不支持 <>
> 大于
< 小于
>= 大于等于 - 返回x是否大于等于y。
<= 小于等于 - 返回x是否小于等于y。

赋值运算符

Operators Explains
= 赋值运算符
+= 加法赋值运算符
-= 减法赋值运算符
*= 乘法赋值运算符
/= 除法赋值运算符
%= 取模赋值运算符
**= 幂赋值运算符
//= 取整除赋值运算符
:= 海象运算符,可在表达式内部为变量赋值,然后直接进行判断。Python3.8 版本新增运算符

位运算符

Operators Explain
& 按位与运算符: 参与运算的两个值,如果两个相应位都为 1,则该位的结果为 1,否则为 0。
| 按位或运算符: 只要对应的两个二进位有一个为 1 时,结果位就为 1。
^ 按位异或运算符: 当两对应的二进位相异时,结果为 1。
~ 按位取反运算符: 对数据的每个二进制位取反,即把 1 变为 0,把 0 变为 1。~x 类似于 x-1
<< 左移动运算符: 运算数的各二进位全部左移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补 0。
>> 右移动运算符: 把 >> 左边的运算数的各二进位全部右移若干位,>> 右边的数指定移动的位数。

逻辑运算符

Operators Expressions Explains
and x and y 布尔 “与” 如果 xFalsex and y 返回 x 的值,否则返回 y 的计算值。
or x or y 布尔 “或” 如果 xTrue,它返回 x 的值,否则它返回 y 的计算值。
not not x 布尔 “非” 如果 xTrue,返回 False;如果 xFalse,它返回 True

成员运算符

Operators Explains
in 如果在指定的序列中找到值返回 True,否则返回 False
not in 如果在指定的序列中没有找到值返回 True,否则返回 False

身份运算符

Operators Explains
is 判断两个标识符是不是引用自一个对象。 x is y,类似 id(x) == id(y)。 如果引用的是同一个对象则返回 True,否则返回 False
is not 判断两个标识符是不是引用自不同对象。 x is not y,类似 id(a) != id(b)。 如果引用的不是同一个对象则返回结果 True,否则返回 False

is== 的区别:

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

运算符优先级

优先级按从高到底排列:

Operators Explains
** 指数
~ + - 按位翻转,一元加号和减号(最后两个的方法名为 +@ 和 -@)
* / % // 乘,除,求余数和取整除
+ - 加法减法
>> << 右移,左移运算符
& 按位与
^ | 按位异或,按位与
<= < > >= 比较运算符
== != 等于运算符
= %= /= //= -= += *= **= 赋值运算符
is is not 身份运算符
in not in 成员运算符
not and or 逻辑运算符

if条件控制

if 语句的形式:

if condition_1:
    statement_block_1
elif condition_2:
    statement_block_2
else:
    statement_block_3

if 语句的条件通常由关系运算符或返回值为布尔值的语句组成,使用逻辑运算符在一条 if 语句中连接多个条件。


条件运算符

Python 提供了条件运算符(三目运算符):

statement_1 if condition else statement
  • 如果 conditionTrue,执行 statement_1 并且返回 staement_1 的结果。

  • 如果 conditionFalse,执行 statement_2 并且返回 statement_2 的结果。


循环语句

Python 有 whilefor 两种循环语句。


while条件循环

while 循环的一般用法是,当指定条件成立时执行一次 while 后的代码块。

while condition:
    statements_block

while 语句后可接 else 语句。当指定条件不成立时,执行一次 while 后面的 else 语句。

while condition:
    statements_block_1
else:
    statements_block_2

for循环遍历

for 循环的一般用法是,遍历任何可迭代对象。每次循环,都会从指定的可迭代对象中选取一个赋给指定的变量。如此循环下去,直到遍历完指定的可迭代对象中所有元素。

for variables in sequence:
    statements_block

for 语句后可接 else 语句。当指定的可迭代对象为空时,执行一次 for 后面的 else 语句。

for variable in sequence:
    statements_block_1
else:
    statements_block_2

for 循环经常与 range() 函数一起使用。当需要遍历数字序列时,可以使用 range() 函数生成数列。

# 输出数字 5~12
for i in range(5, 13):
    print(i, end=' ')

print('')    # 输出一个换行符

遍历技巧

在字典中遍历时,关键字和对应的值可以使用 items() 方法同时解读出来:

>>> dict = {'one':1, 'two': 2, 'three': 3}
>>> for key, val in dict.items():
...     print(key, ' = ', val)
...
one  =  1
two  =  2
three  =  3

在序列中遍历时,索引位置和对应值可以使用 enumerate() 函数同时得到:

>>> for index, val in enumerate(['one', 'two', 'three']):
...     print(index, ': ', val)
...
0 :  one
1 :  two
2 :  three

同时遍历两个或更多的序列,可以使用 zip() 组合:

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

反向遍历序列,可以调用 reversed() 函数:

>>> for i in reversed(range(10)):
...     print(i, end = '')
...
9876543210

要按顺序遍历一个序列,使用 sorted() 函数返回一个已排序的序列,并不修改原值:

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear

列表推导式

列表推导式(又称列表解析式)的基本语法如下:

[val for val in sequence if_or_for_statement]
# if_or_for_statement是一个if判断语句或for循环
# 如果是if语句,那么只有条件位真时val才会在列表中

Example:

>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

列表推导式中的for语句和if语句是可嵌套的:

[x*y for x in range(1,5) if x > 2 for y in range(1,4) if y < 3]

上面语句的执行顺序是:

for x in range(1,5)
    if x > 2
        for y in range(1,4)
            if y < 3
                x*y

列表推导式除了list还可以被转化成其它对象:

>>> (x for x in range(10))
<generator object <genexpr> at 0x0000026F7A655BA0>
>>> tuple(x for x in range(10))
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

break and continue

  • break 语句 —— 跳出(终止)循环。不执行当前循环块中 break 语句以下的语句,并且将控制转移到当前循环语句的下一条语句。

  • continue 语句 —— 进入下一轮循环。不执行当前循环块中 continue 语句以下的语句,并且将控制转义到循环语句的开头。

breakcontinue 在循环语句中使用。一般在循环中的 if 条件语句中使用。即,达到某个条件便结束或进入下一次循环。


pass语句

pass 语句是空语句,即不执行任何操作。pass 是为了保持 Python 程序结构的完整性。

pass 一般作为占位语句,当操作都在条件语句或循环语句中完成时,在语句块中使用 pass 占位。如:

if condition_1:
    pass

while condition_2:
    pass

for variable in sequence:
    pass

迭代器与生成器

迭代器

迭代是一种访问集合元素的方式。迭代器是一个可以记住遍历位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

迭代器有两个基本的方法:iter()next()

字符串、列表或元组对象都可用于创建迭代器。

>>> list = [1, 2, 3, 4]
>>> it = iter(list)     # 创建迭代器对象
>>> print(next(it))    # 输出迭代器的下一个元素
1
>>> print(next(it))
2

迭代器对象可以使用常规 for 语句进行遍历:

list = [1, 2, 3, 4]
it = iter(list)    # 创建迭代器对象
for x in it:
    print(x, end = ' ')

使用 next() 迭代:

import sys         # 引入 sys 模块
 
list=[1, 2, 3, 4]
it = iter(list)    # 创建迭代器对象
 
while True:
    try:
        print (next(it))
    except StopIteration:
        sys.exit()

创建迭代器

把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()__next__()

  • __iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__()方法并通过 StopIteration 异常标识迭代的完成。

  • __next__() 方法(Python 2 里是 next())会返回下一个迭代器对象。

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    x = self.a
    self.a += 1
    return x
 
myclass = MyNumbers()
myiter = iter(myclass)

i = 0
while i < 10:
    print(next(myiter), end = ' ')
    i += 1

# 输出:1 2 3 4 5 6 7 8 9 10

迭代结束标志

StopIteration 异常用于标识迭代的完成,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

# 迭代 10 次后停止
class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration
 
myclass = MyNumbers()
myiter = iter(myclass)
 
for x in myiter:
  print(x, end = ' ')

# 输出:1 2 3 4 5 6 7 8 9 10

生成器

在 Python 中,使用了 yield 的函数被称为生成器(generator),生成器是一个返回迭代器的函数,只能用于迭代操作。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

import sys
 
def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 1, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1

f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

# 输出:1 1 2 3 5 8 13 21 34 55 89

函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。

函数的定义形式:

def function_name(arguments):
    """explain"""
    statements_block
    return [expression]
# end function_name
  • explain 是使用文档字符串存放的函数说明。

  • return 代表一个函数的结束,并且返回一个值给调用方,这个值放在 return 后面,可以是具体的值也可以是一条有确切值的表达式。不带值或表达式的 return 相当于返回 None。当函数不需要返回值时,可以省略 return

调用一个函数时需要使用该函数名并根据其定义指定参数:

def function_name(arguments):
    """explain"""
    statements_block
    return [expression]
# end function_name

function_name(arguments)

参数

  • 不可变类型的参数传递:类似 C++ 的值传递。传递的只是参数的值,没有影响参数对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。即,修改参数之前函数内部参数的地址和函数外部参数的地址相同,一旦修改了变量,参数的地址将会发生变化。如 Number、String、Tuple。

  • 可变类型的参数传递:类似 C++ 的引用传递。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响。无论是否对参数进行修改,参数地址都不会发生改变。如 List、Dictionary、Set。

参数类型:

  • 必须参数:须以正确的顺序传入函数。调用时,必须参数的数量必须和声明时一样。

  • 关键字参数:函数调用时使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

    def func(a, b):
        print('a = ', a, 'b = ', b)
        return
    
    func(b = 2, a = 1)
    
  • 默认参数:调用函数时,如果没有传递参数,则会使用默认参数。默认参数在定义函数时定义。

    def func(a, b = 1)
      print('a = ', a, 'b = ', b)
        return
    
    func(2)
    
  • 不定长参数

    • 加了星号 * 的参数会以元组的形式导入,存放所有未命名的变量参数。

      def func(atg1, *vartuple):
          print(atg1, vartuple)
          return
      
      func(1, 2, 3)
      # 输出:1 (2, 3)
      
    • 加了两个星号 ** 的参数会以字典的形式导入。

      def func(atg1, **vartuple):
          print(atg1, vartuple)
          return
      
      func(1, a = 2, b = 3)
      # 输出:1 {'a': 2, 'b': 3}
      
    • 如果参数单独出现星号 * 后的参数必须用关键字传入。

      >>> def f(a,b,*,c):
      ...     return a+b+c
      ... 
      >>> f(1,2,3)   # 报错
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      TypeError: f() takes 2 positional arguments but 3 were given
      >>> f(1,2,c=3) # 正常
      6
      
  • 强制位置参数:Python3.8 新增了一个函数形参语法 / 用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式。

    def f(a, b, /, c):
        print(a, b, c)
        return
    
    # 形参 a 和 b 必须使用指定位置参数,c 可以是位置形参或关键字形参
    f(1, 2, c = 3)
    

lambda匿名函数

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

  • lambda 的主体是一个表达式,而不是一个代码块(比 def 简单很多)。仅仅能在 lambda 表达式中封装有限的逻辑进去。

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

  • 虽然 lambda 函数看起来只能写一行,却不等同于 C 或 C++ 的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

lambda 函数的语法:

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

用法:

>>> sum = lambda arg1, arg2: arg1 + arg2
>>> sum(1, 2)
3

模块

模块是一个包含函数定义和变量的文件,其后缀名是 .py

  • 模块可以被别的程序引入,以使用该模块中的函数等功能。

  • Python 标准库也是一个个内置的模块。

    这些模块会根据不同的操作系统进行不同的配置,不同的操作系统可能会有一些不同的模块。

  • 每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用,可以使用 dir() 查看。

    在导入模块后,可以通过 modname.itemname 这样的表示法来访问模块内的函数。

  • 模块可以被导入其他模块。被导入的模块的名称将被放入当前操作的模块的符号表中。

  • 模块除了方法定义,还可以包括可执行的代码。

    这些代码一般用来初始化这个模块,只有在第一次被导入时才会被执行。


包是一种管理 Python 模块命名空间的形式,采用 “点模块名称”。如一个模块的名称是 A.B, 那么他表示一个包 A 中的子模块 B。优点是不用担心不同模块之间的名称冲突。

包结构实例:

sound/                          顶层包
      __init__.py               初始化 sound 包
      formats/                  文件格式转换子包
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  声音效果子包
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  filters 子包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

包还提供一个额外的属性 __path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的 __init__.py,得在其他 __init__.py 被执行前定义。可以修改这个变量,用来影响包含在包里面的模块和子包。这个功能并不常用,一般用来扩展包里面的模块。


import语句

import module1[, module2[,... moduleN]

当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。import 命令一般放在脚本顶端,如:

#!/usr/bin/python3
# Filename: support.py
 
def print_hello():
    print ('Hello World!')
    return
#!/usr/bin/python3
# Filename: test.py
 
# 导入模块
import hello
 
# 使用 . 运算符调用模块中的函数
hello.print_hello()

对于同一个模块,无论执行了多少次 import 语句,一个模块只会被导入一次。

搜索路径是一个解释器会先进行搜索的所有目录的列表,由一系列目录名组成的,Python 解释器就依次从这些目录中去寻找所引入的模块(很像环境变量)。搜索路径是在 Python 编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在 sys 模块中的 path 变量。

以 Windows 系统为例(在 VS2017 中安装的 Python3):

>>> import sys
>>> print(sys.path)
['', 'C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Python36_64\\python36.zip', 'C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Python36_64\\DLLs', 'C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Python36_64\\lib', 'C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Python36_64', 'C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Python36_64\\lib\\site-packages']

sys.path 输出是一个列表,其中第一项是空串,代表当前目录(若是从一个脚本中打印出来的话,可以更清楚地看出是哪个目录),即我们执行 Python 解释器的目录(对于脚本的话就是运行的脚本所在的目录)。

如果要引入一些不在搜索路径中的模块,可以在脚本中修改 sys.path

如果要经常使用一个模块中的函数,可以赋给它一个本地名称:

import module

func = module.func
func(arg)

from … import语句

使用 from ... import 语句可以从模块中导入一个指定的部分到当前命名空间中。这种导入的方法不会把被导入的模块的名称放在当前的字符表中。

from modname import item1[, item2[, ... itemN]]

这样只会把 modname 中的指定的部分 item1[, item2[, ... itemN]] 导入到当前命名空间中,并且可以直接使用它们的名称来调用它们(无需使用 . 运算符)。item1[, item2[, ... itemN]] 可以是子模块、函数、类或变量等。

使用 from package import item 这种形式的时候,import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。

把一个模块的所有内容全都导入到当前的命名空间(不推荐使用),但是那些由单一下划线 _ 开头的名字不在此例:

from modname import *

Python 会进入文件系统,找到这个包里面所有的子模块,然后一个一个的把它们都导入进来。但这个方法在 Windows 平台上工作的就不是非常好,因为 Windows 是一个不区分大小写的系统。

为了解决这个问题,需要提供一个精确包的索引。导入语句遵循如下规则:

如果包定义文件 __init__.py 存在一个叫做 __all__ 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。

以下实例在 file:sounds/effects/_init_.py 中包含如下代码:

__all__ = ["echo", "surround", "reverse"]

from … import … as语句

import ... as 可以将导入的模块、函数或者变量等重命名:

import name as new_name

该语句也可以接在 from 语句后面:

from modname import name as new_name

\_\_name\_\_ 属性

一个模块被另一个程序第一次引入时,其主程序将运行。可以用 __name__ 属性来使该程序块仅在该模块自身运行时执行(被其它程序或模块引入时不执行)。

每个模块都有一个 __name__ 属性,当其值是 '__main__' 时,表明该模块自身在运行,否则是被引入。

#!/usr/bin/python3
# Filename: using_name.py

if __name__ == '__main__':
   print('程序自身在运行')
else:
   print('我来自另一模块')
$ python using_name.py
程序自身在运行
$ python
>>> import using_name
我来自另一模块

dir() 函数

内置的函数 dir() 可以找到指定模块内定义的所有名称,以一个字符串列表的形式返回。

>>> import math
>>> dir(math)
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

如果没有给定参数,dir() 函数会罗列出当前定义的所有名称。

>>> a = [1, 2, 3]
>>> b = 4
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b']

输入输出

标准输出

在 Python 中使用 print() 来将其参数输出到标准输出上。默认的标准输出是屏幕。

可以使用 str.format() 函数等方式⤴ 来格式化输出值。

可以使用 repr()str() 函数来将输出的值转成字符串。

  • str():函数返回一个用户易读的表达形式。

  • repr():产生一个解释器易读的表达形式。


标准输入

Python 提供了 input() 内置函数从标准输入读入一行文本,默认的标准输入是键盘。

Python3.x 中 input() 函数接受一个标准输入数据,返回为 string 类型。Python3.x 没有 raw_input()

input() 函数的语法:

input([prompt])
# prompt —— 输出到标准输出的提示信息

用法示例:

>>> a = input('输入一个整数:')
输入一个整数123
>>> type(a)
<class 'str'>      # input() 返回的是 string
>>> a = int(a)      # 使用 int() 类型转换
>>> print(type(a), a)
<class 'int'> 123    # 此时 a 才是 int

input() 接收多个值:

#输入三角形的三边长
a, b, c = (input("请输入三角形三边的长:").split())
a = int(a)
b = int(b)
c = int(c)

#计算三角形的半周长p
p = (a + b + c) / 2

#计算三角形的面积s
s = (p * (p - a) * (p - b) * (p - c))**0.5

#输出三角形的面积
print("三角形面积为:", format(s, '.2f'))

读写文件

open() 将会返回一个 file 对象,基本语法格式如下:

open(filename[, mode='r'])
# filename —— 包含了要访问的文件名称的字符串值
# mode —— 打开文件的模式的字符串值,默认文件访问模式为只读(r)

打开文件的模式:

模式 描述
t 文本模式 (默认)。
x 写模式,新建一个文件,如果该文件已存在则会报错。
b 二进制模式。
+ 打开一个文件进行更新(可读可写)。
U 通用换行模式(不推荐)。
r 以只读方式打开文件(默认模式)。文件的指针将会放在文件的开头。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w 打开一个文件只用于写入。 如果该文件已存在,则清空原文件内容,并从开头开始编辑。 如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。 打开方式与 w 相同(会覆盖原有的文件)。
w+ 打开一个文件用于读写。 打开方式与 w 相同。
wb+ 以二进制格式打开一个文件用于读写。 打开方式与 w 相同。
a 打开一个文件用于追加。 如果该文件已存在,文件指针将会放在文件的结尾。 即,新的内容将会被写入到已有内容之后。 如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。 打开方式与 a 相同。
a+ 打开一个文件用于读写。 打开方式与 a 相同。
ab+ 以二进制格式打开一个文件用于追加。 打开方式与 a 相同。
模式 r r+ w w+ a a+
+ + + +
+ + + + +
创建 + + + +
覆盖 + +
指针在开始 + + + +
指针在结尾 + +

open() 函数的完整语法格式:

open(file[, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None])
# file —— 必需,文件路径(相对或者绝对路径)。
# mode —— 可选,文件打开模式
# buffering —— 设置缓冲
# encoding —— 一般使用 utf8
# errors —— 报错级别
# newline —— 区分换行符
# closefd —— 传入的 file 参数类型
# opener —— 自定义打开文件方式

打开文件后,要进行的就是写入和读取操作,这些操作都是对 file 对象进行操作。

file 对象方法:

方法 说明
file.close() 关闭文件。关闭后文件不能再进行读写操作。 在执行完文件的读写操作后,有必要执行该方法。
file.flush() 刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
file.fileno() 返回一个整型的文件描述符(file descriptor FD 整型),可以用在如 os 模块的 read 方法等一些底层操作上。
file.isatty() 如果文件连接到一个终端设备返回 True,否则返回 False
file.next() 返回文件下一行。
file.read([size=-1]) 从文件读取指定的字节数,默认为 -1,为负数时表示读取所有。
file.readline([size=-1]) 读取整行(size 默认为 -1,为负数表示读取整行),包括 '\n' 字符。 如果指定了 size 则读取该行的前 size 字符。 第一次调用时读取第一行,第二次调用时读取第二行,以此类推。
file.readlines([sizeint]) 读取所有行并返回列表,若给定 sizeint > 0,则是设置一次读多少字节,这是为了减轻读取压力。
file.seek(offset[, whence=0]) 设置文件当前位置。 offset —— 开始的偏移量。 whence —— 给 offset 参数一个定义,表示要从哪个位置开始偏移;0 代表从文件开头开始算起,1代表从当前位置开始算起,2 代表从文件末尾算起。默认为 0
file.tell() 返回文件当前位置。
file.truncate([size=file.tell()]) 截取文件,截取的字节通过 size 指定,默认为当前文件位置。
file.write(str) 将字符串写入文件,返回的是写入的字符长度。 如果文件打开模式带 b,那写入文件内容时,str 要用 encode() 方法转为 bytes 形式,否则报错:TypeError: a bytes-like object is required, not ‘str’。
file.writelines(sequence) 向文件写入一个序列字符串列表(这一序列字符串可以是由迭代对象产生的,如一个字符串列表),如果需要换行则要自己加入每行的换行符。

file.write() 用法:

f = open('test.txt', 'w+')
f.write('test1')
f.seek(0)
print('从文件中读取到的字符串:', f.read())
f.close()
# 输出:
# 从文件中读取到的字符串: test1

f = open('test.txt', 'wb+')
f.write('test1'.encode())
f.seek(0)
print('从二进制文件中读取到的内容:', f.read())
f.seek(0)
print('将其转化为字符串输出:', f.read().decode())
f.close()
# 输出:
# 从二进制文件中读取到的内容: b''
# 将其转化为字符串输出: test1

os模块

os 模块提供了非常丰富的方法用来处理文件和目录。以下是一些常用的方法:


os.access(path, mode)

检验权限模式(使用当前的 UID/GID 尝试访问路径)。

  • path —— 要用来检测是否有访问权限的路径。

  • mode —— 参数取值如下:

    • os.F_OK —— 测试 path 是否存在;

    • os.R_OK —— 测试 path 是否可读;

    • os.W_OK —— 测试 path 是否可写;

    • os.X_OK —— 测试 path 是否可执行。

    • 在相应模式下,如果存在/可读/写/执行返回 True ,否则返回 False


方法 描述
os.chdir(path) 改变当前工作目录到 path 指定的路径。如果允许访问返回 True,否则返回 False
os.chflags(path, flags) 设置路径 path 的标记为数字标记。多个标记可以使用 OR 来组合起来。 flags —— 参数取值如下: stat.UF_NODUMP —— 非转储文件; stat.UF_IMMUTABLE —— 文件是只读的; stat.UF_APPEND —— 文件只能追加内容; stat.UF_NOUNLINK —— 文件不可删除; stat.UF_OPAQUE —— 目录不透明,需要通过联合堆栈查看; 以下参数值是超级用户可设置的: stat.SF_ARCHIVED —— 可存档文件; stat.SF_IMMUTABLE —— 文件是只读的; stat.SF_APPEND —— 文件只能追加内容; stat.SF_NOUNLINK —— 文件不可删除; stat.SF_SNAPSHOT —— 快照文件。
os.chmod(path, mode) 更改权限
os.chown(path, uid, gid) 更改文件所有者
os.chroot(path) 改变当前进程的根目录
os.close(fd) 关闭文件描述符 fd
os.closerange(fd_low, fd_high) 关闭所有文件描述符,从 fd_low (包含) 到 fd_high (不包含), 错误会忽略
os.dup(fd) 复制文件描述符 fd
os.dup2(fd, fd2) 将一个文件描述符 fd 复制到另一个 fd2
os.fchdir(fd) 通过文件描述符改变当前工作目录
os.fchmod(fd, mode) 改变一个文件的访问权限,该文件由参数fd指定,参数mode是Unix下的文件访问权限。
os.fchown(fd, uid, gid) 修改一个文件的所有权,这个函数修改一个文件的用户ID和用户组ID,该文件由文件描述符fd指定。
os.fdatasync(fd) 强制将文件写入磁盘,该文件由文件描述符fd指定,但是不强制更新文件的状态信息。
os.fdopen(fd[, mode[, bufsize]]) 通过文件描述符 fd 创建一个文件对象,并返回这个文件对象
os.fpathconf(fd, name) 返回一个打开的文件的系统配置信息。name为检索的系统配置的值,它也许是一个定义系统值的字符串,这些名字在很多标准中指定(POSIX.1, Unix 95, Unix 98, 和其它)。
os.fstat(fd) 返回文件描述符fd的状态,像stat()。
os.fstatvfs(fd) 返回包含文件描述符fd的文件的文件系统的信息,Python 3.3 相等于 statvfs()。
os.fsync(fd) 强制将文件描述符为fd的文件写入硬盘。
os.ftruncate(fd, length) 裁剪文件描述符fd对应的文件, 所以它最大不能超过文件大小。
os.getcwd() 返回当前工作目录
os.getcwdb() 返回一个当前工作目录的Unicode对象
os.isatty(fd) 如果文件描述符fd是打开的,同时与tty(-like)设备相连,则返回true, 否则False。
os.lchflags(path, flags) 设置路径的标记为数字标记,类似 chflags(),但是没有软链接
os.lchmod(path, mode) 修改连接文件权限
os.lchown(path, uid, gid) 更改文件所有者,类似 chown,但是不追踪链接。
os.link(src, dst) 创建硬链接,名为参数 dst,指向参数 src
os.listdir(path) 返回path指定的文件夹包含的文件或文件夹的名字的列表。
os.lseek(fd, pos, how) 设置文件描述符 fd当前位置为pos, how方式修改: SEEK_SET 或者 0 设置从文件开始的计算的pos; SEEK_CUR或者 1 则从当前位置计算; os.SEEK_END或者2则从文件尾部开始. 在unix,Windows中有效
os.lstat(path) 像stat(),但是没有软链接
os.major(device) 从原始的设备号中提取设备major号码 (使用stat中的st_dev或者st_rdev field)。
os.makedev(major, minor) 以major和minor设备号组成一个原始设备号
os.makedirs(path[, mode]) 递归文件夹创建函数。像mkdir(), 但创建的所有intermediate-level文件夹需要包含子文件夹。
os.minor(device) 从原始的设备号中提取设备minor号码 (使用stat中的st_dev或者st_rdev field )。
os.mkdir(path[, mode]) 以数字mode的mode创建一个名为path的文件夹.默认的 mode 是 0777 (八进制)。
os.mkfifo(path[, mode]) 创建命名管道,mode 为数字,默认为 0666 (八进制)
os.mknod(filename[, mode=0600, device]) 创建一个名为filename文件系统节点(文件,设备特别文件或者命名pipe)。
os.open(file, flags[, mode]) 打开一个文件,并且设置需要的打开选项,mode参数是可选的
os.openpty() 打开一个新的伪终端对。返回 pty 和 tty的文件描述符。
os.pathconf(path, name) 返回相关文件的系统配置信息。
os.pipe() 创建一个管道. 返回一对文件描述符(r, w) 分别为读和写
os.popen(command[, mode[, bufsize]]) 从一个 command 打开一个管道
os.read(fd, n) 从文件描述符 fd 中读取最多 n 个字节,返回包含读取字节的字符串,文件描述符 fd对应文件已达到结尾, 返回一个空字符串。
os.readlink(path) 返回软链接所指向的文件
os.remove(path) 删除路径为path的文件。如果path 是一个文件夹,将抛出OSError; 查看下面的rmdir()删除一个 directory。
os.removedirs(path) 递归删除目录。
os.rename(src, dst) 重命名文件或目录,从 src 到 dst
os.renames(old, new) 递归地对目录进行更名,也可以对文件进行更名。
os.rmdir(path) 删除path指定的空目录,如果目录非空,则抛出一个OSError异常。
os.stat(path) 获取path指定的路径的信息,功能等同于C API中的stat()系统调用。
os.stat_float_times([newvalue]) 决定stat_result是否以float对象显示时间戳
os.statvfs(path) 获取指定路径的文件系统统计信息
os.symlink(src, dst) 创建一个软链接
os.tcgetpgrp(fd) 返回与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组
os.tcsetpgrp(fd, pg) 设置与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组为pg。
os.tempnam([dir[, prefix]]) Python3 中已删除。 返回唯一的路径名用于创建临时文件。
os.tmpfile() Python3 中已删除。 返回一个打开的模式为(w+b)的文件对象 .这文件对象没有文件夹入口,没有文件描述符,将会自动删除。
os.tmpnam() Python3 中已删除。 为创建一个临时文件返回一个唯一的路径
os.ttyname(fd) 返回一个字符串,它表示与文件描述符fd 关联的终端设备。如果fd 没有与终端设备关联,则引发一个异常。
os.unlink(path) 删除文件路径
os.utime(path, times) 返回指定的path文件的访问和修改的时间。
os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]]) 输出在文件夹中的文件名通过在树中游走,向上或者向下。
os.write(fd, str) 写入字符串到文件描述符 fd中. 返回实际写入的字符串长度
os.path 模块 获取文件的属性信息。
os.pardir() 获取当前目录的父目录,以字符串形式显示目录名。

错误和异常

Python 中的语法错误也可称之为解析错。即便 Python 程序的语法是正确的,在运行它的时候,也有可能发生错误。运行期检测到的错误被称为异常。

异常以不同的类型出现,这些类型都作为信息的一部分打印出来:

>>> 10 * (1/0)             # 0 不能作为除数,触发异常
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: division by zero
>>> 4 + spam*3             # spam 未定义,触发异常
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2               # int 不能与 str 相加,触发异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

错误信息的前面部分显示了异常发生的上下文,并以调用栈的形式显示具体信息。


异常处理

try/except

异常捕捉可以使用 try/except 语句:

# 执行一次 try 子句,并尝试捕捉异常
# 如果没有异常发生,忽略 except 子句
# 当捕捉到异常时,将执行对应的 except 子句
# 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中
# 一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常。但最多只有一个分支会被执行。
try:
    statement_1
except Error1:
    statement_2
except Error2:
    statement_3
......
except: # 省略异常的名称,将被当作通配符使用
    statement_n

# 2. 一次处理多个异常
try:
    statement_1
except (Error1, Error2, ...): # 这里是一个元素为“异常”的元组
    statement_2

Example:

while True:
    try:
        x = int(input("请输入一个数字: "))
        break
    except ValueError:
        print("您输入的不是数字,请再次尝试输入!")
import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise # 再次将触发的异常抛出

try/except … else

try/except 语句还有一个可选的 else 子句,如果使用这个子句,那么必须放在所有的 except 子句之后。else 子句将在 try 子句没有发生任何异常的时候执行。

try:
    statement_1
except Error1:
    statement_2
except Error2:
    statement_3
......
except:
    statement_n
else:
    statement_e

判断文件是否可以打开,如果打开文件时正常的没有发生异常则执行 else 部分的语句,读取文件内容:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到,而 except 又无法捕获的异常。异常处理并不仅仅处理那些直接发生在 try 子句中的异常,而且还能处理子句中调用的函数(甚至间接调用的函数)里抛出的异常。


try-finally

try-finally 语句无论是否发生异常都将执行 finally 子句的代码。

try:
    statement_1
except Error1:
    statement_2
except Error2:
    statement_3
......
except:
    statement_n
else:
    statement_e
finally:
    statement_f

Example:

try:
    runoob()
except AssertionError as error:
    print(error)
else:
    try:
        with open('file.log') as file:
            read_data = file.read()
    except FileNotFoundError as fnf_error:
        print(fnf_error)
finally:
    print('这句话,无论异常是否发生都会执行。')

finally 子句通常用作定义任何情况下都会执行的清理行为。

如果一个异常在 try 子句里(或者在 exceptelse 子句里)被抛出,而又没有任何的 except 把它截住,那么这个异常会在 finally 子句执行后被抛出。


抛出异常

Python 使用 raise 语句抛出一个指定的异常。raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。raise 语法格式如下:

raise [Exception [, args [, traceback]]]

Example:

x = 10
if x > 5:
    raise Exception('x 不能大于 5。x 的值为: {}'.format(x))

执行以上代码会触发异常:

Traceback (most recent call last):
  File "test.py", line 3, in <module>
    raise Exception('x 不能大于 5。x 的值为: {}'.format(x))
Exception: x 不能大于 5。x 的值为: 10

用户自定义异常

异常类都是直接或间接地继承自 Exception 类。

>>> class MyError(Exception):
...     def __init__(self, value):
...         self.value = value
...     def __str__(self):
...         return repr(self.value)
...
>>> try:
...     raise MyError(2*2)
... except MyError as e:
...     print('My exception occurred, value:', e.value)
...
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'

预定义清理行为

with 语句可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法:

with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

assert 断言

Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。

断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况。

语法格式如下:

assert expression
# expression 值为 True 时,正常执行;
# 为 False 时,触发异常

它等价于:

if not expression:
    raise AssertionError

assert 后面也可以紧跟参数:

assert expression [, arguments]

等价于:

if not expression:
    raise AssertionError(arguments)

Example:

>>> assert True     # 条件为 true 正常执行
>>> assert False    # 条件为 false 触发异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
>>> assert 1==1    # 条件为 true 正常执行
>>> assert 1==2    # 条件为 false 触发异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

>>> assert 1==2, '1 不等于 2'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: 1 不等于 2
# 判断当前系统是否为 Linux,如果不满足条件则直接触发异常,不必执行接下来的代码
import sys
assert ('linux' in sys.platform), "该代码只能在 Linux 下执行"

# ... 接下来要执行的代码

面向对象

  • (Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

  • 方法:类中定义的函数。

  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

  • 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。

  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

  • 局部变量:定义在方法中的变量,只作用于当前实例的类。

  • 实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。

  • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。继承是模拟 “是一个(is-a)” 关系。

  • 实例化:创建一个类的实例,类的具体对象。

  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。


类定义和类对象

创建类

创建类的语法格式如下:

class ClassName():
   
    <statement-1>
    .
    .
    .
    <statement-N>

创建实例对象

类对象支持两种操作:属性引用和实例化。

在 Python 中,类的实例化类似函数调用方式,其语法格式如下:

obj = ClassName([arg1, arg2, ...])

访问对象属性

类的属性是指在类中定义的变量和方法等。

属性引用使用 obj.name 语法。类对象创建后,类命名空间中所有的命名(如变量、方法等)都是有效属性名。

Example:

class MyClass():
    var1 = 1
    var2 = 2
    
    def method(self):
        print("This is my class.")
        
obj = MyClass()
print(obj.var1, obj.var2)
print()
obj.func()

# 输出:
# 1 2
# This is my class.

\_\_init\_\_() 和self

在类定义中有一个特殊的方法,构造方法 __init__()。如果类定义了 __init__() 方法,那么在类实例化时,会自动调用该方法。

在类定义中,self 代表类的实例,而非类。self 必须存在,且位于其他形参前面。类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称,按照惯例它的名称是 self

self 的名字并不是规定死的,也可以使用 this,但是最好还是按照约定使用 self

在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数。

Example:

class Complex():
    """复数类"""
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart
        
    def print_complex(self):
        print('{:f}{:+f}i'.format(self.r, self.i))

x = Complex(3.0, -4.5) # 实例化
print(x.r, x.i)   # 输出:3.0 -4.5
x.print_complex() # 输出:3.000000-4.500000i

在 Python 调用 __init__() 来创建实例时,将自动传入实参 self。每个与类相关联的方法调用都自动传递实参 self

在类的内部使用属性和方法,需要使用 self.attrsself.method()


修改属性值

通常有两种方法可以修改属性值,一种是直接使用 obj.name 语法修改,另一种是通过方法对属性值进行修改。

Example:

class Car(): 
 
    def __init__(self, make, model, year): 
        """初始化描述汽车的属性""" 
        self.make = make 
        self.model = model 
        self.year = year
        self.odometer_reading = 0 

    def get_descriptive_name(self): 
        """返回整洁的描述性信息""" 
        long_name = str(self.year) + ' ' + self.make 
          + ' ' + self.model 
        return long_name.title()

    def read_odometer(self): 
        """打印一条指出汽车里程的消息""" 
        print("This car has " + str(self.odometer_reading) 
              + " miles on it.") 
        
    def update_odometer(self, mileage): 
        """ 
        将里程表读数设置为指定的值
        禁止将里程表读数往回调
        """
        if mileage >= self.odometer_reading: 
       self.odometer_reading = mileage 
     else: 
      print("You can't roll back an odometer!")
            
    def increment_odometer(self, miles): 
     """将里程表读数增加指定的量""" 
     self.odometer_reading += miles

my_new_car = Car('audi', 'a4', 2016) 
# 通过方法获取属性
print(my_new_car.get_descriptive_name()) 
my_new_car.read_odometer()

# 输出:
# 2016 Audi A4 
# This car has 0 miles on it.

# 通过方法对属性值进行修改
my_used_car = Car('subaru', 'outback', 2013) 
print(my_used_car.get_descriptive_name())

my_used_car.update_odometer(23500) 
my_used_car.read_odometer()

my_used_car.increment_odometer(100) 
my_used_car.read_odometer()

# 输出:
# 2013 Subaru Outback 
# This car has 23500 miles on it. 
# This car has 23600 miles on it.

继承

子类(派生类 DerivedClassName)会继承父类(基类 BaseClassName)的属性和方法。派生类的定义如下所示:

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>

BaseClassName(实例中的基类名)必须与派生类定义在一个作用域内。

如果基类定义在另一个模块中,需要使用 . 运算符:

class DerivedClassName(modname.BaseClassName):

Example:

# 类定义
class people:
    # 定义基本属性
    name = ''
    age = 0
    # 定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    # 定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" %(self.name,self.age))
 
# 单继承示例
class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        # 调用父类的构函
        people.__init__(self,n,a,w)
        self.grade = g
    # 覆写父类的方法
    def speak(self):
        print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
 
 
 
s = student('ken',10,60,3)
s.speak()

多继承

Python 有限地支持多继承,多继承的类定义如下:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

若是父类中有相同的方法名,而在子类使用时未指定,Python 会根据圆括号中父类的顺序从左至右搜索。即方法在子类中未找到时,从左到右查找父类中是否包含方法。

Example:

# 接上一个例子

# 另一个类,多重继承之前的准备
class speaker():
    topic = ''
    name = ''
    def __init__(self,n,t):
        self.name = n
        self.topic = t
    def speak(self):
        print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))
 
# 多重继承
class sample(speaker,student):
    a =''
    def __init__(self,n,a,w,g,t):
        student.__init__(self,n,a,w,g)
        speaker.__init__(self,n,t)
 
test = sample("Tim",25,80,4,"Python")
test.speak()   # 方法名同,默认调用的是在括号中排前地父类的方法

方法重写

可以在子类中重写其父类的方法:

class Parent:        # 定义父类
   def myMethod(self):
      print ('调用父类方法')
 
class Child(Parent): # 定义子类
   def myMethod(self):
      print ('调用子类方法')
 
c = Child()          # 子类实例
c.myMethod()         # 子类调用重写方法
super(Child,c).myMethod() # 用子类对象调用父类已被覆盖的方法

# 输出:
# 调用子类方法
# 调用父类方法

子类继承父类构造函数说明

如果在子类中需要父类的构造方法就需要显式地调用父类的构造方法,或者不重写父类的构造方法。

子类不重写 __init__,实例化子类时,会自动调用父类定义的 __init__

Example:

class Father(object):
    def __init__(self, name):
        self.name = name
        print ("name: %s" % (self.name))
    def getName(self):
        return 'Father ' + self.name
 
# 重写了 getName 但并没有重写 __init__
class Son(Father):
    def getName(self):
        return 'Son ' + self.name
 
if __name__ == '__main__':
    son = Son('runoob')
    print(son.getName())
    
# 输出:
# name: runoob
# Son runoob

如果重写了__init__ 时,实例化子类,就不会调用父类已经定义的 __init__

Example:

class Father(object):
    def __init__(self, name):
        self.name=name
        print ( "name: %s" %( self.name) )
    def getName(self):
        return 'Father ' + self.name
 
# 重写了 __init__ 和 getName
class Son(Father):
    def __init__(self, name):
        print ( "hi" )
        self.name =  name
    def getName(self):
        return 'Son '+self.name
 
if __name__=='__main__':
    son=Son('runoob')
    print (son.getName())
    
# 输出:
# hi
# Son runoob

如果重写了 __init__ 时,要继承父类的构造方法,可以在 __init__ 的定义中使用 super 关键字:

super(子类, self).__init__(参数1, 参数2, ....)

另一种写法:

父类名称.__init__(self, 参数1, 参数2, ...)

Example:

class Father(object):
    def __init__(self, name):
        self.name = name
        print("name: %s" % (self.name))
    def getName(self):
        return 'Father ' + self.name
 
class Son(Father):
    def __init__(self, name):
        super(Son, self).__init__(name)
        print("hi")
        self.name =  name
    def getName(self):
        return 'Son ' + self.name
 
if __name__ == '__main__':
    son=Son('runoob')
    print (son.getName())
    
# 输出:
# name: runoob
# hi
# Son runoob

类的私有属性与私有方法

私有属性不能在类的外部被使用或直接访问。使用两个下划线开头,声明该属性为私有:

__private_attrs

在类内部的方法中使用私有属性:

self.__private_attrs

Example:

class JustCounter:
    __secretCount = 0  # 私有变量
    publicCount = 0    # 公开变量
 
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print (self.__secretCount)
 
counter = JustCounter()
counter.count()
counter.count()
print (counter.publicCount)
print (counter.__secretCount)  # 报错,实例不能访问私有变量

输出结果:

1
2
2
Traceback (most recent call last):
  File "test.py", line 16, in <module>
    print (counter.__secretCount)  # 报错,实例不能访问私有变量
AttributeError: 'JustCounter' object has no attribute '__secretCount'

私有方法只能在类的内部调用 ,不能在类的外部调用。使用两个下划线开头,声明该方法为私有方法:

def __private_method(self[, ...]):

在类内部的方法中使用私有方法:

self.__private_methods([...])

Example:

class Site:
    def __init__(self, name, url):
        self.name = name   # public
        self.__url = url   # private
 
    def who(self):
        print('name  : ', self.name)
        print('url : ', self.__url)
 
    def __foo(self):          # 私有方法
        print('这是私有方法')
 
    def foo(self):            # 公共方法
        print('这是公共方法')
        self.__foo()
 
x = Site('菜鸟教程', 'www.runoob.com')
x.who()        # 正常输出
x.foo()        # 正常输出
x.__foo()      # 报错

输出结果:

name  :  菜鸟教程
url :  www.runoob.com
这是公共方法
这是私有方法
Traceback (most recent call last):
  File "c:/Users/27120/Documents/MyDocuments/Code/test.py", line 20, in <module>      
    x.__foo()      # 报错,外部不能调用私有方法
AttributeError: 'Site' object has no attribute '__foo'

类的专有方法

方法 描述
__init__ 构造函数,在生成对象时调用
__del__ 析构函数,释放对象时使用
__repr__ 打印,转换
__setitem__ 按照索引赋值
__getitem__ 按照索引获取值
__len__ 获得长度
__cmp__ 比较运算
__call__ 函数调用
__add__ 加运算
__sub__ 减运算
__mul__ 乘运算
__truediv__ 除运算
__mod__ 求余运算
__pow__ 乘方

运算符重载

可以对类的专有方法进行重载:

class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b
 
   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)
   
   def __add__(self, other):
      return Vector(self.a + other.a, self.b + other.b)
 
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)

# 输出:
# Vector(7,8)

命名空间

命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。

命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。

一般有三种命名空间:

  1. 内置名称(built-in names):Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。

  2. 全局名称(global names):模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。

  3. 局部名称(local names):函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量(类中定义的也是)。

  • 命名空间查找顺序:局部的命名空间 $\rightarrow$ 全局命名空间 $\rightarrow$ 内置命名空间。

  • 如果找不到变量,将放弃查找并引发一个 NameError 异常:

    NameError: name 'runoob' is not defined
    
  • 命名空间的生命周期:命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。

因此,无法从外部命名空间访问内部命名空间的对象。


作用域

作用域就是一个 Python 程序可以直接访问命名空间的正文区域。变量的访问权限取决于这个变量是在哪里赋值的。

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用域一共有 4 种,分别是:

  1. L(Local):最内层,包含局部变量。如一个函数/方法内部。

  2. E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。 如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 non-local。

  3. G(Global):当前脚本的最外层。如当前模块的全局变量。

  4. B(Built-in): 包含了内建的变量/关键字等,最后被搜索。

作用域查找顺序: L $\rightarrow$ E $\rightarrow$ G $\rightarrow$ B。

g_count = 0  # 全局作用域
def outer():
    o_count = 1  # 闭包函数外的函数中
    def inner():
        i_count = 2  # 局部作用域

内置作用域是通过一个名为 builtin 的标准模块来实现的,但是这个变量名自身并没有放入内置作用域内,所以必须导入这个文件才能够使用它。在Python3.0中,可以使用以下的代码来查看到底预定义了哪些变量:

>>> import builtins
>>> dir(builtins)

Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/elsetry/exceptfor/while等)是不会引入新的作用域的:


全局变量和局部变量

定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。


global和nonlocal

globalnonlocal 的作用是让内部作用域修改外部作用域变量。

global —— 修改全局变量:

num = 1
def fun1():
    global num  # 需要使用 global 关键字声明
    print(num) 
    num = 123
    print(num)
    
fun1()
print(num)

# 输出:
# 1
# 123
# 123

nonlocal —— 修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量:

def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 100
        print(num)
    inner()
    print(num)
    
outer()

# 输出:
# 100
# 100

不使用 globalnonlocal 修改外部作用域变量:

a = 10
def test():
    a = a + 1
    print(a)
test()

结果:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    test()
  File "test.py", line 5, in test
    a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

局部作用域引用错误,因为 test 函数中的 a 使用的是局部,未定义,无法修改。


for x in range(1,5)

if x > 2

for y in range(1,4)

if y < 3

x*y