哈喽大家好,我是咸鱼。

今天咸鱼列出了一些大家在初学 Python 的时候容易踩的一些坑,看看你有没有中招过。

原文:https://www.bitecode.dev/p/unexpected-python-traps-for-beginners

不明显的字符串拼接

Python 在词法分析的时候会把多个字符串自动拼接起来。

data = "very""lazy"
print(data) # verylazy

这个特性可以让我们在声明一个长字符串的时候可以分成多行来写,这样看起来比较优雅。

msg = (
"I want this to be on a single line when it prints "
"but I want it to be broken into several lines in "
"the code"
) print(msg)
# I want this to be on a single line when it prints but I want it to be broken into several lines in the code
msg ="I want this to be on a single line when it prints " \
"but I want it to be broken into several lines in " \
"the code" print(msg)
# I want this to be on a single line when it prints but I want it to be broken into several lines in the code

但初学者往往会忽略这一点,他们在使用包含字符串的列表时把分隔符漏掉,造成了意想不到的字符串拼接。

比如说他们想要声明一个包含域名的列表host。

host = [
"localhost",
"bitecode.dev",
"www.bitecode.dev"
] print(host) # ['localhost', 'bitecode.dev', 'www.bitecode.dev']

但是写成了下面这样。

host = [
"localhost",
"127.0.0.1",
"bitecode.dev" # 这里把逗号忘掉了
"www.bitecode.dev"
] print(host) # ['localhost', 'bitecode.devwww.bitecode.dev']

这是有效的代码,不会触发语法错误,但是解析的时候会把 "bitecode.dev""www.bitecode.dev" 拼接在一起,变成 'bitecode.devwww.bitecode.dev'

sorted() 和 .sort() 傻傻分不清

在 Python 中,大多数函数或方法都会返回一个值。比如说我们要对一个列表里面的内容进行排序,可以使用 sorted() 方法。

# sorted() 方法会返回一个排序后的新列表
numbers = [4, 2, 3]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [2, 3, 4]

我们也可以用列表自带的 .sort() 方法来排序,需要注意的是: .sort() 直接对原有列表进行排序,不会返回任何值。

# .sort() 方法直接对原列表进行排序
numbers = [4, 2, 3]
numbers.sort()
print(numbers) # [2, 3, 4]

但是初学者很容易把 sorted() 的用法用在 .sort() 上,结果发现怎么返回了一个 None。

numbers = [4, 2, 3]
sorted_numbers = numbers.sort()
print(sorted_numbers) # None

list.sort() 修改原列表,它不会返回任何内容。当 Python 可调用对象不返回任何内容时,会得到 None

或者把 .sort() 的用法用在了 sorted() 上。

numbers = [4, 2, 3]
sorted(numbers)
print(numbers) # [4, 2, 3]

不要乱加尾随逗号

我们在创建一个空元组的时候可以用下面的两种方法:

t1 = ()
t2 = tuple()
print(type(t1))
print(type(t2))

在 Python 中,虽然元组通常都是使用一对小括号将元素包围起来的,但是小括号不是必须的,只要将各元素用逗号隔开,Python 就会将其视为元组。

t1 = 1,
print(t1) # (1,)
print(type(t1)) # <class 'tuple'>

所以如果在数据后面多加了一个逗号,就会产生一些问题。

比如说下面是一个列表:

colors = [
'red',
'blue',
'green',
]
print(colors) # ['red', 'blue', 'green']

如果不小心加了一个尾随逗号,列表就变成了元组。

colors = [
'red',
'blue',
'green',
],
print(colors) # (['red', 'blue', 'green'],)

在 python 中,包含一个元素的元组必须有逗号,比如下面是包含一个列表的元组:

colors = [
'red',
'blue',
'green',
],

这是列表:

colors = ([
'red',
'blue',
'green',
])

可怕的 is

在 python 中, is 和 == 都是用来比较 python 对象的,区别是:

  • is 比较需要对象的值和内存地址都相等
  • == 比较只需要对象的值相等就行了

事实上,这两者的实际使用要远远复杂的多。

比如说下面的 a 和 b 是两个不同的对象,a is b 应该返回 False,但是却返回了 True。

a = 4
b = 4 print(a == b) # True
print(a is b) # True

在 python 中,由于小整数池和缓存机制,使用 is 来比较对象往往会出现意想不到的结果。

关于小整数池和缓存机制可以看我这篇文章:

《Python 中 is 和 == 的区别》

奇怪的引用

在Python中,如果 * 运算符用于数字与非数字型数据(列表、字符串、元组等)的结合,它将重复非数字型数据。

print("0" * 3) # '000'
print((0,) * 3) # (0, 0, 0)

在创建一个多个列表元素的元组时候,如果使用下面的代码:

t1 = ([0],) * 3
print(t1) # ([0], [0], [0])

会带来意想不到的问题:我们对元组中的第一个列表元素中的数据进行算数运算(自增 1)

t1[0][0] += 1
print(t1) # ([1], [1], [1])

我们发现元组中的所有列表元素内的数据都自增 1 了,我们不是只对第一个列表元素进行自增的吗?

实际上,当我们执行 t1 = ([0],) * 3 的时候,不会创建一个包含三个列表组成的元组,而是创建一个包含 3 个 引用的元组,每个引用都指向同一个列表。

元组中的每个元素都是对同一个可变对象(列表)的引用,所以当我们修改其中的元素时,另外的对象也会跟着发生变化。

正确的方法应该是:

t2 = ([0], [0], [0])
# 或者 t2 = tuple([0] for _ in range(3))
t2[0][0] += 1
print(t2) # ([1], [0], [0])

在 python 的其他地方中也有这种类似的坑:

def a_bugged_function(reused_list=[]):
reused_list.append("woops")
return reused_list print(a_bugged_function()) # ['woops']
print(a_bugged_function()) # ['woops', 'woops']
print(a_bugged_function()) # ['woops', 'woops', 'woops']

可以看到,reused_list 在函数定义中被初始化为一个空列表 [],然后每次函数调用时都使用这个默认的空列表。

在第一次调用 a_bugged_function() 后,列表变成了 ['woops']。然后,在第二次和第三次调用中,它分别继续被修改,导致输出的结果为:

['woops']
['woops', 'woops']
['woops', 'woops', 'woops']

这是因为在函数定义中,如果将可变对象(例如列表)作为默认参数,会导致该对象在函数调用时被共享和修改:每次调用函数时,使用的都是同一个列表对象的引用。

为了避免这种情况,常见的做法是使用不可变对象(如 None)作为默认值,并在函数内部根据需要创建新的可变对象。

def a_fixed_function(reused_list=None):
if reused_list is None:
reused_list = []
reused_list.append("woops")
return reused_list print(a_fixed_function())
print(a_fixed_function())
print(a_fixed_function())

Python 初学者容易踩的坑的更多相关文章

  1. Python网络编程踩的坑

    错误:socket.error: [Errno 10013] 原因:端口号被占用 解决:换其他的端口号或者将其他应用的端口号关闭 错误:File "D:/pyworkspace/homewo ...

  2. python绘图踩的坑

    踩的坑 pyecharts安装地图包 pip install echarts-countries-pypkg 报错Unknown or unsupported command 'install' 这可 ...

  3. python——pyinstaller踩的坑 UnicodeDecodeError

    程序本身运行没任何毛病,奈何用pyinstaller -w xx.py的时候提示——UnicodeDecodeError: 'ascii' codec can't decode byte 0xb3 i ...

  4. 关于python数据序列化的那些坑

    -----世界上本来没那么多坑,python更新到3以后坑就多了 无论哪一门语言开发,都离不了数据储存与解析,除了跨平台性极好的xml和json之外,python要提到的还有自身最常用pickle模块 ...

  5. Python初学者之网络爬虫(二)

    声明:本文内容和涉及到的代码仅限于个人学习,任何人不得作为商业用途.转载请附上此文章地址 本篇文章Python初学者之网络爬虫的继续,最新代码已提交到https://github.com/octans ...

  6. 从零开始学 Java - Spring 支持 CORS 请求踩的坑

    谁没掉进过几个大坑 记得好久之前,总能时不时在某个地方看到一些标语,往往都是上面一个伟人的头像,然后不管是不是他说的话,下面总是有看起来很政治正确且没卵用的屁话,我活到目前为止,最令我笑的肚子痛得是下 ...

  7. webuploader插件,我踩得坑

    我在目前的公司做的项目要么是原生写法去做项目,要么就是vue+webpack做项目,但是vue这部分只是用了模板template,vue其他的都没用. 有一个项目需要做上传图片的功能,老大扔给我一个插 ...

  8. 谈谈调用腾讯云【OCR-通用印刷体识别】Api踩的坑

    一.写在前面 最近做项目需要用到识别图片中文字的功能,本来用的Tesseract这个写的,不过效果不是很理想. 随后上网搜了一下OCR接口,就准备使用腾讯云.百度的OCR接口试一下效果.不过这个腾讯云 ...

  9. Asp.Net Core中使用Swagger,你不得不踩的坑

    很久不来写blog了,换了新工作后很累,很忙.每天常态化加班到21点,偶尔还会到凌晨,加班很累,但这段时间,也确实学到了不少知识,今天这篇文章和大家分享一下:Asp.Net Core中使用Swagge ...

  10. Python初学者的一些编程技巧

    #####################喜欢就多多关注哦######################### Python初学者的一些编程技巧   交换变量  ? 1 2 3 4 5 6 7 8 9 ...

随机推荐

  1. LeetCode刷题日记 2020/08/18

    给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: [-10 ...

  2. 将Windows系统设置为NTP服务器

    环境 Windows 10,本机IP地址为:192.168.6.133 步骤 一.编辑注册表 win+r快捷键打开运行窗口,输入regedit打开注册表编辑器,找到以下几项并修改其值: 1.将type ...

  3. 学生成绩管理系统|Python小应用练习

    题目要求 实现学生成绩管理系统 输入学生成绩信息序列,获得成绩从高到低.从低到高.按某一门成绩的排列,相同成绩都按先录入排列在前的规则处理. 数据如下:(数据规则:学生姓名 高数成绩 英语成绩 大物成 ...

  4. 【链表】双向链表的介绍和基本操作(C语言实现)【保姆级别详细教学】

    双向链表 文章目录 前言 双向链表的基本介绍 一些链表的分类 带头双向循环链表的基本结构 双向链表的实现 结点的定义.头指针的创建 开辟结点接口 初始化头结点接口 打印接口 尾插接口 尾删接口 头插接 ...

  5. 求助:Docker怎么连接mongoDB?

    首先 在playwithDocker中通过docker pull mongo引入mongo 然后创建容器 docker run -it --name mymongo -p 27017:27017 -v ...

  6. 快速上手typescript(基础篇)

    壹 ❀ 引 在javascript开发中,你可能也遇到过我这样的苦恼,在维护某段几年前的老旧代码时,我发现了某个数据加工方法fn,而且根据现有逻辑来看fn的某个参数是一个数组,因为新需求我需要对数组做 ...

  7. GaussDB(for MySQL) Serverless全面商用:无感弹性,极致性价比

    本文分享自华为云社区<GaussDB(for MySQL) Serverless全面商用:无感弹性,极致性价比>,作者: GaussDB 数据库. 技术背景 对于现代企业级IT系统,数据库 ...

  8. 2024年,提升Windows开发和使用体验的实践经验 - RIME输入法

    前言 上一篇文章介绍了 Windows 下的包管理器,本文继续介绍输入法. 事实上 Windows 的输入法生态比 Linux/Mac 丰富很多,不过很多国产输入法存在窃取隐私.植入广告.乱安装流氓软 ...

  9. Shiro实战2-Springboot集成Shiro实战

    说明 最近打算打造一个简易的权限管理系统,打算用shiro做认证和授权.本篇作为springboot集成shiro的入门实战案例记录下来,希望也可以帮到大家,代码整理自网络. 技术栈 springbo ...

  10. Java Swing实现五子棋程序

    首先感谢08年MLDN出的这个培训视频,我把代码和文档整理了一下,发布出来给需要学习Swing的朋友. 源码地址: https://gitee.com/indexman/gobang 一.知识点准备: ...