如何编写高效的Python代码:初学者教程

编写高效Python代码的终极指南:初学者教程

初学者程序员喜欢用Python编写代码,因为它简单易读。然而,编写高效的Python代码比你想象的更复杂。它需要理解一些语言的特点(尽管它们同样简单)。

如果您来自C ++或JavaScript等其他编程语言,这个教程适合您学习一些编写高效Python代码的技巧。但如果您是初学者 – 将Python作为第一(编程)语言学习,那么本教程将帮助您从一开始就编写Pythonic的代码。

我们将关注以下内容:

  • Pythonic循环
  • 列表和字典推导式
  • 上下文管理器
  • 生成器
  • 集合类

所以让我们开始吧!

1.编写Pythonic循环

无论您使用哪种语言编程,理解循环结构都很重要。如果您来自C ++或JavaScript等语言,学习如何编写Pythonic循环是很有帮助的。

使用range生成数列

range()函数生成一个序列,通常用作循环中的迭代器。

range()函数返回一个范围对象,默认从0开始,到指定的数字(但不包括该数字)。

这是一个示例:

  for i in range(5): print(i)  

  输入>> > 01234  

使用range()函数时,可以根据需要自定义起始点、结束点和步长。

使用enumerate同时访问索引和元素

当您希望同时获得可迭代对象中每个元素的索引和值时,enumerate()函数非常有用。

在这个例子中,我们使用索引来访问水果列表:

  fruits = ["apple", "banana", "cherry"] for i in range(len(fruits)): print(f"Index {i}: {fruits[i]}")  

  输出>> > 索引0:苹果索引1:香蕉索引2:樱桃  

但是使用enumerate()函数,您可以这样访问索引和元素:

  fruits = ["apple", "banana", "cherry"] for i, fruit in enumerate(fruits): print(f"Index {i}: {fruit}")  

  输出>> >索引0:苹果索引1:香蕉索引2:樱桃  

使用zip在多个可迭代对象上并行迭代

zip()函数用于同时迭代多个可迭代对象。它将来自不同可迭代对象的相应元素进行配对。

考虑以下示例,您需要循环遍历names和scores列表:

  names = ["Alice", "Bob", "Charlie"] scores = [95, 89, 78] for i in range(len(names)): print(f"{names[i]}得分是{scores[i]}分。")  

这将输出:

  输出>> >爱丽丝得分95分。鲍勃得分89分。查理得分78分。 

这是一个更易读的循环例子,使用了 zip() 函数:

names = ["Alice", "Bob", "Charlie"]
scores = [95, 89, 78]
for name, score in zip(names, scores):
    print(f"{name} 得了 {score} 分.")

 

输出结果 >>> Alice 得了 95 分.Bob 得了 89 分.Charlie 得了 78 分.

 

使用 zip() 来写的 Pythonic 版本更加简洁,避免了手动索引的需要-让代码更干净、更易读。

 

2. 使用列表和字典解析

 

在 Python 中,列表解析和字典解析是创建列表和字典的简洁的一行代码。它们还可以包含条件语句以根据特定条件过滤项目。

我们从循环版本开始,然后转向列表和字典的解析表达式。

 

Python 中的列表解析

 

假设你有一个 numbers 列表。你想创建一个 squared_numbers 列表。你可以使用以下的循环代码:

numbers = [1, 2, 3, 4, 5]
squared_numbers = []
for num in numbers:
    squared_numbers.append(num ** 2)
print(squared_numbers)

 

输出结果 >>> [1, 4, 9, 16, 25]

 

但是列表解析提供了一种更简洁和简单的语法来完成这个任务。通过将一个表达式应用到可迭代对象的每个项来创建新的列表。

  

下面是使用列表解析表达式的简洁替代方案:

numbers = [1, 2, 3, 4, 5]
squared_numbers = [num ** 2 for num in numbers]
print(squared_numbers)

 

输出结果 >>> [1, 4, 9, 16, 25]

 

在这里,列表解析创建一个包含 numbers 列表中每个数字的平方的新列表。

 

带有条件过滤的列表解析

 

你还可以在列表解析表达式中添加过滤条件。看看这个例子:

numbers = [1, 2, 3, 4, 5]
odd_numbers = [num for num in numbers if num % 2 != 0]
print(odd_numbers)

 

输出结果 >>> [1, 3, 5]

 

在这个例子中,列表解析创建一个只包含 numbers 列表中奇数的新列表。

 

Python 中的字典解析

 

和列表解析类似,字典解析允许你从现有的可迭代对象中创建字典。

  

假设你有一个 fruits 列表。你想创建一个 key-value 对为 fruit:len(fruit) 的字典。

这是如何用循环完成这个任务的:

fruits = ["apple", "banana", "cherry", "date"]
fruit_lengths = {}
for fruit in fruits:
    fruit_lengths[fruit] = len(fruit)
print(fruit_lengths)

输出 >>> {'apple': 5, 'banana': 6, 'cherry': 6, 'date': 4}

现在让我们写出等效的字典推导式:

fruits = ["apple", "banana", "cherry", "date"]fruit_lengths = {fruit: len(fruit) for fruit in fruits}print(fruit_lengths)

输出 >>> {'apple': 5, 'banana': 6, 'cherry': 6, 'date': 4}

这个字典推导式创建了一个字典,其中键是水果,值是水果名称的长度。

带有条件过滤的字典推导式

让我们修改我们的字典推导式来包含一个条件:

fruits = ["apple", "banana", "cherry", "date"]long_fruit_names = {fruit: len(fruit) for fruit in fruits if len(fruit) > 5}print(long_fruit_names)

输出 >>> {'banana': 6, 'cherry': 6}

在这里,字典推导式创建了一个字典,其中水果名称作为键,它们的长度作为值,但只限于名称超过5个字符的水果。

3. 使用上下文管理器有效处理资源

Python中的上下文管理器可以帮助您高效地管理资源。通过使用上下文管理器,您可以轻松设置和清理(清除)资源。上下文管理器的最简单和最常见的示例是文件处理。

看一下下面的代码片段:

filename = 'somefile.txt'file = open(filename,'w')file.write('Something')

它没有关闭文件描述符,导致资源泄漏。

print(file.closed)输出 >>> False

您可能会做出以下修改:

filename = 'somefile.txt'file = open(filename,'w')file.write('Something')file.close()

虽然这尝试关闭描述符,但它没有考虑在写操作过程中可能出现的错误。

好吧,现在您可以实施异常处理,尝试打开一个文件并在没有任何错误的情况下写入一些内容:

filename = 'somefile.txt'file = open(filename,'w')try:    file.write('Something')finally:    file.close()

但这很冗长。现在看看下面使用with语句的版本,它支持上下文管理器的open()函数:

filename = 'somefile.txt'with open(filename, 'w') as file:    file.write('Something')print(file.closed)

输出 >>> True

我们使用with语句创建一个上下文,在该上下文中打开文件。这确保了在执行退出with块时正确关闭文件,即使在操作过程中引发异常也是如此。

4. 使用生成器进行内存高效处理

生成器提供了一种优雅的处理大型数据集或无限序列的方式,提高代码效率并减少内存消耗。

什么是生成器?

生成器是使用yield关键字一次返回一个值的函数,它在调用之间保持其内部状态。与一次计算所有值并返回一个完整列表的常规函数不同,生成器在需要时计算和生成值,使其适用于处理大型序列。

 

发电机是如何工作的?

 

 

让我们了解一下发电机的工作原理:

  • 发生器函数的定义方式与常规函数相同,但是您会使用yield关键字而不是return来生成一个值。
  • 当您调用发电机函数时,它会返回一个发电机对象。您可以通过循环遍历或使用next()调用来迭代它。
  • 当遇到yield语句时,函数的状态被保存,并且生成的值被返回给调用者。函数的执行暂停,但其本地变量和状态被保留。
  • 当再次调用发电机的next()方法时,执行从暂停的位置恢复,函数继续执行直到下一个yield语句。
  • 当函数退出或引发StopIteration异常时,发电机被认为已耗尽,进一步调用next()将引发StopIteration异常。

 

创建发电机

 

您可以使用发电机函数或发电机表达式来创建发电机。

这是一个发电机函数的示例:

def countdown(n):    while n > 0:        yield n        n -= 1# 使用发电机函数for num in countdown(5):    print(num)

 

输出 >>> 5 4 3 2 1

 

发电机表达式类似于列表推导,但它们创建的是发电机而不是列表。

# 使用发电机表达式创建一个平方序列squares = (x ** 2 for x in range(1, 6))# 使用发电机表达式for square in squares:    print(square)

 

输出 >>> 1 4 9 16 25

 

5. 利用集合类

 

我们将通过学习两个有用的集合类来结束本教程:

  • NamedTuple
  • Counter

 

使用命名元组使元组更易读

 

在Python中,命名元组是内置元组类的子类,在collections模块中。但它提供了命名字段。这使得它比常规元组更易读和更易于自我文档化。

这是一个创建一个简单的三维空间点元组并访问各个元素的示例:

# 3D点元组coordinate = (1, 2, 3)# 使用元组解包访问数据x, y, z = coordinateprint(f"X坐标:{x},Y坐标:{y},Z坐标:{z}")

 

输出 >>> X坐标:1,Y坐标:2,Z坐标:3

 

这是命名元组版本:

from collections import namedtuple# 定义一个Coordinate3D命名元组Coordinate3D = namedtuple("Coordinate3D", ["x", "y", "z"])# 创建一个Coordinate3D对象coordinate = Coordinate3D(1, 2, 3)print(coordinate)# 使用命名字段访问数据print(f"X坐标:{coordinate.x},Y坐标:{coordinate.y},Z坐标:{coordinate.z}")

 

输出 >>>Coordinate3D(x=1, y=2, z=3)X坐标:1,Y坐标:2,Z坐标:3

 

因此,通过使用NamedTuples,您可以比使用普通元组编写更清晰和更易于维护的代码。

 

使用Counter简化计数

 

Countercollections模块中的一个类,用于计算可迭代对象(如列表或字符串)中元素的频率。它返回一个带有{element:count}键值对的Counter对象。

让我们以计算长字符串中字符频率的示例为例。

以下是使用循环计算字符频率的传统方法:

word = "难以理解性"# 初始化一个空字典来计数字符char_counts = {}# 计算字符频率for char in word:    if char in char_counts:        char_counts[char] += 1    else:         char_counts[char] = 1# 打印出char_counts字典print(char_counts)# 找到最常见的字符most_common = max(char_counts, key=char_counts.get)print(f"最常见的字符: '{most_common}' (出现 {char_counts[most_common]} 次)")

 

我们手动遍历字符串,更新字典以计算字符频率,并找到最常见的字符。

输出 >>> {'难': 5, '以': 2, '理': 1, '解': 1, '性': 1}最常见的字符: '难' (出现 5 次)

 

现在,让我们使用Counter类以Counter(iterable)的语法执行相同的任务:

from collections import Counterword = "难以理解性"# 使用Counter计算字符频率char_counts = Counter(word)print(char_counts)# 找到最常见的字符most_common = char_counts.most_common(1)print(f"最常见的字符: '{most_common[0][0]}' (出现 {most_common[0][1]} 次)")

 

输出 >>> Counter({'难': 5, '性': 1, '以': 1, '理': 1, '解': 1})最常见的字符: '难' (出现 5 次)

 

因此,Counter提供了一种更简单的方式来计算字符频率,而无需手动迭代和管理字典。

 

总结

 

我希望您能找到一些有用的技巧,以补充您的Python工具箱。如果您想学习Python或准备进行编码面试,以下是一些资源可以帮助您的学习之旅:

愉快的学习吧!

 

[Bala Priya C](https://twitter.com/balawc27)是来自印度的开发人员和技术作家。她喜欢在数学、编程、数据科学和内容创作的交叉点上工作。她的兴趣和专长领域包括DevOps、数据科学和自然语言处理。她喜欢阅读、写作、编码和咖啡!目前,她正在通过撰写教程、指南、观点文章等向开发者社区学习和分享她的知识。