Python基础手册P2 基本数据类型基本类型

Python 基本数据类型

。。。

今天没有想要写在开头的废话,,,好烦,要不破例直接开始吧。

变量与常量

变量

Python 是一门动态语言,这意味着 Python 变量本身的类型是不固定的。
在 Python 中使用变量前不用声明(不用写类似 int a; 的语句),在首次使用前为其赋初始值就行(直接用 a = 0)。

常量

实际上,在 Python 语法中并没有定义常量

但是 PEP 8 定义了常量的命名规范为大写字母和下划线组成。在实际应用中,这种“常量”首次赋值后,无法阻止其他代码对其进行修改或删除

要使用真正的常量,可以自己实现一个,例如:constants-in-python

空值

Python 中使用 None 来代表空值。
None 在交互式命令行中不会显示,但可以用 print() 打印出来:

1
2
3
4
5
>>> None
>>> a = None
>>> a
>>> print(a)
None

布尔值

Python 中有布尔值 TrueFalse

真值问题

代表 ‘假’ 的值有:FalseNone0''[]{},…;
其余值为真。

布尔值的相关的运算

逻辑运算符 相当于C中的
and &&
or `
not !

⚠️【注意 and 的优先级高于 or

比较运算符
<, >, ==, !=

使用比较运算符得到的结果是布尔值(TrueFalse)。

使用逻辑运算符得到的结果未必是布尔值

具体的运算情况可以参考下面这段程序生成的表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# **注意,是第一列的值 and|or 第一行的值!**
li = ['and', 'or']
ls = [True, False, None, 0, 1, 2, '"abc"']

for opt in li:
# head
print('<%s>' % opt, end='\t')
for j in ls:
print(j, end='\t')
print('\n')

for i in ls:
# col
print(i, end='\t')
for j in ls:
# row
print(
eval('%s %s %s' % (i, opt, j)),
end='\t'
)
print('\n')
# end
print('\n-------\n')

从中可以看到,
and 的规则是:

前后两者 皆为真 ,返回 者;
前后两者 有一假 ,返回 者;
前后两者 皆为假 ,返回 者;

or 的规则是:

前后两者 皆为真 ,返回 者;
前后两者 有一真 ,返回 者;
前后两者 皆为假 ,返回 者;

此外,
not 的规则是:

若对象为 ,返回 False
若对象为 ,返回 True

数字

int, float

Python 中内置有 intfloat 两类数字。

数字 含义 表示范围 精度
int 整数 大小没有限制 始终是准确的
float (基于二进制的)浮点数 有一定大小限制,超出后表示为inf 和C一样,不准确

复数

Python 还内置了对 复数 的支持,使用后缀 jJ 表示虚数部分(例如,3+5j)。

1
2
3
4
5
>>> a = 3 + 1j
>>> b = 3 - 1j
>>> a * b
(10+0j)
>>>

关于这部分详见官方文档

其他数字类型

在标准库中,python 还有对 精确小数 Decimal(基于十进制的浮点数)分数 Fraction 等其他数字类型的支持。

Decimal (小数)

在 Python 的标准库中,decimal 库提供了 基于十进制的浮点数 Decimal 类型,这种数字类型修复了 float 的不准确问题,可以用 Decimal 实现更加精准的数学计算(但也不是绝对的准确,仍存在误差)。

1
2
3
4
5
6
7
8
>>> from decimal import *
>>> 0.1 + 0.1 + 0.1 - 0.3 # float
5.551115123125783e-17 # 这个结果是不精确的
>>> Decimal(0.1) + Decimal(0.1) + Decimal(0.1) - Decimal(0.3) # decimal
Decimal('2.775557561565156540423631668E-17') # 较为精确
>>> getcontext().prec = 12 # 限制 Decimal 的小数位数
>>> Decimal(0.1) + Decimal(0.1) + Decimal(0.1) - Decimal(0.3)
Decimal('1.11022302463E-17')

正如上例,要使用 Decimal 类型,

  • 首先要 import decimal

  • 然后用 decimal.Decimal(Num)来获取一个 Decimal 实例,这里的 Num 可以是如下几种:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    >>> Decimal('3.14')     # 内容是 float 的字符串
    Decimal('3.14')
    >>> Decimal((0, (3, 1, 4), -2)) # tuple (sign, digit_tuple, exponent),得到的值是 (-1) * sign * digit_tuple 代表数字 * 10 ^ exponent
    Decimal('3.14')
    >>> Decimal(314) # int,float 都可以
    Decimal('314')
    >>> Decimal(Decimal(314)) # 另一个 decimal 实例
    Decimal('314')
    >>> Decimal(' 3.14 \\n') # 前后可以有空白字符
    Decimal('3.14')
  • decimal.getcontext().prec 代表有效位数,

    • 通过 print(decimal.getcontext().prec) 来查看当前值,默认是28位
    • 通过 decimal.getcontext().prec = Places 来设置有效位数,这个精度的取值是 [1, MAX_PREC] ,MAX_PREC取值在64位机器上是 999999999999999999,32位为 425000000(这是因为这个值要可以转化为C的整型,详见Python版的源码)!
  • 加减乘除运算入常

Fraction (分数)

fractions 库中,定义了 Fraction 类型,用以表达分数,加减乘除运算入常。

使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> from fractions import *
>>> Fraction(1.5) # 传入 float,会自动算出分数表示
Fraction(3, 2)
>>> Fraction(1, 3) # 传入 分子,分母
Fraction(1, 3)
>>> Fraction(2, 6) # 默认会有理化
Fraction(1, 3)
>>> Fraction(2, 6, _normalize=False) # 指定不有理化
Fraction(2, 6)
>>> Fraction(Fraction(1/11)) # 另一个 Fraction 实例,Decimal 实例也可以
Fraction(3275345183542179, 36028797018963968)
>>> Fraction(' 22/7 ') # 使用代表分数的字符串,注意 numerator/denominator,中间不可有空格,前后可有空白字符
Fraction(22, 7)

字符串

字符串的表示

Python 中,字符串可以用单引号 ('...') 或双引号 ("...") 标识单行内的字符串,
还可以使用连续三个单/双引号('''...'''"""...""")表示与格式化的多行字符。

Python没有单独的字符类型;一个字符就是一个简单的长度为1的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> st = '1\
... 2\
... 3\
... a'
>>> st
'123a'
>>> st = '''1
... 2
... 3
... a
... '''
>>> st
'1\n2\n3\na\n'
>>>

⚠️【注意】单引号可以包含双引号,'asd"123"fgh' 是允许的,同样 ''' 中也可以包含 """

字符编码

首先,附上几种字符编码的比较:
| 编码 | 长度 | ‘A’ | ‘中’ |
| – | – | – | – |
| ASCII | 1 Byte | 01000001 | (无此字符) |
| Unicode | 通常是2 Byte | 00000000 01000001 (ASCII前补零) | 01001110 00101101 |
| UTF-8 | 可变(1~6 Byte)| 01000001 (UTF-8包含着ASCII)| 11100100 10111000 10101101 |

Python3.x 默认用 Unicode 编码字符串。

编码 <=> 字符 的函数:

  • ord():获取字符的整数表示;
  • chr():把编码转换成对应的字符;

例如:

1
2
3
4
5
6
7
8
>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(20014)
'丮'

Python 的字符串类型是 str

str 在内存中以 Unicode 表示,一个字符对应若干字节。

在写入二级缓存(本地->硬盘 | 远程->网络)时,str将变为bytes

bytes 以字节为单位。

Python 用带 b 前缀的单/双引号表示 bytes

str bytes
'ABC' b'ABC'

str <=> bytes:

  • encode(): str –> bytes
1
2
3
4
5
6
7
8
9
10
11
>>> 'abc'.encode('ascii')
b'abc'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii') # 超出范围了,报错
'''
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
'''

  • decode(): bytes –> str
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> b'abc'.decode('ascii')
'abc'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('ascii') # 超出范围了,报错
'''
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
'''
>>> # 若 bytes 中包含无法解码的字节,decode() 会报错;
>>> # 可以在调用 decode() 时传入 `errors='ignore'` ,以忽略错误字节。
>>> b'\xe4\xb8\xad\xe6\x96\x87asd'.decode('ascii', errors='ignore')
'asd'

⚠️【注意】为避免乱码问题,应始终使用 utf-8 编码对 strbytes 进行转换。

转义字符

Python 字符串中可以使用转义字符,可以参考下表:

转义字符 描述
\(在行尾时) 续行符
\\ 反斜杠符号
\' 单引号
\" 双引号
\a 响铃
\b 退格(Backspace)
\e 转义
\000
\n 换行
\v 纵向制表符
\t 横向制表符
\r 回车
\f 换页
\xxx 八进制数,例如:'\100' 代表 '@'
\oyy 八进制数,yy代表的字符,例如:'\o12' 代表换行
\xyy 十六进制数,yy代表的字符,例如:'\x0a' 代表换行
\uxxxx 十六进制数,xxxx代表的字符,例如:'\u4e2d\u6587' 代表 '中文'
\other 其它的字符以普通格式输出,例如:'\sds' 代表 '\\sds'

若要取消转义,可以给字符串 r 前缀:

1
2
3
4
5
>>> print('C:\some\name')  # here \n means newline!
C:\some
ame
>>> print(r'C:\some\name') # note the r before the quote
C:\some\name

基本字符串操作

获取字符串长度

Python 内置的 len() 函数可以获取多种类型的对象长度,包括字符串。

1
2
3
4
5
>>> len(word)
6
>>> # 这个方法等效于:
>>> word.__len__()
6

字符串连接

  • 相邻的两个字符串文本自动连接在一起。
1
2
3
4
5
6
7
>>> 'Py' 'thon'
'Python'
>>> # 这个功能可以用来写长字符串:
>>> text = ('Put several strings within parentheses '
'to have them joined together.')
>>> text
'Put several strings within parentheses to have them joined together.'
  • 字符串的 +*

字符串可以由 + 操作符连接(粘到一起),可以由 * 表示重复。

1
2
3
>>> # 3 times 'un', followed by 'ium'
>>> 3 * 'un' + 'ium'
'unununium'

字符串插值

字符串的插值主要有 4 种方式:

  1. 字符串连接
1
2
3
>>> a = 123
>>> "a is " + str(a) + ":)"
'a is 123:)'
  1. % 元组插值
1
2
3
4
5
>>> a = 123
>>> b = 66.77
>>> c = 'hello'
>>> 'a = %s, b = %s, c = %s' % (a, b, c)
'a = 123, b = 66.77, c = hello'
  1. 字符串的 format 方法
1
2
>>> 'a = {arg_a}, b = {arg_b}, c = {arg_c}'.format(arg_a="东方", arg_b=123, arg_c=333.44+8j)
'a = 东方, b = 123, c = (333.44+8j)'

或者也可以匿名:

1
2
>>> 'Another way: {0}, {2}, {1}, {3}'.format("zero", 2, 1.0, 3)
'Another way: zero, 1.0, 2, 3'
  1. f-string

Python 3.6 引入了 f-string,让插值更加优雅:

1
2
3
4
>>> a = 123
>>> b = 456.789
>>> f'{a} + {b} = {a+b}'
'123 + 456.789 = 579.789'

更多关于 f-string 的说明,可以参考 realpython的这篇文章,或者查看 f-string 的来源:PEP 498 – Literal String Interpolation

字符串切分

用字符串的 split 方法可以以指定字符串为分割切分字符串:

1
2
3
4
>>> 'a b   c'.split(' ')
['a', 'b', '', '', 'c']
>>> 'he-*-ll-*-o--'.split('-*-')
['he', 'll', 'o--']

字符串替换

用字符串的 replace 方法可以替换字符串的一部分:

1
2
>>> 'he-*-ll-*-o--'.replace('-*-', '...')
'he...ll...o--'

字符串索引及切片

  • 索引
字符 P y t h o n
正向索引 0(-0) 1 2 3 4 5
反向索引 -6 -5 -4 -3 -2 -1
1
2
3
4
5
>>> word = 'Python'
>>> word[0] # character in position 0
'P'
>>> word[-2] # second-last character
'o'
  • 切片

索引用于获得单个字符,切片 让你获得一个子字符串。

切片的规则是:str[起始:末尾:间隔]
* 起始:开始的索引(取),缺省为0
* 末尾:结束的索引(不取),缺省时,取到字符串最后一个字符(取得到)
* 间隔:每取一个之后间隔几个(>0),缺省为1,为负值时倒着取

1
2
3
4
5
6
7
>>> word[0:2]  # characters from position 0 (included) to 2 (excluded)
'Py'
>>> # 几个常用模式:
>>> word[1:-1] # 去掉首位字符(可以用来去括号、引号)
'ytho'
>>> word[::-1] # 字符串反向
'nohtyP'

⚠️【注意】切片包含起始的字符,不包含末尾的字符。
s[:i] + s[i:] == s
⚠️【注意】字符串索引、切片都是只读的,不可以给字符串索引、切片赋值!

正则表达式

字符串的使用肯定绕不过正则表达式,python 内置了正则表达式模块—— re

我不在这里介绍正则表达式的写法,只是展示怎么用 Python 完成常见的正则表达式操作。正则本身有问题,请到 https://regexr.com。

匹配

1
2
3
4
5
6
7
import re
pattern = r'abc(.*?)def' # 正则里有大量'\',用r字符串取消转义
test = 'abc123def'
if re.match(pattern, test):
print('ok')
else:
print('failed')

提取

1
2
3
4
5
6
7
8
import re
pattern = r'abc(.*?)def(\d+)'
test = 'abc123def456'
m = re.match(pattern, test)
if m:
print(m.groups())
else:
print('failed')

用 m.groups() 即可获取提取到的组:('123', '456')

切分

1
2
# 切分
re.split(r'[\s\,\;]+', 'a,b;; c d')

集合类型

其实,在我心中,字符串也是种集合类型(你学过C语言就会有同感了,字符串不过是字符组成的 list 而已)。所以,刚才介绍字符串的很多操作,在接下来的 list 中也一样适用。

list

list:列表,写在 [] 中。

1
2
3
4
5
6
7
>>> l0 = []
>>> l1 = [1, 2, 3]
>>> l2 = list("hello")
>>> print(f'{l0}\n{l1}\n{l2}')
[]
[1, 2, 3]
['h', 'e', 'l', 'l', 'o']

索引&切片

  • get:lst[索引 || 切片]
  • set:lst[索引 || 切片] = 值
1
2
3
>>> l[0] = 3.14
>>> l[0]
3.14

索引&切片 和字符串中介绍的相同,不了解可以这回去看。

我们来看 list 的基本操作:

尾部添加lst.append(值), lst.extend(iterable)

1
2
3
4
5
6
7
8
9
>>> l = []
>>> l.append(12)
>>> l.append("abc")
>>> l.append(999)
>>> l.append([3, 4])
>>> l.extend(['你', '好'])
>>> l.extend({'再': 1, '见': 2})
>>> l
[12, 'abc', 999, [3, 4], '你', '好', '再', '见']

append 和 extend 一个是添加单个元素,一个是添加一系列元素。

利用切片,append 方法就等效于 lst[len(lst):] = [值], 而 extend 即 lst[len(lst):] = iterable

插入lst.insert(索引, 值)

1
2
3
4
5
6
7
>>> l = []
>>> l.insert(0, 100)
>>> l.insert(len(l), 200) # 等效于 l.append(200)
>>> l.insert(1, 300)
>>> l.insert(1, 400)
>>> l
[100, 400, 300, 200]

lst.remove(值), lst.pop(索引), clear()

remove 是删除 list 中存在的一个值,若不存在,会报 ValueError。

pop 是删除 list 中给定索引处的值,若索引越界,会报 IndexError。

clear 就是清空(删除)所有元素;也可以用 del a[:] 完成类似的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> l
[100, 400, 300, 200]
>>> l.remove(400)
>>> l
[100, 300, 200]
>>> l.pop() # pop 缺省参数则出最后一个,等效于 pop(-1) 或 pop(len(l)-1)
200
>>> l.pop(0)
100
>>> l
[300]
>>> l.remove('fool')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
>>> l.pop(999)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop index out of range
>>> l.clear()
>>> l
[]

其他操作

  • list.index(x[, start[, end]]): 你给值,它返索引
  • list.count(x): 你给值,它告诉你有几个
  • list.sort(key=None, reverse=False): 一种比较快的原址排序
  • list.reverse(): 列表反转,调个个(这个词好怪a,我的问题嘛?)
  • list.copy(): 返一个自己的浅拷贝

关于 list ,可以写的东西实在太多了,都可以单独再写一篇,专门研究了。。。(以后再说吧,可能我会写吧,用了复习数据结构课。)

tuple

tuple:元组,和 list 类似,只是常写在 () 中。

tuple 可以 get,不能set。也就是说,不能 append、不能 pop、不能索引赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> t = (1,2,3)
>>> t[1] = "sdfdsf"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, 3)
>>> t.append(12)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'append'
>>> t.pop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'pop'
>>> t[1:]
(2, 3)

注意,刚才我写了一句“tuple常写在()中”,何来此“常”,tuple 不就是写在圆括号里的嘛?如果你有此疑问,一定是没有好好读过文档。

给你个改过自新的机会:https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences

RTFM

文档里写了:

A tuple consists of a number of values separated by commas.

意思是说:“元组(tuple)是一系列用逗号分开的值!”,可没提用圆括号扩起来的呀,再看看这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples 可以嵌套:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Tuples 不可变:
... t[0] = 88888
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # 但可以包含可变的元素:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])

可见,tuple 对 Python 中例如函数参数的一系列元素的打包传递相当有用!

set

set 是保证元素唯一无序集合。

我们一般也就用它来消除重复,保证唯一。当然,你应该知道,set 就大概对应于数学里的「集合」,所以 set 可以算 病娇茶部 并、交、差、(你可以想象,数学那种补是不能算的,但可以算「对称差」)。

创建一个 set 有两种写法:set() 函数和使用花括号 {ele1, ele2, ...}。但如果你要建空 set,只能用 set(),而不能用 {}!因为你应该知道,{} 建的是空字典。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> s = {}
>>> type(s)
<class 'dict'>
>>> s = set()
>>> type(s)
<class 'set'>
>>> s1 = set([1, 2, 3])
>>> s1
{1, 2, 3}
>>> s2 = {3, 4, 5, 5, 5}
>>> s2
{3, 4, 5}
>>> type(s2)
<class 'set'>
1
2
3
4
5
6
7
8
9
10
11
12
>>> a
{1, 2, 3}
>>> b
{3, 4, 5}
>>> a | b # 并,类似于「逻辑或」:在a或在b或都在
{1, 2, 3, 4, 5}
>>> a & b # 交,类似于「逻辑与」:在a且在b
{3}
>>> a - b # 差,类似于「逻辑差」:在a不在b
{1, 2}
>>> a ^ b # 对称差,类似于「逻辑异或」:在a或在b,但不都在
{1, 2, 4, 5}

dict

dict:字典,键值对,底层实现是哈希表。字面值写作 {key1: value1, key2: value2, ...}

既然 dict 都写成和 set 一样的 {} 了,那你看可以想象,dict 也可以保证唯一性 —— dict保证 key 的唯一。

key 可以是任何「不可变」的值,也就是说,数字、字符串、不包含可变元素的元组都可以作为 key。

1
2
3
4
5
>>> d = {1: 'abc', '2': 2.0001}
>>> d[1]
'abc'
>>> d['2']
2.0001
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['sape'] # 删除元素
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel) # 取键
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel # 键在不在字典里
True
>>> 'jack' not in tel
False

dict() 函数可以有更多初始化字典的技巧,我随便从文档抄两个,你细品:

1
2
3
4
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}

推导式

推导式(comprehensions)是用来创建集合类型实例的。

比如,你想建一个“1到10的平方组成的list”,你如何来写代码?

当然,数据量不大,我们肯定可以这样:

1
a = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

但如果是1~100,1~100000呢?你想用循环对吧:

1
2
3
4
>>> a = []
>>> for i in range(1, 101):
... a.append(i**2)
...

但这样不够简洁!

“1到10的平方组成的list”,说起来就一句话,代码也应该简简单单一行就完成!「推导式」就是干这个的:

1
2
>>> [x**2 for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

推导式的一般形式是:

1
[表达式 任意个forif句]

这个例子可以给你个比较好的印象:

1
2
3
4
5
6
7
8
9
10
11
>>> [(x, y, x*y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3, 3), (1, 4, 4), (2, 3, 6), (2, 1, 2), (2, 4, 8), (3, 1, 3), (3, 4, 12)]
>>> # 相当于:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y, x*y))
...
>>> combs
[(1, 3, 3), (1, 4, 4), (2, 3, 6), (2, 1, 2), (2, 4, 8), (3, 1, 3), (3, 4, 12)]

其实准确的说,我们刚才写的这些都是「列表推导式」。我们还有「set推导式」甚至是「dict推导式」。

1
2
3
4
>>> {x for x in 'abracadabra' if x not in 'abc'}
{'r', 'd'}
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

你可能还想写这个:

1
2
>>> (x**2 for x in range(1, 11))
<generator object <genexpr> at 0x107988bd0>

哈哈,有点不一样了,这次它给你返了个 generator——生成器,这就不是推导式了,这里不做介绍,后面我们再讨论这东西。

最后,再看两个很有意思的例子:

1
2
3
4
>>> [[(j, i, j*i) for j in range(1, i+1)] for i in range(1, 10)]

>>> [(j, i, j*i) for i in range(1, 10) for j in range(1, i+1)]
[[(1, 1, 1)], [(1, 2, 2), (2, 2, 4)], [(1, 3, 3), (2, 3, 6), (3, 3, 9)], [(1, 4, 4), (2, 4, 8), (3, 4, 12), (4, 4, 16)], [(1, 5, 5), (2, 5, 10), (3, 5, 15), (4, 5, 20), (5, 5, 25)], [(1, 6, 6), (2, 6, 12), (3, 6, 18), (4, 6, 24), (5, 6, 30), (6, 6, 36)], [(1, 7, 7), (2, 7, 14), (3, 7, 21), (4, 7, 28), (5, 7, 35), (6, 7, 42), (7, 7, 49)], [(1, 8, 8), (2, 8, 16), (3, 8, 24), (4, 8, 32), (5, 8, 40), (6, 8, 48), (7, 8, 56), (8, 8, 64)], [(1, 9, 9), (2, 9, 18), (3, 9, 27), (4, 9, 36), (5, 9, 45), (6, 9, 54), (7, 9, 63), (8, 9, 72), (9, 9, 81)]]

乘法表,这两种写法效果是一样的。

1
2
3
4
5
6
7
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

没错!矩阵转置,利用推导式中只用一行代码就可以实现。


未完待续…

(最近这两天都在补作业呀、补作业呀、补作业呀…不补作业的时候还要打游戏…忙得很。而且最近练习双拼,中文打字速度直线下降,所以这篇写了好久。现在作业大概补完了,双拼也已有了小成(大概?),以后应该会快一些了。)

Next: 【Python流程控制】