原标题:Python:被忽视的核心功能
| CSDN(ID:CSDNnews)
图片由 Stefan Steinbauer提供,发布在 Unsplash上
Kyle Simpson 在他的书中提到的关于 Java 的观点,Luciano Ramalho 在他的《Fluent Python》一书中对 Python 的描述也有相同的主题。他们基本上是在讨论这两种语言的同一个问题。用我的话来说:
因为这门语言非常容易学,许多实践者只是了解了它的基础知识,而忽略了深入探讨这门语言更高级和强大的方面,这也是它真正独特和强大的地方。
所以,让我们简要讨论一下所有你可能还没有听说过的功能,但如果你想成为一名真正经验丰富的 Pythonista,你肯定想了解。
参数默认值
需要重点注意的是,Python 的参数在遇到函数定义时就进行评估。这意味着每次调用 fib_memo函数(下文会提到)而没有明确提供 memo 参数的值时,它将使用函数定义时创建的相同字典对象。
deffib_memo(n, memo={ 0: 0, 1: 1}) : “”” n 是你想要返回的序列中的第 n 个数字 “”” ifnotn inmemo: memo[n] = fib_memo(n -1) + fib_memo(n -2) returnmemo[n] # 返回一个介于0和100之间(包括0和100)的数字。 fib_memo( 6) # 应该返回8这段代码在 Python 中可以正常运行。这也意味着你可以在一个脚本中多次执行 fib_memo函数,比如在一个 for 循环中,每次执行都会增加要计算的 fibonacci 数字,而不会达到“超过最大递归深度”的限制,因为 memo 会不断扩展。关于这方面的更多信息可以在 我的另一篇文章中找到。
海象操作符
海象操作符 (:=) 是在 Python 3.8 中引入的,它允许你在表达式中为变量赋值。这样,你可以在一个表达式中为变量赋值并检查其值:
importrandom some_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] defsum_numbers(first_number, second_number): returnfirst_number + second_number# 这将返回一个类型错误。# 类型错误:sum 缺少 1 个必需的位置参数:“second_number” sum_numbers(my_numbers)
#这将返回预期的结果,3。sum_numbers(*my_numbers)
当我们调用 sum_numbers函数时,如果不解包 my_numbers,它会引发一个 TypeError ,因为函数期望两个独立的参数。然而,通过使用星号 (*),我们可以从 my_numbers中解包值并将它们作为独立的参数传递,从而得到正确的输出。
这种解包技术不仅适用于元组和列表,还适用于字典(尽管它会将键作为参数传递)。那么关键字参数呢?对此,我们可以利用双星号 (**)。以下代码作为例子:
defgreet_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
你可以将 all和 any函数与列表推导式结合使用,它返回一个可迭代的结果并将其作为参数传递给all函数:
numbers= [ 5, 10, 3, 8, – 2] all_positive= all(num > 0for num in numbers)… 或 any函数:
fruits = [ apple, banana, cherry, durian]# 检查是否所有水果都以“a”开头result = all(fruit.startswith( a) forfruit infruits) 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只尝试返回一个字符串表示。
下面是一个更好的例子:
importdatetimetoday = 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
或者其他组合。
多个上下文管理器
我们习惯于一次使用一个上下文管理器,比如打开一个文件:
withopen( file.txt, r) asfile: # 使用该文件的代码# 该文件将在块结束时自动关闭# 即使发生异常# 示例:从文件中读取行forline infile: print(line.strip)
withopen( file_2.txt, r) asother_file: # 第二个上下文管理器
forline inother_file: print(line.strip)
但我们可以轻易地在一个语句中打开多个文件。如果你想将一行写入另一个文件,这非常简便:
withopen( file1.txt) asfile1, open( file2.txt) asfile2: # 同时使用 file1 和 file2 的代码# 文件将在块结束时自动关闭# 即使发生异常# 示例:从文件 1 读取行并将其写入文件 2forline infile1: file2.write(line)
Python 调试器
我们可以在我们的文件中打印大量的变量进行调试,或者我们可以简单地使用 Python 调试器 (pdb),它帮助我们设置断点,使得操作更加简单:
importpdb# 在你的代码中设置这个断点pdb.set_trace
使这个功能更有价值的是,程序会在你设置的断点处停止,你可以打印任何变量来检查其在特定断点处的值或存在情况。试试看!当程序触发一个断点时,你可以使用以下几个命令:
n 或 next :执行下一行。 s 或 step :步入函数调用。 c 或 continue :继续执行直到下一个断点。 l 或 list :显示当前的代码上下文。 p <expression> 或 pp <expression> :打印表达式的值。 b <line> 或 break <line> :在指定的行设置一个新的断点。 h 或 help :获取关于 pdb 的使用帮助。 q 或 quit :退出调试器并终止程序。collections.Counter
collections 模块中的Counter 类提供了一种便捷的方式来计算可迭代对象中的元素个数:
Counter “collections from collections import Countermy_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(组合)
importitertools# 生成组合combs = itertools.combinations( ABC, 2) print(list(combs)) # 输出: [( A, B), ( A, C), ( B, C)]
Cartesian product(笛卡尔积)
importitertools# 生成笛卡尔积cartesian = itertools.product( AB, [ 1, 2]) print(list(cartesian)) # 输出: [( A, 1), ( A, 2), ( B, 1), ( B, 2)]
下划线的两种用法
下面是在 Python 中使用下划线的两种方式:作为大数字的分隔符或作为丢弃变量。
丢弃变量
下划线 _ 可以用作丢弃变量,用来丢弃不想要的值:
# 忽略函数的第一个返回值_, result = some_function# 不使用循环变量进行循环for_ inrange(5): do_something
# 你只需要第一个和最后一个first, *_, last = [1, 2, 3, 4, 5]
大数字的分隔符
在处理大数字值时,你可以使用下划线 (_) 作为视觉分隔符以提高可读性。这个特性在 Python 3.6 中被引入,被称为 “下划线字面量”。
population = 7_900_000_000revenue = 3_249_576_382.50print(population) # 输出: 7900000000print(revenue) # 输出: 3249576382.5
你知道 Python 中还有哪些容易被忽略的好用功能或技巧?欢迎在评论区留言分享。返回搜狐,查看更多
责任编辑: