|
元组
Python元组的属性:
- 任意对象的有序集合
- 通过偏移量存取
- 属于“不可变序列”
- 固定长度、多样性、任意嵌套
- 对象引用的数组
元组的常见方法:
运算解释()空元组T = (0,)单个元素的元组T = (0, 'Ni', 1.2, 3)四个元素的元组T = 0, 'Ni', 1.2, 3还是四个元素的元组T = ('Bob', ('dev', 'mgr'))嵌套元组T = tuple('spam')可迭代对象的元素组成的元组T索引T[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{}False1True0.0FalseNoneFalse这样做会使对象的检查更加简单,如检查字符串是否为空可以简化成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, [...]]
复制代码 经验法则:除非真的需要,否则不要使用循环引用。
不可变类型不可以在原位置改变
如果要改变,需要通过分片、拼接等操作创建一份新的对象。
来源:https://www.cnblogs.com/hiu-siu-dou/p/18411402
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|