了解8大Python小陷阱,深入理解Python

news2025/1/16 16:16:49

学习了解python常见的使用陷阱,避免二次踩坑

在这里插入图片描述

Python是最流行的且适合初学者学习的语言之一。它的语法非常优雅简洁。只要知道python基础知识,阅读代码几无障碍。

然而,就像其他语言一样,Python确实有一些古怪特殊的地方。本文将介绍Python 8大高频陷阱,告诉你冰山之下的世界!

1. 命名空间和作用域

当站在计算机底层观察Python时,有两件事注意:命名空间作用域

命名空间

Python是一种面向对象的编程语言,所有东西都被认为是一个对象。命名空间本质上是一个容器,将对象名称映射到对象。

function_namespace = { 
    name_of_obj_a: obj_1, 
    name_of_obj_b: obj_2 
}

for_loop_namespace = { 
    name_of_obj_a: obj_3, 
    name_of_obj_b: obj_4 
}

可以把命名空间看作是Python字典,其中对象的变量名是键,而值是对象本身。我们每次定义一个循环、一个函数或一个类时,都会创建一个新的、独立的命名空间。每个命名空间都有自己的层次结构,称为作用域

作用域

作用域,是Python解释器可以"看到"所以定义的对象的层次结构。解释器从最小的作用域 (local) 开始,如果找不到声明的变量,就向外寻找封闭的作用域。如果解释器在封闭的作用域中找不到它,它就看向全局作用域

如下代码所示:

i = 1
 
def foo():
   i = 5
   print(i, 'in foo()')
   print("local foo() namespace", locals())
   return i
 
print("global namespace", globals())
foo()

"""
global namespace {'__builtins__': <module 'builtins' (built-in)>, '__spec__': None, 'foo': <function foo at 0x7f7909609f28>, '__file__': 'main.py', '__cached__': None, '__doc__': None, 'i': 1, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f790953ada0>, '__name__': '__main__'}
5 in foo()
local foo() namespace {'i': 5}
"""

代码中一个全局命名空间和一个foo()函数命名空间。可以通过在代码的指定位置打印globals()locals() 来查看各个命名空间的具体内容。

本地命名空间是非常直接的。可以清楚地看到i和它的值5全局命名空间有一点不同,它还包括来自 Python系统内置的一些内容。

在全局命名空间中,foo()函数显示为内存中的一个位置<function foo at 0x7f7909609f28>,而不是函数的返回值,但可以看到变量i的值为1,而不是内存地址。

虽然上面例子中,foo()重新申请了变量i(在本地命名空间中),但也可以在变量名前面使用 global 关键字将其放到全局命名空间中:

i = 1
 
def foo():
   global i
   i = 5
   print(i, 'in foo()')
   print("local namespace", locals())
   return i
 
print("global i before func invocation", globals()["i"])
 
 
foo()
 
print("global i after func invocation", globals()["i"])
"""
global i before func invocation 1
5 in foo()
local namespace {}
global i after func invocation 5
"""

2. 迭代时删除列表项

在Python中处理列表时,我们需要看一下当我们在列表上循环时从列表中删除项目会发生什么。一般来说,由于意外的后果,从一个列表中迭代并删除项目不是一个好主意。以这些例子为例。

del关键字

del关键字只删除本地命名空间中的对象实例,而不是全局命名空间中的对象实例。所以全局定义的list_1不受影响:

list_1 = ["apples", "oranges", "bananas", "strawberries"]
 
for item in list_1:
   del item
 
print("list_1: ",list_1); # ['apples', 'oranges', 'bananas', 'strawberries']
"""
list_1:  ['apples', 'oranges', 'bananas', 'strawberries']
"""

remove()方法

remove()方法中,一旦Python从列表中移除一项,所有其他项将向左移动一次,但是迭代直到所有项都被移动之后才会发生。

list_2 = ["apples", "oranges", "bananas", "strawberries"]
 
for item in list_2:
   list_2.remove(item)
   print(list_2)
 
print("list_2: ",list_2)# ['oranges', 'strawberries']
"""
['oranges', 'bananas', 'strawberries']
['oranges', 'strawberries']
list_2:  ['oranges', 'strawberries']
"""

下面是迭代步骤:

  1. 第1次迭代:删除applesoranges向左移动,现在oranges处于当前迭代索引位置。bananas向左移动,成为下一个索引,即下一次迭代的位置。当然strawberries也向左移动。
  2. 第2次迭代bananas在当前索引,所以方法删除bananasstrawberries向左移动,处于当前迭代索引位置。由于没有更多待迭代索引,循环结束。
  3. 结果: 列表中只剩下orangesstrawberries

pop(idx)方法

与列表循环时不推荐使用remove()方法的原因相同,迭代时也不推荐使用pop(idx)方法。

注意: 如果没有传递idxpop()函数,Python将删除列表中的最后一项。

list_3 = ["apples", "oranges", "bananas", "strawberries"]
 
for item in list_3:
   list_3.pop()
   print(list_3)
 
print("list_3: ",list_3) # ['apples', 'oranges']
"""
['apples', 'oranges', 'bananas']
['apples', 'oranges']
list_3:  ['apples', 'oranges']
"""
  1. 第1次迭代: 删除strawberries,列表长度变为3。继续下一次迭代。
  2. 第2次迭代: 删除bananas,列表长度变为2。没有可迭代索引值,循环结束。
  3. 结果: 列表中只剩下applesoranges

注意:如果索引被传递到pop()方法中,而它并不存在,会引发IndexError异常。

如何迭代时删除了?

上述remove()pop()在迭代时进行列表删除操作没有符合预期,本质上是因为迭代时列表的长度不断的变化。所以一种思路是通过列表切片[:]操作复制一个新的列表,循环时迭代新的列表,但删除操作针对原列表,如下代码:

list_4 = ["apples", "oranges", "bananas", "strawberries"]
 
 
for item in list_4[:]: 
      list_4.remove()  #pop() 这里也可以工作 
 
print("list_4: ",list_4) # [] 

list_4[:]列表切片操作将在内存中复制一个新列表。当迭代时,本质上不是对原列表进行迭代,但在for函数里面进行了删除原列表的操作,所以最终列表list_4删除了所有项!

3. bool有什么问题?

说到布尔bool值,似乎很简单。下面这个混合类型的列表,有多少个布尔值,有多少个整数值?

mixed_type_list = [
  False, 
  4.55, 
  "edu.py2fun.com",
  3, 
  True,
  [],
  False,
  dict()
]
integers_count = 0
booleans_count = 0
 
for item in mixed_type_list:
   if isinstance(item, int): # 优先判断int
       integers_count += 1
   elif isinstance(item, bool): # 其次判断bool
       booleans_count += 1
 
 
print(integers_count)
print(booleans_count)
"""
4
0
"""

为什么输出是40?简而言之,Python中的布尔值是整数int的一个子类。在Python中,True等于1False等于0

4. 类变量和实例变量

Python面向对象中,类是一个模板,实例是一个基于该模板的新对象。如果我们试图改变或混淆对类变量和实例变量的赋值,会发生什么?

class Animal:
   x = "tiger"
 
class Vertebrate(Animal):
   pass
 
class Cat(Animal):
   pass
 
print(Animal.x, Vertebrate.x, Cat.x)
 
Vertebrate.x = "monkey"
print(Animal.x, Vertebrate.x, Cat.x)
 
Animal.x = "lion"
print(Animal.x, Vertebrate.x, Cat.x)
 
a = Animal()
print(a.x, Animal.x)
 
a.x += "ess"
print(a.x, Animal.x)
"""
tiger tiger tiger
tiger monkey tiger
lion monkey lion
lion lion
lioness lion
"""

代码中有三个类: AnimalVertebrateCat。当我们在Animal类中分配一个变量,而其他类是Animal类的子类时,这些子类可以访问Animal类中创建的变量。

在处理类和实例对象时,如果你想改变类的变量,请使用类名。如果想改变实例的变量,请使用实例对象名。

5. 小心默认可变参数

下面代码中nums参数默认为[],并且作为return返回。如果连续多次调用该函数,可能会有一些意想不到的后果:

def num_list(nums=[]):
   num = 1
   nums.append(num)
   return nums
 
print(num_list())
print(num_list())
print(num_list([]))
print(num_list())
print(num_list([4]))
"""
[1]
[1, 1]
[1]
[1, 1, 1]
[4, 1]
"""

前两次调用num_list()时,都会在nums列表中追加一个1。结果是[1, 1]。要重置这个列表,必须在下一次调用时传入一个空的列表[]

6. 相同操作符,不同的故事

Python中的重新赋值经常引发疑惑。比如列表一起使用时,=+= 操作符有两种不同的含义。

# reassignment
a = [1, 2, 3, 4]
b = a
a = a + [5, 6, 7, 8]
 
print(a) # [1, 2, 3, 4, 5, 6, 7, 8]
print(b) # [1, 2, 3, 4]
# extends
a = [1, 2, 3, 4]
b = a
a += [5, 6, 7, 8]
 
print(a) # [1, 2, 3, 4, 5, 6, 7, 8]
print(b) #
"""
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8]
"""

在操作列表时,=运算符只是意味着重新赋值。当b被赋值为a时,创建了一个当时a的副本。当a被重新赋值为a+[5,6,7,8]时,它将原来的a[5,6,7,8]连接起来,创建[1,2,3,4,5,6,7,8]b列表与原来的赋值相比保持不变。

+=运算符,当它涉及到列表时,是extends()方法的一个快捷方式。这样做的结果是列表就地改变,最终ab都是[1, 2, 3, 4, 5, 6, 7, 8]

7. split()方法

split()方法在Python中具有一些独特的属性。看下下面例子:

# case 1
print('         foo '.split(" ")) # ['', '', '', '', '', '', '', '', '', 'foo', '']
# case 2
print(' foo        bar   '.split()) # ['foo', 'bar']
# case 3
print(''.split(' ')) #['']
"""
['', '', '', '', '', '', '', '', '', 'foo', '']
['foo', 'bar']
['']
"""

当给split()方法传递分隔符时,在本例中是(" "),并在任何长度的字符串上使用它,它将在空白处分割。不管你有多少个空白字符,它都会在每个字符上进行分割。

如果没有指明分隔符,Python解释器将把所有重复的空白字符压缩成一个,并在这个字符上进行分割,只留下非空白字符组的分离。

在空白" "字符上分割的空字符''将返回一个以空字符串''为首个元素的列表。

8. 通配符导入

当通配符导入比较省事,但它们有一些特殊细节,经常产生非预期的结果。以此为例:

def hello_world(str):
   return str;
 
def _hello_world(str):
   return str
from helpers import *
hello_world("hello world -- WORKS!")
_hello_world("_hello_world -- WORKS!")

如果试图在这些文件所在目录中运行该程序,hello_world()函数的第一次调用会正常工作。第二个就不太妙了。当使用通配符*导入时,以下划线_开头的函数不会被导入。

对于这些方法,将不得不直接导入函数,或者使用 __all__ 列表允许通配符*导入。

from helpers import *
print(hello_world("hello world -- WORKS!"))
print(_private_hello_world("__all__ -- WORKS!"))

小节

👏 ! 现在你已经知晓Python常见的8大陷阱,希望对你有所启发,避免二次踩坑。

深入理解Python底层实现对了解这些陷阱非常有用。但要真正掌握Python,还有更多的路要走,比如:

  • del 如何删除
  • 字符串使用技巧
  • 子类与父类的关系
  • 动态扩展的字典
  • 以及更多。

关于Python学习指南

学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

包括:Python激活码+安装包、Python web开发,Python爬虫,Python数据分析,人工智能、自动化办公等学习教程。带你从零基础系统性的学好Python!

👉Python所有方向的学习路线👈

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。(全套教程文末领取)

在这里插入图片描述

👉Python学习视频600合集👈

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

在这里插入图片描述

温馨提示:篇幅有限,已打包文件夹,获取方式在:文末

👉Python70个实战练手案例&源码👈

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

👉Python大厂面试资料👈

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

在这里插入图片描述

在这里插入图片描述

👉Python副业兼职路线&方法👈

学好 Python 不论是就业还是做副业赚钱都不错,但要学会兼职接单还是要有一个学习规划。

在这里插入图片描述

👉 这份完整版的Python全套学习资料已经上传,朋友们如果需要可以扫描下方CSDN官方认证二维码或者点击链接免费领取保证100%免费

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1608624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssm062会员管理系统+jsp

会员管理系统 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于会员管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了会员管理系统&#xff0c;它彻底改…

关于二级指针void**的一点问题与思考

前言 这两天写一个高并发内存池的项目时&#xff0c;遇到了一个关于二级指针的问题&#xff0c;剖析清楚后发觉有必要记录一下&#xff0c;这让我加深了对于C/C中指针的理解&#xff08;果然学到老活到老&#xff09;。 问题的分析 在我的内存池项目中&#xff0c;有一个需求…

2024华中杯C题光纤传感器平面曲线重建原创论文分享

大家好&#xff0c;从昨天肝到现在&#xff0c;终于完成了2024华中杯数学建模C题的完整论文啦。 给大家看一下目录吧&#xff1a; 目录 摘 要&#xff1a; 10 一、问题重述 12 二&#xff0e;问题分析 13 2.1问题一 13 2.2问题二 14 2.3问题三 14 三、模型假设 15 四、…

Spring Task 定时任务调度

一、概念 Spring Task 是 Spring 框架的一个组件&#xff0c;它为任务调度提供了支持&#xff0c;使得开发者能够创建后台任务或定期执行的任务。通过 Spring Task&#xff0c;您可以方便地在 Java 应用程序中实现定时任务&#xff0c;比如每天凌晨进行数据同步、每小时执行一…

day02-新增员工

day01 新增员工业务逻辑整理 EmployeeController.java PostMappingApiOperation("新增员工")public Result save(RequestBody EmployeeDTO employeeDTO){System.out.println("当前线程的ID:" Thread.currentThread().getId());log.info("新增员工&a…

2024年华中杯数模竞赛A题完整解析(附代码)

2024年华中杯数模竞赛A题 基于动态优化的太阳能路灯光伏板朝向以最大化能量收集研究摘要问题重述问题分析模型假设符号说明 代码问题一 完整资料获取 基于动态优化的太阳能路灯光伏板朝向以最大化能量收集研究 摘要 随着可再生能源技术的发展&#xff0c;太阳能作为一种清洁的…

2024新版淘宝客PHP网站源码

源码介绍 2024超好看的淘客PHP网站源码&#xff0c;可以做优惠券网站&#xff0c;上传服务器&#xff0c;访问首页进行安装 安装好了之后就可以使用了&#xff0c;将里面的信息配置成自己的就行 喜欢的朋友们拿去使用把 效果截图 源码下载 2024新版淘宝客网站源码

【云计算】云数据中心网络(七):负载均衡

《云网络》系列&#xff0c;共包含以下文章&#xff1a; 云网络是未来的网络基础设施云网络产品体系概述云数据中心网络&#xff08;一&#xff09;&#xff1a;VPC云数据中心网络&#xff08;二&#xff09;&#xff1a;弹性公网 IP云数据中心网络&#xff08;三&#xff09;…

MySQL数据库-优化慢查询

1、什么是慢查询&#xff1f; 慢查询就是SQL执行时间过长&#xff0c;严重影响用户体验的SQL查询语句。当它频繁出现时数据库的性能和稳定性都会受到威胁 慢查询是数据库性能瓶颈的常见原因&#xff0c;是指SQL执行时间超过阈值&#xff1b;可能由于复杂的连接、缺少索引、不恰…

保持领先:四个ChatGPT小技巧助你成为不可替代的数据分析师

在前文中&#xff0c;我们初步探讨了为何ChatGPT无法完全替代数据分析师的原因&#xff0c;而本文将深入探讨如何利用GPT辅助数据分析师提升工作效率。 **场景一&#xff1a;SQL数据提取** 许多数据分析师需使用SQL语言从数据库中抽取数据。尽管SQL操作简便&#xff0c;但编写…

类和对象-封装-设计案例1-立方体类

#include<bits/stdc.h> using namespace std; class Cube{public://设置长void setL(int l){m_Ll;} //获取长int getL(){return m_L;}//设置宽 void setW(int w){m_Ww;}//获取宽 int getW(){return m_W;}//设置高 void setH(int h){m_Hh;}//获取高int getH(){return m_H;…

在线拍卖系统|基于Springboot的在线拍卖系统设计与实现(源码+数据库+文档)

在线拍卖系统目录 基于Springboot的在线拍卖系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、前台&#xff1a; 2、后台 用户功能模块 5.2用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a…

VR全景展览——开启全新视界的虚拟展览体验

随着VR技术的不断发展和成熟&#xff0c;VR全景展览已经成为现代展览行业的一大亮点。通过模拟现实世界的场景&#xff0c;VR全景展览为用户提供了一个沉浸式的观展体验&#xff0c;使参观者能够跨越地理和时间限制&#xff0c;探索不同领域的展览。 一、VR全景展览的功能优势 …

合并有序表 (顺序存储 和 链式存储 方式实现)

代码详细解析: 合并有序表文章浏览阅读1.4k次&#xff0c;点赞6次&#xff0c;收藏7次。●假设有两个有序表 LA和LB , 将他们合并成一个有序表LC●要求不破坏原有的表 LA和 LB构思:把这两个表, 合成一个有序表 , 不是简简单单吗?就算是把他们先遍历不按顺序插入到表 C里面 , …

电机控制专题(二)——Sensorless之扩展反电动势EEMF

文章目录 电机控制专题(二)——Sensorless之扩展反电动势EEMF前言理论推导仿真验证总结参考文献 电机控制专题(二)——Sensorless之扩展反电动势EEMF 前言 总结下电机控制中的扩展反电动势模型。 纯小白&#xff0c;如有不当&#xff0c;轻喷&#xff0c;还请指出。 在得出E…

C#自定义窗体更换皮肤的方法:创建特殊窗体

目录 1.窗体更换皮肤 2.实例 &#xff08;1&#xff09;图片资源管理器Resources.Designer.cs设计 &#xff08;2&#xff09;Form1.Designer.cs设计 &#xff08;3&#xff09;Form1.cs设计 &#xff08;4&#xff09; 生成效果 &#xff08;5&#xff09;一个遗憾 1.窗…

智能化新浪潮:国产智能体势在必行,一探究竟!

回顾之前的文章 GPTs大爆发&#xff1a;我的智能助手累计使用71k&#xff0c;荣登全球排名79&#xff0c;我们已经见证了智能助手的强劲增长势头。今天&#xff0c;我兴奋地分享一个新的里程碑&#xff1a;我的GPTs使用量已经突破10万次&#xff0c;排名再次提升&#xff01; 接…

【银角大王——Django课程Day1】

Django框架第一课 安装Django框架方式一&#xff08;命令行的形式创建Django项目&#xff09;方式二&#xff08;适合企业版的pycharm&#xff09;默认文件介绍app文件介绍快速上手我的导包一直爆红是因为我没使用解释器&#xff0c;没导入包&#xff0c;去设置里面导入包即可—…

C# 动态加载dll

方式1 using System; using System.Reflection;class Program {static void Main(){string dllPath "path/to/your/library.dll"; // 替换为你的DLL文件路径Assembly myAssembly Assembly.LoadFile(dllPath);Type myType myAssembly.GetType("YourNamespace…

JavaSE高阶篇-反射

第一部分、Junit单元测试 1&#xff09;介绍 1.概述:Junit是一个单元测试框架,在一定程度上可以代替main方法,可以单独去执行一个方法,测试该方法是否能跑通,但是Junit是第三方工具,所以使用之前需要导入jar包 2&#xff09;Junit的基本使用&#xff08;重点啊&#xff09; 1.…