元组
Python元组的属性:
- 任意对象的有序集合
- 通过偏移量存取
- 属于“不可变序列”
- 固定长度、多样性、任意嵌套
- 对象引用的数组
元组的常见方法:
运算 | 解释 |
---|---|
() |
空元组 |
T = (0,) |
单个元素的元组 |
T = (0, 'Ni', 1.2, 3) |
四个元素的元组 |
T = 0, 'Ni', 1.2, 3 |
还是四个元素的元组 |
T = ('Bob', ('dev', 'mgr')) |
嵌套元组 |
T = tuple('spam') |
可迭代对象的元素组成的元组 |
T[i] |
索引 |
T[i][j] |
索引的索引 |
T[i:j] |
分片 |
len(T) |
长度 |
T1 + T2 |
拼接 |
T * 3 |
重复 |
for x in T: print(x) |
迭代 |
'spam' in T |
成员关系测试 |
[x ** 2 for x in T] |
列表推导(好像不应该出现在这里?) |
namedtuple('Emp', ['name', 'jobs']) |
有名元组扩展类型 |
元组的实际应用
元组支持字符串和列表的一般序列操作,如拼接、重复、索引和分片。
>>> (1, 2) + (3, 4)
(1, 2, 3, 4)
>>> (1, 2) * 4
(1, 2, 1, 2, 1, 2, 1, 2)
>>> T = (1, 2, 3, 4)
>>> T[0]
1
>>> T[1:3]
(2, 3)
元组的特殊语法:逗号和圆括号
如果我们要得到元组而不是表达式的值,我们要在圆括号中加,
。如果要构造单个元素的元组,就要在元素的后面加上,
。
>>> (40) # 数字
40
>>> (40,) # 元组
(40,)
在不会引起二义性的情况下,Python允许构造元组时省略圆括号。要用到圆括号的情形:元组出现在一个函数调用中,或嵌套在一个更大的表达式内。
转换、方法和不可变性
注意:上述的对元组的操作(拼接、重复等)会返回一个新的元组。元组不提供字符串、列表和字典中的方法。要对一个元组的元素排序,可以转换成可变对象(如列表),或者使用新的内置函数(如sorted
)。
>>> T = ('cc', 'aa', 'dd', 'bb')
>>> T = tuple(tmp)
>>> tmp = list(T)
>>> tmp.sort()
>>> sorted(T)
['aa', 'bb', 'cc', 'dd']
>>> T
('aa', 'bb', 'cc', 'dd')
list
将元组转换为列表,tuple
将列表转换为元组。
列表推导也可以用来转换元组。
>>> T = (1, 2, 3, 4, 5)
>>> L = [x + 20 for x in T]
>>> L
[21, 22, 23, 24, 25]
列表的本质是不可变序列操作,列表推导甚至可以用在某些并非实际存储的序列上,任何可迭代对象都可以。
元组有属于自己的方法——index
索引,count
寻找元组中元素的数目。
>>> T = (1, 2, 3, 2, 4, 2)
>>> T.index(2) # 第一个2的索引
1
>>> T.index(2, 2) # 从偏移2开始,第一个2的索引
3
>>> T.count(2)
3
注意:元组的不可变性只适用于元组本身顶层而非其内容。我们可以修改元组内部的列表。
>>> T = (1, [2, 3], 4)
>>> T[1] = 'spam'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> T[1][0] = 'spam'
>>> T
(1, ['spam', 3], 4)
为什么有了列表还需要元组
“元组”的概念来自于数学,元组的不可变性提供了某种一致性,确保元组不会被另一个引用修改,类似于“常量”声明。
重访记录:有名元组
我们可以实现一个同时提供序号和键两种询问方式的对象。例:namedtuple
工具实现了一个增加了逻辑的元组扩展类型(在模块collections
被使用),能同时支持使用序号和属性访问组件。
>>> from collections import namedtuple
>>> Rec = namedtuple('Rec', ['name', 'age', 'jobs'])
>>> bob = Rec('Bob', age=40.5, jobs=['dev', 'mgr'])
>>> bob
Rec(name='Bob', age=40.5, jobs=['dev', 'mgr'])
>>> bob[0]
'Bob'
>>> bob[2]
['dev', 'mgr']
>>> bob.name
'Bob'
>>> bob.jobs
['dev', 'mgr']
>>>
可以将其转换成一个字典,或者类似字典的结构:
>>> O = bob._asdict()
>>> O['name']
'Bob'
>>> O['jobs']
['dev', 'mgr']
>>> O
{'name': 'Bob', 'age': 40.5, 'jobs': ['dev', 'mgr']}
元组和有名元组都支持解包元组赋值:
>>> bob = Rec('Bob', 40.5, ['dev', 'mgr'])
>>> name, age, jobs = bob
>>> name
'Bob'
>>> jobs
['dev', 'mgr']
但是,解包元组赋值有时候不适用于字典,因为字典中的键值是没有序号的。
>>> bob = {'name': 'Bob', 'age': 40.5, 'jobs': ['dev', 'mgr']}
>>> bob.values()
dict_values(['Bob', 40.5, ['dev', 'mgr']])
>>> job, name, age = bob.values()
>>> job
'Bob'
>>> name
40.5
文件
通过open
,我们能够创建一个Python文件对象作为到计算机上一个文件的链接。在调用open
后,我们可以通过返回的文件对象的方法,在程序与相应文件之间来回传递串形式的数据。
文件对象与前面介绍的核心数据类型不同,它只支持与文件处理任务相关的方法。常见的文件操作见下表:
操作 | 解释 |
---|---|
output = open(r'C:\spam', 'w') |
创建输出文件('w' 代表写入文件) |
input = open('data', 'r') |
创建输入文件('r' 代表从文件读入) |
input = open('data') |
同上('r' 是默认值) |
aString = input.read() |
把整个文件读入字符串 |
aString = input.read() |
读取接下啦的N 个字符到一个字符串 |
aString = input.readline() |
读取下一行(包括行末的'\n' )到一个字符串 |
aList = input.readlines() |
读取整个文件到一个字符串列表,列表中的元素是包括行末的'\n' 的行 |
output.write(aString) |
把字符串写入文件 |
output.writelines(aList) |
把列表内所有的字符串写入文件 |
output.close() |
手动关闭(文件收集完成时会自动关闭) |
output.flush() |
把输出缓冲区刷入硬盘中,但不关闭文件 |
anyFile.seek(N) |
把文件位置移动到偏移量N 处以便进行下一个操作 |
for line in open('data'): use line |
文件迭代器逐行获取 |
open('f.txt', encoding='latin-1') |
Python 3.X Unicode文件(str 字符串) |
open('f.bin', 'rb') |
Python 3.X 字节码文件(bytes 字符串) |
codecs.open('f.txt', encoding='utf-8') |
Python 2.X Unicode文件(unicode 字符串) |
open('f.bin', 'rb') |
Python 2.X 字节码文件(str 字符串) |
打开文件
打开文件时,程序调用内置函数open
,第一个参数是外部文件名,第二个参数是文件的处理模式,返回文件对象,这个文件对象带有传输数据的方法:
afile = open(filename, mode)
afile.method()
第一个参数是外部文件名,它可能有相对路径前缀,如果没有,则默认在脚本所运行的目录下。
第二个参数是打开方式,'r'
表示读文件,'w'
表示写文件,'a'
表示在文件内容追加内容并打开文件,'b'
表示可以进行二进制处理,'+'
表示同时支持输入输出。
第三个参数是可选的,控制输出缓冲,0表示输出无缓冲。
使用文件
基础用法的提示:
- 文件迭代器最适合逐行读取
- 写入文件的内容必须是字符串,Python不会帮你转换
- 文件是被缓冲的、可定位的,写入的文本不会被立刻从内存转移到硬盘,除非关闭文件或用
flush()
方法 close
是可选的,回收时自动关闭
文件的实际应用
首先为输出打开一个文件,写入两行文本,关闭文件:
>>> afile = open('1.txt', 'w')
>>> myfile = open('myfile.txt', 'w')
>>> myfile.write('hello text file\n')
16
>>> myfile.write('goodbye text file\n')
18
>>> myfile.close()
在Python 3.X中,写入字符串会返回字符数,但Python 2.X不会。
然后打开文件,逐行读取。第三个readline()
返回空字符串,表示文件已经到达末尾。
>>> myfile = open('myfile.txt')
>>> myfile.readline()
'hello text file\n'
>>> myfile.readline()
'goodbye text file\n'
>>> myfile.readline()
''
read
方法把整个文件内容读入到一个字符串中:
>>> open('myfile.txt').read()
'hello text file\ngoodbye text file\n'
如果要逐行扫描文件并对每一行操作,文件迭代器是最佳选择:
>>> for line in open('myfile.txt'):
... print(line.upper(), end='')
...
HELLO TEXT FILE
GOODBYE TEXT FILE
这样,open
创建的临时文件对象将自动在每次循环迭代时读入并返回一行。优点:容易编写、更高的内存使用效率、更快。
文本和二进制文件:一个简要的故事
Python总是支持文本和二进制文件,其中:
-
文本文件把内容表示为常规的
str
字符串,自动执行Unicode编码和解码,并且默认执行末行转换; -
二进制文件把内容表示为一个特殊的
bytes
字节串类型。
由于文本文件实现了Unicode编码,因此不能以文本模式打开一个二进制文件,否则解码会失败。当我们读取一个二进制文件时,会得到一个bytes
对象(字节串)。
>>> open('myfile.txt', 'rb').read()
b'hello text file\r\ngoodbye text file\r\n'
此外,二进制文件不会对数据执行任何字符串转换。
在文件中存储Python对象:转换
下面的例子把多种Python对象写入一个文本文件的各行,必须把对象转换成字符串。
>>> open('myfile.txt', 'r').read()
'hello text file\ngoodbye text file\n'
>>> X, Y, Z = 43, 44, 45
>>> S = 'Spam'
>>> D = {'a': 1, 'b': 2}
>>> L = [1, 2, 3]
>>>
>>> F = open('datafile.txt', 'w')
>>> F.write(S + '\n')
5
>>> F.write('%s,%s,%s\n' % (X, Y, Z))
9
>>> F.write(str(L) + '$' + str(D) + '\n')
27
>>> F.close()
创建文件后,我们可以读取文件中的内容。注意,交互式(在交互模式下输入对象)显示给出直接的字节内容,而print
操作解释内嵌的换行符”:
>>> chars = open('datafile.txt').read()
>>> chars
"Spam\n43,44,45\n[1, 2, 3]${'a': 1, 'b': 2}\n"
>>> print(chars)
Spam
43,44,45
[1, 2, 3]${'a': 1, 'b': 2}
但是,问题是如何将文本文件中的字符串转换成Python对象。
把第一行转换为字符串:
>>> F = open('datafile.txt')
>>> line = F.readline()
>>> line
'Spam\n'
>>> line.rstrip()
'Spam'
把第二行转换为数字序列。这里int
函数可以忽略数字字符串旁边的的空白:
>>> line = F.readline()
>>> parts = line.split(',')
>>> parts
['43', '44', '45\n']
>>> numbers = [int(P) for P in parts]
>>> numbers
[43, 44, 45]
转换第三行时,我们可以运行eval
函数,它把字符串参数当作可执行程序代码:
>>> line = F.readline()
>>> line
"[1, 2, 3]${'a': 1, 'b': 2}\n"
>>> parts = line.split('$')
>>> parts
['[1, 2, 3]', "{'a': 1, 'b': 2}\n"]
>>> eval(parts[0])
[1, 2, 3]
>>> eval(parts[1])
{'a': 1, 'b': 2}
存储Python原生对象:pickle
eval
的缺陷是:它会执行任何表达式,包括删除计算机中所有文件的表达式。如果要存储Python的原生对象,就应该使用pickle
。
pickle
是一种能够让我们直接在文件中存储几乎任何Python对象的高级工具,而且不需要字符串转换。
例:在文件中存储字典:
>>> D = {'a': 1, 'b': 2}
>>> F = open('datafile.pkl', 'wb')
>>> import pickle
>>> pickle.dump(D, F)
>>> F.close()
想要取回字典时,再次使用pickle
重建即可:
>>> F = open('datafile.pkl', 'rb')
>>> E = pickle.load(F)
>>> E
{'a': 1, 'b': 2}
实际上,pickle
将字典转化为一系列二进制的字节串,也能从字节串转化为对象:
b'\x80\x04\x95\x11\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x01a\x94K\x01\x8c\x01b\x94K\x02u.'
shelve
也可以实现类似功能,但这里不讨论。
用JSON格式存储Python对象
JSON是一种新兴的数据交换格式,它与语言无关,也支持多种系统。它的可移植性在一些场景中会带来巨大优势。此外,由于JSON与Python中的字典和列表在语法上的相似性,使Python的json
标准库模块能够很容易地在Python对象与JSON之间来回转换。
比如,Python的字典和JSON数据十分相似。我们创建一个字典,发现可以将字面量几乎原封不动地传给JSON文件:
>>> name = dict(first='Bob', last='Smith')
>>> rec = dict(name=name, job=['dev', 'mgr'], age=40.5)
>>> rec
{'name': {'first': 'Bob', 'last': 'Smith'}, 'job': ['dev', 'mgr'], 'age': 40.5}
>>> import json
>>> json.dumps(rec)
'{"name": {"first": "Bob", "last": "Smith"}, "job": ["dev", "mgr"], "age": 40.5}'
>>> S = json.dumps(rec)
>>> O = json.loads(S)
>>> O
{'name': {'first': 'Bob', 'last': 'Smith'}, 'job': ['dev', 'mgr'], 'age': 40.5}
json
模块可以使JSON表达式和Python对象互相转换。从json
文件读取对象时,json
模块把文件地内容从JSON表示重建成Python对象。这里,dump
把对象地字面量读取到json文件中,load
把json文件中的字面量加载到变量。注意,实际上的文件扩展名应该是.json
而不是.txt
。
>>> json.dump(rec, fp=open('testjson.txt', 'w'), indent=4)
>>> print(open('testjson.txt').read())
{
"name": {
"first": "Bob",
"last": "Smith"
},
"job": [
"dev",
"mgr"
],
"age": 40.5
}
>>> P = json.load(open('testjson.txt'))
>>> P
{'name': {'first': 'Bob', 'last': 'Smith'}, 'job': ['dev', 'mgr'], 'age': 40.5}
存储打包二进制数据:struct
struct
模块能够构造/解析打包二进制数据。
要生成一个打包二进制数据,可以用'wb'
(写入二进制)打开它,并将一个格式化字符串和几个Python对象传给struct。
>>> F = open('data.bin', 'wb')
>>> import struct
>>> data = struct.pack('>i4sh', 7, b'spam', 8)
>>> data
b'\x00\x00\x00\x07spam\x00\x08'
>>> F.write(data)
10
>>> F.close()
Python会创建一个我们通常写入文件的二进制bytes
数据字符串,主要由不可打印字符的十六进制转义组成。
要把二进制文件解析成一般的Python对象,可以直接读取字节串,并使用相同的格式字节串解压即可。
>>> F = open('data.bin', 'rb')
>>> data = F.read()
>>> data
b'\x00\x00\x00\x07spam\x00\x08'
>>> values = struct.unpack('>i4sh', data)
>>> values
(7, b'spam', 8)
二进制文件是高级且底层的工具,因此这里不会介绍更多细节。
文件上下文处理器
文件上下文能够让我们把文件处理代码包装到一个逻辑层中,以确保在推出后一定会自动关闭文件,而不是在垃圾回收时自动关闭:
with open(r'C:\code\data.txt') as myfile:
for line in myfile:
...use line here...
之后学的try/finally
也提供类似的功能。
其他文字工具
更多文件的方法查询:dir()
函数、help()
函数、第13章。
Python工具集中有其他类似可用的文件工具:
- 标准流
os
模块中的描述文件- 套接字、管道和FiFO文件
- 通过键存取的文件
- Shell命令流
核心类型复习总结
- 按照分类,一些对象拥有共同的操作。如字符串、列表和元组拥有序列操作。
- 只有可变对象可以在原位置修改。可变对象有:列表、字典和集合。
- 文件只导出方法,因此可变性不适用于文件类型。
- “数字”包含:整数、浮点数、复数、小数和分数。
- 字符串包含:
str
、Python 3.X中的bytes
和Python 2.X中的unicode
。 - 集合可以视为没有值只有键的字典。
- 除了类型分类操作,所有类型都有可调用的方法。
请注意:运算符重载
在数值类型中,+
表示将两个数的值相加;在序列中,+
表示拼接两个序列。如果要设计新的类,我们可以定义加号(以及其他运算符)作用于这个类对象的含义。比如,定义类的加法可以用:def __add__(self, other):
。
对象灵活性
一般来说:
- 列表、字典和元组可以包含任何种类的对象;
- 集合可包含任意的不可变类型对象;
- 列表、字典和元组可以任意嵌套;
- 列表、字典和集合可以动态地扩大和缩小。
Python的复合对象类型支持任意结构,因此它们非常适合表示程序中的复杂数据,可以嵌套任意多层。下面是一个嵌套的例子:
>>> L = ['abc', [(1, 2), ([3], 4)], 5]
>>> L[1]
[(1, 2), ([3], 4)]
>>> L[1][1]
([3], 4)
>>> L[1][1][0]
[3]
>>> L[1][1][0][0]
3
用图表示,就是:
引用vs复制
第6章提到,赋值操作总是存储对象的引用,而不是对象的副本。当涉及到较大的对象时,这种现象会变得微妙:
>>> X = [1, 2, 3]
>>> L = ['a', X, 'b']
>>> D = {'x': X, 'y': 2}
修改这三个引用的任意一个共享列表对象X
,也会改变另外两个引用的对象:
>>> X[1] = 'surprise'
>>> L
['a', [1, 'surprise', 3], 'b']
>>> D
{'x': [1, 'surprise', 3], 'y': 2}
引用是其他语言中指针的更高级模拟。虽然我们不能获得引用本身,但是我们可以在不止一个地方存储相同的引用。
这意味着,我们可以在程序范围内任何地方传递大型对象而不用复制。如果要复制,需要明确要求:
- 没有参数的分片表达式
L[:]
可以复制序列; - 字典、集合和列表的
copy
方法可以复制本身; - 内置函数也可以复制,如将
list
方法应用于列表,dict
方法应用于字典等等; copy
标准库模块能够在需要时创建完整副本。
列表和字典复制的例子:
>>> L = [1, 2, 3]
>>> D = {'a': 1, 'b': 2}
>>> # 将副本赋值给其他变量
>>> A = L[:]
>>> B = D.copy()
>>> # 由其他变量引发的改变将修改新产生的副本,而不是原有的对象
>>> A[1] = 'Ni'
>>> B['c'] = 'spam'
>>> L, D
([1, 2, 3], {'a': 1, 'b': 2})
>>> A, B
([1, 'Ni', 3], {'a': 1, 'b': 2, 'c': 'spam'})
但是,copy
方法只能进行顶层复制。
>>> L0 = [1, 2, 3]
>>> L1 = ['a', L0, 'b']
>>> L2 = L1.copy()
>>> L2
['a', [1, 2, 3], 'b']
>>> L2[0] = 'c'
>>> L1, L2
(['a', [1, 2, 3], 'b'], ['c', [1, 2, 3], 'b'])
>>> L2[1][1] = 5
>>> L1, L2
(['a', [1, 5, 3], 'b'], ['c', [1, 5, 3], 'b'])
要进行深层复制(完整、独立的复制),需要copy
模块中的deepcopy
方法。
比较、等价性和真值
所有的Python对象都支持:测试等价性、相对大小等。比较复合对象时,会检查所有的组件,直到得出结果为止。
这种比较也被称为递归比较——对顶层对象的比较会被应用到每一个嵌套的下一次对象中,直到最底层的对象并得出结果。就核心类型而言,递归功能是默认实现的。
在比较列表对象时将自动比较所有内容,直到找到一个不匹配或完成比较:
>>> L1 = [1, 2, 3]
>>> L2 = [1, 2, 3]
>>> L1 == L2
True
>>> L1 is L2
False
这里展示了两种测试等价性的方式,分别是:
==
运算符测试值的等价性,Python递归地比较所有内嵌对象。is
表达式测试对象的同一性,Python测试两者是否为同一个对象(在存储器中有相同地址)。
注意对短字符串的运行结果:
>>> S1 = 'spam'
>>> S2 = 'spam'
>>> S1 == S2
True
>>> S1 is S2
True
这是因为,Python会通过重复地理利用短字符串进行优化,因此S1
和S2
共享同一个'spam'
。但这不适用于长字符串:
>>> S1 = 'A Longer String'
>>> S2 = 'A Longer String'
>>> S1 == S2
True
>>> S1 is S2
False
相对大小比较也能递归地应用于嵌套数据结构:
>>> L1 = [1, ('a', 3)]
>>> L2 = [1, ('a', 2)]
>>> L1 < L2, L1 == L2, L1 > L2
(False, False, True)
Python比较相对大小地规则是:
- 数字在转换成必要的公共最高级类型后,比较数值地相对大小。
- 字符串按照字母字典顺序比较(从左到右比较字符的ASCII码大小)。
- 列表和元组从左到右对每个组件内容进行比较,直到末尾或发现区别。
- 集合是相等的,当且仅当它们含有相同的元素。集合的比较大小采取子集和超集的标准。
- 字典通过比较排序后的
(key, value)
列表判断是否相同。(仅Python 2.X) - 非数字不同类型(混合类型)的比较。(仅Python 2.X)
Python 2.X和3.X混合类型比较和排序
Python 2.X中的混合类型通过任意的顺序的比较,但3.X不允许混合顺序比较。
Python 2.X和3.X中的字典比较
在Python 2.X中,字典支持相对大小比较,就等效于比较排序的键/值列表,但是在Python 3.X中,字典之间的比较大小由于开销太大,故被移除。
Python 3.X的替代方式是:要么编写循环键比较值,要么手动比较排序的键/值列表(利用sorted
方法):
>>> list(D1.items())
[('a', 1), ('b', 2)]
>>> list(D2.items())
[('a', 1), ('b', 3)]
>>> sorted(D1.items()) < sorted(D2.items())
True
>>> sorted(D1.items()) > sorted(D2.items())
False
Python中True和False的含义
在Python中,1表示真,0表示假。实际上,真和假是Python中每个对象的固有属性,规则如下:
- 数字如果等于0则为假,反之则为真。
- 其他对象如果为空则为假,反之则为真。
一些是一些例子:
对象 | 值 |
---|---|
"spam" |
True |
"" |
False |
[1, 2] |
True |
[] |
False |
{'a': 1} |
True |
{} |
False |
1 |
True |
0.0 |
False |
None |
False |
这样做会使对象的检查更加简单,如检查字符串是否为空可以简化成if X:
,也可以if X != '':
。
None对象
None
总是为假,这是Python中一种特殊数据类型的唯一值,作用是空占位符。
None
不意味着“未定义”,而是某些内容。None
是一个真正的对象,有一块真实的内存。None
是Python给定的一个内置名称,也是函数的默认返回值。
bool类型
Python的布尔类型bool
只是扩展了Python中的真假概念。这种设计只是为了让真值更加显式,程序更明确。
Python还提供了一个内置的函数bool
,显式地把对象转换为对象的布尔值。
>>> bool(1)
True
>>> bool('spam')
True
>>> bool({})
False
Python的类型层次
类型和对象
即使是类型本身在Python中也是一种类型的对象:对象的类型本身,也属于type
类型的对象。
从Python 2.2开始,每个核心类型都有一个新内置名,用来支持通过面向对象编写子类的类型定制:dict
、list
、tiple
、str
、int
、float
、complex
、bytes
、type
、set
等。
Python的其他类型
后面要学到的类型:函数、模块和类。以及扩展:正则表达式对象、DBM文件、GUI组件、网络套接字等等。它们(扩展)与核心类型的区别是:内置类型不需要导入模块,但扩展类型需要用import
导入模块。
内置类型陷阱
赋值创建引用,而不是复制
可变对象的共享引用在你的程序中至关重要。
如果不想要这种共享,那么使用复制的手段来避免共享。
重复会增加层次深度
这里,当可变序列进行嵌套时,可能会使列表重复,或者列表中嵌套四个列表:
>>> L = [4, 5, 6]
>>> X = L * 4
>>> X
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> Y = [L] * 4
>>> Y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
Y
包含了指向原本L
的列表的引用,因此出现了副作用:
>>> L[1] = 0
>>> X
[4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]
>>> Y
[[4, 0, 6], [4, 0, 6], [4, 0, 6], [4, 0, 6]]
解决方法是:进行复制,这里用list
解决:
>>> L = [4, 5, 6]
>>> Y = [list(L)] * 4
>>> Y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
>>> L[1] = 0
>>> Y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
但是,Y
对应的列表对象所嵌套的引用仍然指向同一个对象。要避免这种现象,必须保证每一个嵌套有一个单独的副本:
>>> Y[0][1] = 99
>>> Y
[[4, 99, 6], [4, 99, 6], [4, 99, 6], [4, 99, 6]]
>>> L = [4, 5, 6]
>>> Y = [list(L) for i in range(4)]
>>> Y
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
>>> Y[0][1] = 99
>>> Y
[[4, 99, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
注意循环数据结构
如果一个复合对象包含指向自己的引用,就称之为循环对象。Python在检测到循环时,会打印出[...]
。
>>> L = [1, 2, 3]
>>> L.append(L)
>>> L
[1, 2, 3, [...]]
经验法则:除非真的需要,否则不要使用循环引用。
不可变类型不可以在原位置改变
如果要改变,需要通过分片、拼接等操作创建一份新的对象。
标签:文件,Python,笔记,元组,对象,列表,open From: https://www.cnblogs.com/hiu-siu-dou/p/18411402