python函数运行没有结果_Python中常见的9大坑,看看你有没有遇到!

原标题:Python中常见的9大坑,看看你有没有遇到!

Python作为一门简洁且容易上手的语言,正在受到越来越多人的喜爱。但如果你对其中的一些细节不甚了解,就有可能掉入它的“坑”里。本文将介绍学习Python过程中遇到的一些问题,接下来看看Python里一些常见的坑。

a062c57acf5e4d349eb9cd9d986024eb.png

01

三元表达式

43bf6751639a4fb98063d9b256550c93.jpeg

乍一看,按照根深蒂固的四则运算的思维,加号之前是一部分,加号之后为另一部分,结果貌似等于10。为什么打印出来的结果跟我们预想的大相径庭呢?很显然,Python解释器在遇到三元表达式时,默认把if之前的(10+4)作为三元表达式的前面部分了。

02

嵌套列表的创建

要创建一个嵌套的列表,我们可能会选择这样的方式:

75570c0d8f0142dabbf98e0cf7cbe6fa.jpeg

目前看起来一切正常,我们得到了一个包含3个list的嵌套list。接下来往第一个list中添加一个元素:

c5410c14f984487ca0213d3df0819188.jpeg

奇怪的事情发生了,我们只想给第一元素增加元素,结果三个list都增加了一个元素。这是因为[[]]*3并不是创建了三个不同list,而是创建了三个指向同一个list的对象,所以,当我们操作第一个元素时,其他两个元素内容也会发生变化。下面的代码可以证实这一点:

574930a6a18f4be3b1e0226c85c78556.jpeg

正确的做法如下:

61ecfc1813ef4b5285f9148479829258.jpeg

03

try...finally + return

看下面这段代码,可以试想一下print语句打印的顺序:

d13da059d45c44e686ac36980e538089.jpeg

是不是很多小伙伴看到return就对print的顺序感到不知所措了,下图是最终的结果:

0222cb8d733344828494f556d5cc260a.jpeg

我们先来看一段Python官网对于finally的解释:

50619be620a74ec581f95bb410dcb2b6.jpeg

翻译成中文就是try块中包含break、continue或者return语句的,在离开try块之前,finally中的语句也会被执行。所以在上面的例子中,try块return之前,会执行finally中的语句,最后再执行try中的return语句返回结果。看到这里小伙伴们都豁然开朗了吧。

04

参数默认值

当我们把函数的参数默认值设置为列表时,会发生什么?

6a5ca86079d74bfc83f4833743d80a63.jpeg

出现以上情况的原因是:默认值在定义函数时计算(通常在加载模块时),因此默认值变成了函数对象的属性。具体来说,函数的参数默认值保存在函数的__defaults__属性中,指向了同一个列表。因此,如果默认值是可变对象,而且修改了它的值,那么后续的函数调用都会受到影响。正确的做法是设置该参数默认为None。

05

lambda自由参数之坑

先来看这样一段代码:

58bc679fe432402f9aa1c94b5b859309.jpeg

结果并不是0,2,4,6,8,而是8,8,8,8,8。不少人可能会觉得匪夷所思,不着急,先试着用dis库分析字节码。

6b59d447b8524002aa477dda519e14ee.jpeg

856e7f40eb3a47539be865f40739cfe5.jpeg

没有得到想要的结果,只能看到参数i和x,参数i的具体值无法获取。这也就是说lambda函数在定义的时候只知道有一个i,而他的值并不明确,之后通过计算获取i的值。到这里很容易联想到闭包,因为i引用了“for i in range(5)”这个表达式中的值。先复习一下“闭包”的定义:闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。接下来验证一下,我们通过f.__code__.co_freevars来获取自由变量的名称,通过f.__closure__[0].cell_contents得到自由变量的值:

117d4d5bf596439dbd9e3af22e74f29c.jpeg

5d077b64b6f3417284d43c593ef088ae.jpeg

果不其然,自由变量i最终的值都是4,这也就解释了最开始的结果。如果还不明白可以看下面这段代码。

ec2b5bcc67584afbaa460fe5c4c0bdfc.jpeg

Python程序从上到下执行,同时它也是一门动态型的语言,举个例子,定义一个类之后,你可以动态的给它增加方法。同样,上面这个例子中,程序执行到最后i的值为5,所以lambda表达式中i为5,最终的结果为:[10, 10,10, 10, 10, 10]。

要解决上述出现的问题,就要把闭包作用域变为局部作用域:

a = [lambda x, i=i: i*x for i in range(5)]。这行代码等效于下面这种写法:

5acaa834156544f8ab95be9922011e60.jpeg

“纸上得来终觉浅,绝知此事要躬行”。

06

含单个元素的元组

70aff890090048baa0c8bdfcca7a08d7.jpeg

上图有两行新建元组的代码,但只有第二种写法是正确的。因为在唯一的元素之后不加逗号,小括号对于Python解释器是无效的。

07

对象销毁顺序

创建一个类OBJ:

74665210df524c19af8cf77ddc3b0151.jpeg

创建两个OBJ示例,使用is判断是否为同一对象:

60d7380bc5bc4897a5d745b709b4fed3.jpeg

72f0913baac246f883e8c59b7589043e.jpeg

接下来同样创建两个对象,使用id来判断。

18aa1161422d4e71887ad6113058504f.jpeg

611e9d5e53ff43d189fca486cd8a2608.jpeg

调用id函数, Python 创建一个OBJ类的实例,并使用id函数获得内存地址后,销毁内存丢弃这个对象。当连续两次进行此操作, Python会将相同的内存地址分配给第二个对象,所以两个对象的id值是相同的。但是is行为却与之不同,通过打印顺序就可以看到。

08

了解执行时机

请看下面这个例子:

e4a59c66dc364734aa0eccaff665a5ea.jpeg

结果并不是[1, 3, 5]而是[5],这有些不可思议。原因在于,in子句在声明时执行, 而条件子句则是在运行时执行。所以上图中的生成器等价于:

c6652f84ea2042b3a3ff25fb6523cbbb.jpeg

09

相同值的不可变对象

23b16c03e5d44ef3896ffe748af353af.jpeg

c87b8b9f6ab74afab4d14b7da33156e8.jpeg

可以看到,key=1,value=’a’的键值对神奇地消失了。这里不得不说一下Python字典是使用哈希表的思想实现的,Python 调用内部的散列函数,将键(Key)作为参数进行转换,得到一个唯一的地址,也就是哈希值。而Python 的哈希算法对相同的值计算得到的结果是一样的,这就很好地解释了上述情况出现的原因。

本文列出了在Python学习或者工作中可能会遇到的一些“坑”,虽然不见得每个人都能遇到上述问题,但是可以作为一个参考,以后就能避免踩坑了。希望小伙伴在跟Python打交道的过程中能多注意细节,甚至去了解一些内部实现的原理,这样才能更好地掌握Python这门语言。返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值