一道99% 人会做错的Python题

2023-10-21 14:59
文章标签 python 99% 一道 人会

本文主要是介绍一道99% 人会做错的Python题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。

作者:王圣元

PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取

python免费学习资料以及群交流解答点击即可加入


题目:在不运行下面代码(Python 3 不是 Python 2)的情况下选择答案。


def f( x=[] ):x.append(1)return xprint( f(), f() )

上面代码运行结果(单选)(单选)

A. [] []

B. [1] [1]

C. [1] [1, 1]

D. [1, 1] [1, 1]

很多人选第二个吧,[1] [1]。理由如下:一开始 x 是空列表,添加一个 1 不就是 [1] 吗?然后函数 f() 运行两遍。

还有人会选第三个吧,[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]
'''

不难发现,我们在评估 x2 之前就把 x1 的值 [1] 打印出来了,在评估 x2 之后 x1 也更新成 [1,1] 但是没打印出来,不过我们可以验证一下是不是这样。

x1 = f()
print( x1 )x2 = f()
print( x2 )print( x1 )
'''
[1]
[1, 1]
[1, 1]
'''

到现在你应该明晰所有难点了吧。再回到开始,其实我们就是希望这个函数就是在传入参数的列表上添加一个元素 1,那么怎么操作呢?用 None !

None

None 和整数、浮点数、布尔一样,是一种数据类型,而且不可更改,它的类型是 NoneType。


type(None)'''
NoneType
'''

正因为它的不可更改性质,如果你在函数中需要传入一个默认参数值,用 None !

因此上面代码更改成:


def f( x=None ):if x is None:x = []x.append(1)return x

核心点是“如果 x 值是 None ,那么重新给 x 赋值一个空列表 []”。

再运行结果正常。


print( f(), f() )
'''
[1] [1]
'''

总结

你看,一个小题目能引出这么多 Python 的细节知识点(如变量是指针PyObject(不)可更改对象函数默认参数print 函数内部机制NoneType 变量),而且这些知识点很多人都没有深挖过。我觉得这个题目作为面试题挺合适的,不要求你能完全做对,但在分析的过程可以检查你对基本知识点的理解有多深。

这篇关于一道99% 人会做错的Python题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/255143

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点: 鼠标左键双击,设定红色的起点。左键双击设定起点,用红色标记。 设定终点: 鼠标右键双击,设定蓝色的终点。右键双击设定终点,用蓝色标记。 设置障碍点: 鼠标左键或者右键按着不放,拖动可以设置黑色的障碍点。按住左键或右键并拖动,设置一系列黑色障碍点

Python:豆瓣电影商业数据分析-爬取全数据【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】

**爬取豆瓣电影信息,分析近年电影行业的发展情况** 本文是完整的数据分析展现,代码有完整版,包含豆瓣电影爬取的具体方式【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】   最近MBA在学习《商业数据分析》,大实训作业给了数据要进行数据分析,所以先拿豆瓣电影练练手,网络上爬取豆瓣电影TOP250较多,但对于豆瓣电影全数据的爬取教程很少,所以我自己做一版。 目

【Python报错已解决】AttributeError: ‘list‘ object has no attribute ‘text‘

🎬 鸽芷咕:个人主页  🔥 个人专栏: 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 文章目录 前言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一:检查属性名2.2 步骤二:访问列表元素的属性 三、其他解决方法四、总结 前言 在Python编程中,属性错误(At