首页 > 编程语言 >【Python学习笔记】 第9章 元组、文件和其他核心类型

【Python学习笔记】 第9章 元组、文件和其他核心类型

时间:2024-11-14 15:33:17浏览次数:1  
标签:文件 Python 笔记 元组 对象 列表 open

元组

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()

第一个参数是外部文件名,它可能有相对路径前缀,如果没有,则默认在脚本所运行的目录下。

img

第二个参数是打开方式,'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

用图表示,就是:

img

引用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}

引用是其他语言中指针的更高级模拟。虽然我们不能获得引用本身,但是我们可以在不止一个地方存储相同的引用。

img

这意味着,我们可以在程序范围内任何地方传递大型对象而不用复制。如果要复制,需要明确要求:

  • 没有参数的分片表达式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会通过重复地理利用短字符串进行优化,因此S1S2共享同一个'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的类型层次

img

类型和对象

即使是类型本身在Python中也是一种类型的对象:对象的类型本身,也属于type类型的对象。

从Python 2.2开始,每个核心类型都有一个新内置名,用来支持通过面向对象编写子类的类型定制:dictlisttiplestrintfloatcomplexbytestypeset等。

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

相关文章

  • 《Django 5 By Example》阅读笔记:p76-p104
    《Django5ByExample》学习第4天,p76-p104总结,总计29页。一、技术总结1.环境变量管理这里作者使用的是:python-decouple,本人在实际项目中使用的是python-dotenv,这里只是简单的使用,感觉两者差不多。2.评论功能评论功能设计的时候主要考虑两个方面:是否需要登录才能发表评论,评论......
  • Python可视化Matplotlib折线图plot用法详解
    importmatplotlib.pyplotaspltimportrandom,iofrompylabimportmplimportnumpyasnp#画出温度变化图#设置显示中文字体mpl.rcParams["font.sans-serif"]=["SimHei"]#设置正常显示符号mpl.rcParams["axes.unicode_minus"]=False# 准备x.y坐标......
  • Python练习2:企业发放的奖金根据利润提成。利润([)低于或等于10万元时,奖金可提10%;利润
     Python练习2:企业发放的奖金根据利润提成。利润([)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%;20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之间时高于40万元的部分,可提成3%;60万到100万之间时......
  • 探险家的秘密武器:Python爬虫
    在一个名为“代码王国”的神秘地方,有一位著名的探险家,他的名字叫“爬虫侠”。爬虫侠不是普通的探险家,他是一位Python程序员,擅长使用代码作为武器,探索未知的世界。今天,他接到了一个任务:从遥远的“电商星球”的店铺中,获取所有的商品信息。准备阶段:装备升级在出发前,爬虫侠需要......
  • python爬虫获得店铺的所有商品
    在编写Python爬虫以获取店铺的所有商品信息时,通常涉及到发送HTTP请求、解析响应内容以及处理API返回的数据。以下是一个详细的Python爬虫示例,用于获取店铺的商品信息。这个示例假设API返回的是JSON格式的数据,并且需要API密钥进行认证。步骤1:导入必要的库首先,需要导入Python......
  • 【跟着阿舜学音乐-笔记】1.09音程与协和度
    音程音程是指两个音之间的距离,即一个音到另一个音经过了多少个音高的音高单位。其中,所经历的音高单位的数量叫做音数。具有不同音数的音之间的距离叫做度。音程的下方较低的音称为根音(该说法也用于和弦中,指原位中最低的音),上方较高的音称为冠音。以下给出各音程的说明:音程的......
  • python-三方库-PyTorch-Pillow (PIL Fork)
    1需求官网:https://python-pillow.org/docs:https://pillow.readthedocs.io/en/stable/需求:Image.filename需求:获取图像格式Image.format需求:获取图像尺寸Image.sizeImage.widthImage.height需求:获取图像颜色模式Image.mode需求:获取图像像素值Image.load()[x,y]......
  • LeetCode刷题笔记9.9-9.15
    LeetCode刷题笔记9.9-9.15二叉树主要学两种遍历方式:层序遍历、递归遍历1)层序遍历BFS基本思想:逐层遍历元素,可以借助队列,先进先出,队首出元素的同时进该元素的左右节点(这也是最简单的实现方式)队列Q:1->出1进2,3(2,3)->出2进4(3,4)->出3进5,6(4,5,6)->出4(5,6)->出5(6)->出6(空)队列进......
  • python毕设KTV点歌系统的设计与实现程序+论文
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容选题背景随着信息技术的快速发展,娱乐行业也在不断进步。KTV作为一种流行的娱乐方式,已经成为了人们休闲放松的重要场所。然而,传统的KTV点歌系统存在......
  • Python讲解(第六篇)
    上一篇讲了运算符和字符串扩展的三种定义方法。但是字符串的扩展内容较多,分为了多篇讲解,所以这篇讲解字符串的扩展的字符串的拼接和字符串格式化。1.字符串的拼接在Python中,字符串拼接是指将两个或多个字符串合并成一个新的字符串。字符串拼接有多种方法,以下是几种常见的方......