Python进阶核心知识点(6)- 列表使用的坑,这里总结好了请查收!

列表是python中最简单最常用的容器,没有之一。很多初学者最新接触的Python数据结构就是列表。这也是列表的特性决定的:列表就是一个筐,什么东西都可以往里面装!关键是里面的元素还可以是不同的类型。也正是因为简单,大多数情况下我们都用列表来完成我们的任务。但是,列表有一些关键的地方需要我们注意的坑,不然会出现一些莫名其妙的错误。下面就是小编平常使用中总结的一些,供大家参考。

入坑之前,我们先明白python的列表是一个可变对象。至于什么是可变对象,用下面的代码来说明:

a = [1,2,3]
b= a
print(f'before: id(a) = {id(a)},id(b) = {id(b)}')
a.append(4)
print(f'after: a = {a}, b={b}')
print(f'after: id(a) = {id(a)},id(b) = {id(b)}')

>>>执行结果:
before: id(a) = 1528755230464,id(b) = 1528755230464
after: a = [1, 2, 3, 4], b=[1, 2, 3, 4]
after: id(a) = 1528755230464,id(b) = 1528755230464

这段代码大家都很容易看懂:

  • 先是创建了一个列表对象 :[1,2,3],并将变量a指向这个对象,之后将变量b也指向这个对象。注意,b=a,只是将a指向的对象赋给了b,此时系统并没有为b在单独创建一个对象。我们用id方法可以看到变量a和b指向同一个对象,即id(a)和id(b)完全相同。这里的指向可以理解为一个便利贴,便利贴上写着a,然后贴到了对象[1,2,3]上面。既然是便利签,那么可以随时拿下来贴到其他对象上面
  • 在变量a的末尾增加一个元素后,实际上是改变的变量a指向的对象,即列表[1,2,3]。我们所说的列表是可变对象,意思就是说这个对象的是可以改变的。增加后值变成了[1,2,3,4]但是地址并没有改变
  • 我们只对a进行了append操作,但是实际上是操作a指向的对象。这个对象的值改变后,所有指向他的变量的值也都变了。所以虽然只对a进行append操作,变量b的值也跟着变了

理解了上面这一点,下面这个就容易了:

2. 坑1: 列表等容器作为可变对象,当其作为参数传递给函数时,是通过引用进行传递。

也就是说函数接受到的是这个变量指向的对象。因而在函数内部对这个对象进行操作时,会改变这个对象的值!从而影响到函数外部对这个对象的引用!这一点需要注意,看下面代码:

list_outer = [1,2,3,4]
print('in the function outside, the list id is {}'.format(id(list_outer)))
def list_append_100(a):
    print('in the function inside, the list id is {}'.format(id(a)))
    a.append(100)
    
list_append_100(list_outer)
print('after call the function, the list_outer is:', list_outer)

>>>执行结果:
in the function outside, the list id is 1528771905856
in the function inside, the list id is 1528771905856
after call the function, the list_outer is: [1, 2, 3, 4, 100]

从中可以很明显的看到函数接受的列表和外部的列表的id相同,即是同一个对象。当在内部对这个对象进行更改操作后,会对外部造成影响。需要注意!如果我们调用函数时不想对外面的对象造成影响,则可以使用深拷贝,拷贝一个对象在内部使用。

3. 坑2:类初始化时,这个点比较隐蔽,但是道理相同

假如现在我们要管理一些机器,每个机器都有各自的id和一些参数需要管控。我们先创建一个机器类:

class machine():
    def __init__(self, id, parameter=[]):
        self.id = id
        self.parameter = parameter
        
    def add_parameter(self,p):
        self.parameter.append(p)
        
    def remove_parameter(self,p):
        self.parameter.remove(p)
        
photo_A = machine("ASML_01", ['LENS_TEMP', 'AIR_TEMP'])
print(f'photo_A id is {photo_A.id}, photo_A parameter is {photo_A.parameter}')

photo_A.add_parameter('CO2_PRES')
print('after add, the photo_A parameter is {}'.format(photo_A.parameter))
>>>执行结果:
photo_A id is ASML_01, photo_A parameter is ['LENS_TEMP', 'AIR_TEMP']
after add, the photo_A parameter is ['LENS_TEMP', 'AIR_TEMP', 'CO2_PRES']

上面的代码中我们定义了一个机器的类作为模板,他有两个属性,id和参数列表。
同时在类的内部定义了两个方法用于添加和删除参数。通过初始化一个光刻机并调用对象的add方法,OK,一切顺利。
有些时候某些设备的参数一开始不明确,初始化的时候如果我们不传参数列表就容易出问题。如下:

# 在创建两台设备,没有参数
Etch_A = machine('LAM-XR001')
Track_A = machine('TEL_AF02')
print(Etch_A.id, Etch_A.parameter)
print(Track_A.id, Track_A.parameter)
>>>执行结果:
LAM-XR001 []
TEL_AF02 []

可以看到,因为创建的时候没有传入参数列表,对象直接继承父类的参数列表,一个空列表。这是一个非常危险的动作。此时,如果对其中一个对象的参数进行操作会同时影响另一个对象!如下:

Etch_A.add_parameter('MaxFlow')
print('after add, Etch_A parameter is: {}'.format(Etch_A.parameter))
print('after add, Track_A parameter is: {}'.format(Track_A.parameter))

print(f'id Etch_A.parameter is {id(Etch_A.parameter)} ')
print(f'id Track_A.parameter is {id(Track_A.parameter)} ')
>>>执行结果:
after add, Etch_A parameter is: ['MaxFlow']
after add, Track_A parameter is: ['MaxFlow']
id Etch_A.parameter is 2710193692416 
id Track_A.parameter is 2710193692416 

可以看到我们虽然只对Etch_A这台设备进行操作,但同时影响了另外一台设备,这个是需要避免的。究其原因是因为这两个设备的参数列表指向了同一个对象,我们可以看到他们的id是相同的。在实际操作中,我们可以通过在初始的时候传入一个空的列表来避免。对不同的设备,即使都是传入一个空的列表,初始化的时候也会为每个对象单独开辟一个地址来存放。这样设备之间的操作就没有影响了。如下:

# 在创建两台设备,没有参数,但是传入一个空的列表作为参数
Etch_B = machine('LAM-XR002', [])
Track_B = machine('TEL_BF02',[])
print(Etch_B.id, Etch_B.parameter,id(Etch_B.parameter))
print(Track_B.id, Track_B.parameter,id(Track_B.parameter))
print('----' *10)
Track_B.add_parameter('PR_weight')
print('after add, Track_B parameter is: {}'.format(Track_B.parameter))
print('after add, Etch_B parameter is: {}'.format(Etch_B.parameter))
>>>执行结果:
LAM-XR002 [] 2710193691200
TEL_BF02 [] 2710193692224
----------------------------------------
after add, Track_B parameter is: ['PR_weight']
after add, Etch_B parameter is: []

可以看到,在初始化的时候,虽然两台设备都是传入一个空的列表作为参数,但是这两个空列表的id不一样,所以是两个对象。对其中一个对象进行操作,不会影响另外一个!

4. 坑3:列表的append, extend等方法没有返回值!他们直接在原列表上修改

其实这一点根本原因也是因为列表是可变对象,可以对他的值就行直接修改,而不必创建一个新的对象。也因为此我们千万不能有下面这种写法:

a = [1,2,3]
b = a.append(4)

原意是想把列表a增加一个元素4后赋值给b, 这样写不可能得到正确的结果,因为append方法没有返回值。也就是说
b = a.append(4)后,b是个None!也就是没有返回值的函数的默认返回值。
与此类似还有extend, remove等方法。使用时需要注意!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容