Python避坑指南(续)

简介: 本文收集整理了所有Python编程中可能遇到的坑,教你如何避开这些坑,写出健壮、高效的Python代码。

在上一篇《Python避坑指南》中,我重点给大家讲了Python可变容器数据类型中的坑。除了这些,Python还有其他一些细小方面的坑,本章为大家讲解Python中这些大家可能会忽视的细节。

Newbie_Watermarked.png

lambda的坑

看下面的代码,思考一下会输出什么?

def m():
    return [lambda x:x*i for i in range(4)]

a = m()
for a in m():
    print(a(1))

你可能以为会输出:

0
1
2
3

正确答案是:

3
3
3
3

这是因为在 [lambda x:x*i for i in range(4)] 列表解析式中,i在lambda声明之外,也就是说i相对lambda来说是个外部变量。当列表解析式运行完后,i的值就定格为3。

我们可以通过简单的方法来验证:

for a in m():
    print(a.__code__.co_code)
    print(a(1))

从上面的代码输出你会发现,对a的内部代码输出都是一样的,这说明列表中的lambda的逻辑都是一样的。所以输出结果也是一样的。

链式or的坑

Python支持简化链式逻辑判断。比如:

if a > 1 and a < 3:

可以简化成:

if 1 < a < 3:

当多个相等判断连接时,比如

if a == 3 or b == 3 or c == 3:

可能有人会简化成这样:

if a or b or c == 3: # !这是错的!

注意:上面的代码是错的!因为or==优先级要低。上面表达式的执行顺序是 if (a) or (b) or (c == 3):

替代链式or的更好的方法是用系统内置的any()方法。

if any([a == 3, b == 3, c == 3]): # 正确

如果嫌上面代码太重复,编码不够高效,可以优化成下面这种写法:

if any(x == 3 for x in (a, b, c)): # 正确

如果比较的值都是相同的,还可以进一步简化为:

if 3 in (a, b, c): # 正确

这里我们用in判断要比较的值是否在待比较的变量构成的元组中。

同理,下面这种写法也是不对的:

if a == 1 or 2 or 3:

应该这样写:

if a in (1, 2, 3):

访问字面量属性的坑

Python中一切皆对象,即便是字面量也是对象。例如7,在Python中也是对象。这也就意味着7也有属性和方法。例如bit_length()这个方法,它会返回表示这个值所需的二进制位数。

x = 7
x.bit_length()
# Out: 3

上面的代码是可以正确输出的。应为7的二进制是111,需要3位二进制来表示,所以bit_length()会返回3。你可能直观地感觉7.bit_length()也一样会返回3。但不幸的是你会得到SyntaxError。为什么会这样?这是因为Python中.有两重含义,即可以是访问对象的属性,也可以是表示浮点数。Python解析器需要区分到底是哪一种含义。7.bit_length()7.2 解析器无法区分,因此会报语法错误。

有两种办法可以直接访问字面量的属性:

(7).bit_length() # 用括号将字面量括起来,告诉解析器这里7不是个浮点数
7 .bit_length()  # 7后面加个空格,告诉解析器这里7不是个浮点数

注意:这里加两个点7..bit_length是不对的。这样写第一个点会被理解为浮点数,第二个点会访问对象的属性。虽然语法上没有歧义,但是浮点数是没有bit_length()方法的。这里如果访问的是浮点数对象有的属性或方法,程序是可以正常运行的。

7..as_integer_ratio()
# Out: (7, 1)

is的坑

编程过程中,整型和字符串是使用最多的数据类型。为了减少整型和字符串频繁创建带来的内存开销,Python会用内部缓存一定范围的整数和字符串。当我们用is判断两个对象是否是同一个对象时,这里的内部缓存机制可能会带来让人迷惑的结果。比如:

>>> -8 is (-7 - 1)
False
>>> -3 is (-2 - 1)
True

再举一个例子:

>>> (255 + 1) is (255 + 1)
True
>>> (256 + 1) is (256 + 1)
False

这里的输出结果着实让人疑惑。-3, 255就返回True,-8, 256就是False。

更具体地说,在[-5, 255]区间内地整型在Python解析器启动时会放入内部缓存。因此用is判断这个区间内的整型是否是同一对象时会返回True。不在这个区间内地整型会在使用时创建,所以即便值相同,但在内存中不是同一对象,因此会返回False。

⚠注意,可能编译器版本不同,内部缓存地范围可能不同。

解决这个问题的方法就是永远用==判断值是否相等,不要用is

⚠注意,在Python交互式运行环境下,用is判断值相等会受到一条警告:

SyntaxWarning: "is" with a literal. Did you mean "=="?

字符串也是同样道理,永远用==判断值相等!

GIL全局锁的坑

GIL全局锁大家可能比较陌生,它跟多线程有关。在处理多线程时,全局锁有时可能会产生疑惑。请看下面这个例子:

import math
from threading import Thread

def calc_fact(num):
    math.factorial(num)

num = 600000
t = Thread(target=calc_fact, daemon=True, args=[num])
print("About to calculate: {}!".format(num))
t.start()
print("Calculating...")
t.join()
print("Calculated")

你可能以为Calculating...会在线程启动后立即打印出来,毕竟我们将calc_fact()这个比较耗时的运算放到了线程中执行。但实际上他会在计算完成后才打印。这是因为math.factorial()背后是C语言实现,线程在执行C语言实现函数时会锁住GIL直到运行结束。

有多种方法可以绕开这个问题。

第一种方法,你可以用纯Python来实现factorial的功能。

def calc_fact(num):
    """ 纯Python实现阶乘 """
    res = 1
    while num >= 1:
        res = res * num
        num -= 1
    return res

这样做的弊端就是运行速度变慢,因为我们不再使用C语言实现的阶乘函数。

第二种方法,你可以在调用C函数前休眠一下。

def calc_fact(num):
    sleep(0.001)
    math.factorial(num)

注意:这里的休眠不会影响C函数的执行,只是让主线程有机会向下执行。

多数据返回的坑

Python允许函数返回多个数据,比如下面的函数xyz就返回了2个值:

def xyz():
    return a, b

Python的这个特性很方便,当我们使用时可以用两个变量承接返回值

a, b = xyz()

但是如果用一个变量来承接多返回值,

t = xyz()

python也是允许的,只不过t的数据类型是个元组(a, b),不是函数返回的第一个值。这里大家要格外注意。

JSON中的坑

JSON是我们日常开发中用到最多的数据类型,也是前后端传输数据最常用的数据类型。但是Python对json的处理跟javascript不同,这会让很多前端转型Python开发的同学不适应。我们看下面这个例子:

my_var = 'bla'
my_key = 'key'

params = {
   
   "language": "en", my_var: my_key}

上面的代码如果在javascript中,params的内容为:

{
   
   
    "language": "en",
    "my_var": "key"
}

而在python中,param的内容为:

{
   
   
    "language": "en",
     "bla": "key"
}

在Python中,字典中的my_varapi_key会被当做变量来求值。

目录
相关文章
|
6月前
|
存储 IDE 测试技术
探索Python中的变量宇宙:详尽解读与实战避坑指南
【4月更文挑战第4天】本文深入探讨了Python变量,包括其声明与赋值、数据类型,以及常见错误和应对策略。通过实例展示了变量在存储用户输入、计算、控制流程和函数参数等方面的应用。强调了理解变量作用域、避免类型不匹配和未初始化的错误,同时提出了最佳实践,如明确命名、避免冗余、适时复用和使用类型提示,以提升编程效率和代码质量。
71 2
|
关系型数据库 MySQL Linux
Python安装mysqlclient报错避坑
MySQL是常用的开源数据库,Python环境下django框架连接MySQL数据库用的是mysqlclient库,今天在用pip安装mysqlclient库时报错,特记录一下,避免后续继续踩坑。
691 1
|
6月前
|
JSON 数据挖掘 数据库
Python复合型数据避坑指南
Python复合型数据避坑指南
76 3
|
6月前
|
人工智能 Python
【Python + 微信】微信公众号开发避坑指南
【Python + 微信】微信公众号开发避坑指南
121 0
|
安全 程序员 C++
Python避坑指南
本文收集整理了所有Python编程中可能遇到的坑,教你如何避开这些坑,写出健壮、高效的Python代码。
99 0
Python避坑指南
|
机器学习/深度学习 IDE 开发工具
python机器学习入门之opencv的安装与环境配置(超详细,避坑必看,包括没代码提示的解决)
python机器学习入门之opencv的安装与环境配置(超详细,避坑必看,包括没代码提示的解决)
366 0
python机器学习入门之opencv的安装与环境配置(超详细,避坑必看,包括没代码提示的解决)
|
Python C++ 数据挖掘
Python初级能力错题避坑——William
Python初级能力错题避坑——William
442 0
|
存储 JSON 数据格式
Python配置文件总结与避坑指南
最近拿python在写项目部署的相关集成代码,本来两天的工作量,硬是在来回的需求变更中,拖到了一周的时间。今天算是暂时告一段落了。这次由于涉及多个系统的调用和配置参数,代码开发中出现了较多之前未发现或者说没暴露出来的问题,今天针对这些知识点进行相关的复习与总结
281 0
|
5天前
|
存储 数据挖掘 开发者
Python编程入门:从零到英雄
在这篇文章中,我们将一起踏上Python编程的奇幻之旅。无论你是编程新手,还是希望拓展技能的开发者,本教程都将为你提供一条清晰的道路,引导你从基础语法走向实际应用。通过精心设计的代码示例和练习,你将学会如何用Python解决实际问题,并准备好迎接更复杂的编程挑战。让我们一起探索这个强大的语言,开启你的编程生涯吧!
下一篇
无影云桌面