一道99% 人会做错的Python题

还有人会选第三个吧,[1] [1, 1]。能选第三个的已经很强了,至少了解列表是可更改对象(mutable object),作为函数的默认参数(default argument)每次会更新参数的默认值。很棒,但是忽略了 print() 函数的运行机制。

答案是第四个, [1, 1] [1, 1]。

如果你选了第四个,你可以关闭页面,如果不是,请往下看,绝对能学到一些新的知识。你能需要的知识点是:

  • 了解什么是不可更改对象和可更改对象

  • 了解函数的默认参数

  • 了解在 Python 3 里面 print() 是函数而不是语句(在 Python 2 里是语句)

不可更改对象 VS 可更改对象

判断一个数据类型 X 是不是可更改的呢?两种方法:

  • 方法一:用 id(X) 函数,对 X 进行某种操作,比较操作前后的 id,如果不一样,则 X 不可更改,如果一样,则 X 可更改。

  • 方法二:用 hash(X) 函数,只要不报错,证明 X 可被哈希,即不可更改,反过来不可被哈希,即可更改。

我们用方法一,id() 函数,来验证整数和列表是否可更改。

先看整数 i:

i = 1

print( id(i) )

i = i + 2

print( id(i) )

‘’’

1607630928

1607630992

‘’’

整数 i 在加 1 之后的 id 和之前不一样,因此加完之后的这个 i (虽然名字没变),但是不是加前的那个 i 了,因此整数是不可更改的。

下图给上述过程做了可视化,在 Python 中,给 i 赋值 1 其实是创建一个 PyObject(有个字段存储的值为 1),然后将变量 i 指向这个 PyObject。当更新 i = i + 2 时,其实是新创建了个 PyObject(有个字段存储的值为 3),而将变量 i 指向新的 PyObject,旧的 PyObject 最后会被“回收”。

从新建 PyObject 这个特点可看出,整数不能更改。

在这里插入图片描述

再看列表 l:

l = [1, 10.31]

print( id(l) )

l.append(‘Python’)

print( id(l) )

‘’’

2022027856840

2022027856840

‘’’

列表 l 在附加 ‘Python’ 之后的 id 和之前一样,因此列表是可更改的。

下图给上述过程做了可视化。我们发现,列表作为容器型数据,它本身的 PyObject,在添加或者删除元素的时候,没有改变。换句话说,列表是可更改的。

在这里插入图片描述

函数默认参数

先回顾一下题目中的代码:

def f( x=[] ):

x.append(1)

return x

在函数 f 中,x 是默认参数,默认值是空列表

在 Python 中

默认参数值只能被初始化一次


如果使用可更改对象作为默认参数,那么被更改后的值将一直保留。

那么下面代码的运行结果就好理解了吧(注意我先用两个 print 函数打印 f() 值)。

print( f() )

print( f() )

‘’’

[1]

[1, 1]

‘’’

在运行第一行代码时,没有给参数值,则用其默认值 [],然后添加元素 1,结果是 [1],没任何问题。

在运行第二行代码时,也没有给参数值,还是用其默认值,但这个时候默认参数的类型是可更改的列表,它在第一次运行函数 f() 的时候已经变成了 [1],而这个 [1] 就更新为默认值了。再添加元素 1,结果为 [1, 1]。

So far so good,那为什么两个 f() 一起打印出来会得到 [1,1] [1,1] 呢?

print( f(), f() )

‘’’

[1, 1] [1, 1]

‘’’

这就需要了解一下 print() 函数的细节了。

print() 函数

在 Python 3 中,print() 是个函数 (function) 而不是语句 (statement)。因此 print() 函数的所有参数要

在调用函数前先被估值


因此

print( f(), f() )

等价于

x1 = f()

x2 = f()

print( x1, x2 )

解释如下:

  • 第一次调用 f() 产生 PyObject 并赋值给 x1 时,x1 指向 PyObject 而且其值为 [1]

  • 第二次调用 f() 赋值给 x2 时,PyObject 里的值更新为 [1, 1],而 x1 和 x2 指向它,因此两个值都更新为 [1, 1]

用一张图可视化下上述过程:

在这里插入图片描述

用代码验证一下,注意 id(x1) 和 id(x2) 一样,就是说 x1 和 x2 指向同一个 PyObject。

x1 = f()

x2 = f()

print( id(x1), id(x2) )

print( x1, x2 )

‘’’

2457681941960 2457681941960

[1, 1] [1, 1]

‘’’

由于我们在调用 print() 函数前就完成了对 x1 和 x2 的评估,所以它们的值都为 [1, 1]。

如果分开调用 print(f()) 呢?那么结果就是 [1] [1,1] 了。看代码:

print( f() )

print( f() )

‘’’

[1]

[1, 1]

‘’’

为什么结果不是 [1, 1] [1, 1] 呢?看下面的等价代码先:

x1 = f()

print( x1 )

x2 = f()

print( x2 )

‘’’

[1]

[1, 1]

‘’’

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)

3-1713731363392)]

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值