百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

Python:被忽视的核心功能

wptr33 2025-03-02 19:16 43 浏览

【编者按】这篇文章主要介绍了一些在 Python 编程中可能被忽视的核心功能,包括默认参数、海象运算符、*args 和 **kwargs 的使用、变量交换、str 与 repr 的区别、可迭代对象的扩展解包、多个上下文管理器的使用、Python 调试器、collections.Counter 的使用、itertools 的使用以及下划线的两种用法等。

原文链接:https://erikvandeven.medium.com/python-uncovering-the-overlooked-core-functionalities-54590420c225

未经允许,禁止转载!


作者 | Erik van de Ven 译者 | 明明如月
责编 | 夏萌
出品 | CSDN(ID:CSDNnews)


图片由 Stefan Steinbauer 提供,发布在 Unsplash

Kyle Simpson 在他的书中提到的关于 JavaScript 的观点,Luciano Ramalho 在他的《Fluent Python》一书中对 Python 的描述也有相同的主题。他们基本上是在讨论这两种语言的同一个问题。用我的话来说:

因为这门语言非常容易学,许多实践者只是了解了它的基础知识,而忽略了深入探讨这门语言更高级和强大的方面,这也是它真正独特和强大的地方。

所以,让我们简要讨论一下所有你可能还没有听说过的功能,但如果你想成为一名真正经验丰富的 Pythonista,你肯定想了解。

参数默认值

需要重点注意的是,Python 的参数在遇到函数定义时就进行评估。这意味着每次调用 fib_memo 函数(下文会提到)而没有明确提供 memo 参数的值时,它将使用函数定义时创建的相同字典对象。

def fib_memo(n, memo={0:0, 1:1}):  """  n 是你想要返回的序列中的第 n 个数字  """  if not n in memo:  memo[n] = fib_memo(n-1) + fib_memo(n-2)  return memo[n] # 返回一个介于0和100之间(包括0和100)的数字。 fib_memo(6) # 应该返回8

这段代码在 Python 中可以正常运行。这也意味着你可以在一个脚本中多次执行 fib_memo 函数,比如在一个 for 循环中,每次执行都会增加要计算的 fibonacci 数字,而不会达到“超过最大递归深度”的限制,因为 memo 会不断扩展。关于这方面的更多信息可以在我的另一篇文章中找到。

海象操作符

海象操作符 (:= ) 是在 Python 3.8 中引入的,它允许你在表达式中为变量赋值。这样,你可以在一个表达式中为变量赋值并检查其值:

import randomsome_value = random.randint(0,100) # 返回一个介于0和100之间(包括0和100)的数字。if((below_ten := some_value) < 10):  print(f"{below_ten} 小于 10")

显然,它也可以方便地赋值并检查返回的值是否包含真值:

if(result := some_method()): # 如果结果不为假值(Falsy)  print(result)

*args 和 **kwargs

通过星号 (* ),你可以在传递给函数之前解包参数或关键字参数(使用 ** )。

例如,考虑以下代码:

my_numbers = [1,2]def sum_numbers(first_number, second_number):  return first_number + second_number
# 这将返回一个类型错误。# 类型错误:sum() 缺少 1 个必需的位置参数:“second_number” sum_numbers(my_numbers)
#这将返回预期的结果,3。sum_numbers(*my_numbers)

当我们调用 sum_numbers 函数时,如果不解包 my_numbers,它会引发一个 TypeError ,因为函数期望两个独立的参数。然而,通过使用星号 (*),我们可以从 my_numbers 中解包值并将它们作为独立的参数传递,从而得到正确的输出。

这种解包技术不仅适用于元组和列表,还适用于字典(尽管它会将键作为参数传递)。那么关键字参数呢?对此,我们可以利用双星号 (**)。以下代码作为例子:

def greet_person(last_name, first_name):  print(f"Hello {first_name} {last_name}")
data = {"first_name": "John", "last_name": "Doe"}greet_person(**data)

除了解包一个序列来将它们作为函数的参数,你也可以用它来创建一个新的序列,例如:

numbers = [1, 2, 3, 4, 5]new_list_numbers = [*numbers]

原始的 numbers 列表保持不变,而你有一个 new_list_numbers 变量,它包含了相同列表的副本。然而,对于包含对象的链接要小心:

numbers = [[1, 2], [3, 4], [5, 6]]packed_numbers = [*numbers]
numbers[0].append(10) # 修改原始列表中的嵌套列表
print(numbers) # 输出: [[1, 2, 10], [3, 4], [5, 6]] print(packed_numbers) # 输出: [[1, 2, 10], [3, 4], [5, 6]]

any 和 all

any all 是两个内建函数,它们对可迭代对象(如列表、元组或集合)进行操作,并基于可迭代对象中的元素返回一个布尔值。

例如:

some_booleans = [True, False, False, False]
any(some_booleans) # 返回 Trueall(some_booleans) # 返回 False

你可以将 allany 函数与列表推导式结合使用,它返回一个可迭代的结果并将其作为参数传递给 all 函数:

numbers = [5, 10, 3, 8, -2]all_positive = all(num > 0 for num in numbers)

… 或 any 函数:

fruits = ['apple', 'banana', 'cherry', 'durian']
# 检查是否所有水果都以“a”开头result = all(fruit.startswith('a') for fruit in fruits)print(result)

下面的表格显示了根据可迭代对象中的值返回的输出的不同。

变量交换

你可以组合元组打包(在等号 (=) 右边发生)和解包(在等号 (=) 左边发生),并利用这个功能进行变量交换:

a = 10b = 5
# 通过打包和解包交换 b 和 a 的值a, b = b, a
print(a) #5print(b) #10

str vs repr

我们习惯于使用 str(some_value) 将某个变量或值转换为字符串,以便于进行调试打印。我想让你了解 repr(some_value)。主要的区别是 repr 尝试返回对象的可打印表示,而 str 只尝试返回一个字符串表示。

下面是一个更好的例子:

import datetime
today = datetime.datetime.now()
print(str(today))print(str(today)) # 输出: 2023-07-20 15:30:00.123456print(repr(today)) # 输出:datetime.datetime(2023, 7, 20, 15, 30, 0, 123456)

如你所见,str() 简单地将 datetime 作为一个字符串表示返回。如果你想确定变量 today 是否包含一个字符串还是一个 datetime 对象,你无法单独从这个信息中得到答案。另一方面,repr() 提供了有关变量所持有的实际对象的信息。在调试过程中,这个信息会更有价值。

扩展的可迭代对象解包

这个可以简单理解:如果你想通过一个命令获取序列的第一个和最后一个值:

first, *middle, last = [1, 2, 3, 4, 5]
print(first) # 1print(middle) # [2, 3, 4]print(last) # 5

但这也是可行的

*the_first_three, second_last, last = [1, 2, 3, 4, 5]
print(the_first_three) # [1, 2, 3]print(second_last) # 4print(last) # 5

或者其他组合。

多个上下文管理器

我们习惯于一次使用一个上下文管理器,比如打开一个文件:

with open('file.txt', 'r') as file: # 使用该文件的代码 # 该文件将在块结束时自动关闭 # 即使发生异常
# 示例:从文件中读取行 for line in file: print(line.strip())
with open('file_2.txt', 'r') as other_file: # 第二个上下文管理器
for line in other_file: print(line.strip())

但我们可以轻易地在一个语句中打开多个文件。如果你想将一行写入另一个文件,这非常简便:

with open('file1.txt') as file1, open('file2.txt') as file2: # 同时使用 file1 和 file2 的代码 # 文件将在块结束时自动关闭 # 即使发生异常
# 示例:从文件 1 读取行并将其写入文件 2 for line in file1: file2.write(line)

Python 调试器

我们可以在我们的文件中打印大量的变量进行调试,或者我们可以简单地使用 Python 调试器 (pdb),它帮助我们设置断点,使得操作更加简单:

import pdb
# 在你的代码中设置这个断点pdb.set_trace()

使这个功能更有价值的是,程序会在你设置的断点处停止,你可以打印任何变量来检查其在特定断点处的值或存在情况。试试看!当程序触发一个断点时,你可以使用以下几个命令:

  • nnext:执行下一行。

  • s step:步入函数调用。

  • c continue:继续执行直到下一个断点。

  • l list:显示当前的代码上下文。

  • p pp :打印表达式的值。

  • b break :在指定的行设置一个新的断点。

  • h help:获取关于 pdb 的使用帮助。

  • q quit:退出调试器并终止程序。

collections.Counter

collections 模块中的 Counter 类提供了一种便捷的方式来计算可迭代对象中的元素个数:

Counter``collections
from collections import Counter
my_list = [1, 2, 3, 1, 2, 1, 3, 4, 5]counts = Counter(my_list)print(counts) # 输出: 计数器({1: 3, 2: 2, 3: 2, 4: 1, 5: 1})

使用 Itertools 实现组合

我们可以组合不同的 for 循环来创建排列、组合或笛卡尔积,或者我们可以简单地使用内建的 itertools。

Permutations(排列)

import itertools
# 生成排列perms = itertools.permutations([1, 2, 3], 2)print(list(perms)) # 输出: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

Combinations(组合)

import itertools
# 生成组合combs = itertools.combinations('ABC', 2)print(list(combs)) # 输出: [('A', 'B'), ('A', 'C'), ('B', 'C')]

Cartesian product(笛卡尔积)

import itertools
# 生成笛卡尔积cartesian = itertools.product('AB', [1, 2])print(list(cartesian)) # 输出: [('A', 1), ('A', 2), ('B', 1), ('B', 2)]

下划线的两种用法

下面是在 Python 中使用下划线的两种方式:作为大数字的分隔符或作为丢弃变量。

丢弃变量

下划线 _ 可以用作丢弃变量,用来丢弃不想要的值:

# 忽略函数的第一个返回值_, result = some_function()
# 不使用循环变量进行循环for _ in range(5): do_something()
# 你只需要第一个和最后一个first, *_, last = [1, 2, 3, 4, 5]

大数字的分隔符

在处理大数字值时,你可以使用下划线 (_) 作为视觉分隔符以提高可读性。这个特性在 Python 3.6 中被引入,被称为 "下划线字面量"。

population = 7_900_000_000revenue = 3_249_576_382.50
print(population) # 输出: 7900000000print(revenue) # 输出: 3249576382.5

你知道 Python 中还有哪些容易被忽略的好用功能或技巧?欢迎在评论区留言分享。

粉丝福利:

相关推荐

oracle数据导入导出_oracle数据导入导出工具

关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...

继续学习Python中的while true/break语句

上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个else解...

python continue和break的区别_python中break语句和continue语句的区别

python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...

简单学Python——关键字6——break和continue

Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...

2-1,0基础学Python之 break退出循环、 continue继续循环 多重循

用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...

Python 中 break 和 continue 傻傻分不清

大家好啊,我是大田。今天分享一下break和continue在代码中的执行效果是什么,进一步区分出二者的区别。一、continue例1:当小明3岁时不打印年龄,其余年龄正常循环打印。可以看...

python中的流程控制语句:continue、break 和 return使用方法

Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...

L017:continue和break - 教程文案

continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...

作为前端开发者,你都经历过怎样的面试?

已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...

面试被问 const 是否不可变?这样回答才显功底

作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...

2023金九银十必看前端面试题!2w字精品!

导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域...

前端面试总结_前端面试题整理

记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...

由浅入深,66条JavaScript面试知识点(七)

作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录由浅入深,66条JavaScript面试知识点(一)由浅入深,66...

2024前端面试真题之—VUE篇_前端面试题vue2020及答案

添加图片注释,不超过140字(可选)1.vue的生命周期有哪些及每个生命周期做了什么?beforeCreate是newVue()之后触发的第一个钩子,在当前阶段data、methods、com...

今年最常见的前端面试题,你会做几道?

在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...