《Python Cookbook》的作者David Beazley的课程PPT开源了,目标用户是希望从编写基础脚本过渡到编写更复杂程序的高级 Python 程序员,课程主题侧重于流行库和框架中使用的编程技术,主要目的是更好地理解 Python 语言本身,以便阅读他人的代码,并将新发现的知识应用到自己的项目中。内容组织的很棒,总共分为九个章节,我在阅读过程中顺便翻译整理下,用来查缺补漏了。翻译内容并非原版无对照翻译,有所增减,本篇是系列第五节。
感兴趣可前往原课件进行阅读👉 https://github.com/dabeaz-course/python-mastery/blob/main/PythonMastery.pdf
LLM应用全栈开发
函数设计
- 仅对传递的参数进行操作
- 对于相同的参数产生相同的结果
- 避免隐藏的副作用
- 目标:简单性和可预测性
函数调用
-
名称和参数是“接口”
-
调用函数应该直观且简单
-
编写 API 的核心组件
命名约定
-
有一种首选的 Python 风格
-
下划线方式,而不是驼峰式
-
对内部/私有函数使用前导
_
默认参数
- 默认参数必须出现在定义的最后
- 如果指定了参数值,则该参数在函数调用中是可选的
关键字参数
-
首选关键字来传递可选参数
a = read_data('data.csv', debug=True) # YES! b = read_data('data.csv', True) # NO!
-
关键字使代码更加清晰
-
可以强制使用关键字参数
默认值
-
仅使用不可变值,例如 None、True、False、数字或字符串
def func(a, items=[]): items.append(a) return items >>> func(1) [1] >>> func(2) [1, 2] >>> func(3) [1, 2, 3]
- 整个程序的默认值仅创建一次
文档字符串
-
函数应该有一个文档字符串
def add(x, y): ''' Adds x and y together. ''' return x + y
-
有助于提供 help() 命令和开发工具
-
重要提示:类型签名或其他详细信息可以帮助人们阅读代码
类型提示 (PEP 484)
-
可选注释可以指示类型
def add(x:int, y:int) -> int: ''' Adds x and y together. ''' return x + y
-
类型提示不执行任何操作,但可能对代码检查器、文档、IDE 等有用
并发
-
函数可能同时执行(线程)
-
共享状态,在单个解释器中执行
Future
处理并发,两个函数同时执行
from concurrent.futures import Future
fut = Future()
函数式编程
高阶函数
-
函数可以接受函数作为输入
-
函数可以返回函数作为结果
Lambda函数
def sum_map(func, nums):
total = 0
for n in nums:
total += func(n)
return total
nums = [1, 2, 3, 4]
result = sum_map(lambda x: x*x, nums)
Map函数(Map-Reduce)
def map(func, values):
result = []
for x in values:
result.append(func(x))
return result
def reduce(func, values, initial=0):
result = initial
for n in values:
result = func(n, result)
return result
def sum(x, y):
return x + y
def square(x):
return x * x
nums = [1, 2, 3, 4]
result = reduce(sum, map(square, nums))
将复杂问题分解为可管理的部分,然后将这些部分组合在一起,以创建一个整体的解决方案。这种方式有助于减少复杂性,提高代码的可读性、可维护性和可扩展性,是现代软件开发的一个重要原则。
内部函数
def add(x, y):
def do_add():
print(f'{x} + {y} -> {x+y}')
return do_add
>>> a = add(3,4)
>>> a
<function do_add at 0x6a670>
>>> a()
3 + 4 -> 7
>>>
嵌套作用域:外部函数定义的变量
闭包
- 内部函数称为“闭包”
- 闭包是一种机制,允许函数保留它需要的所有变量的值,以确保在稍后的运行中能够正常工作
>>> a = add(3, 4)
>>> a.__closure__
(<cell at 0x54f30: int object at 0x54fe0>,
<cell at 0x54fd0: int object at 0x54f60>)
>>> a.__closure__[0].cell_contents
3
>>> a.__closure__[1].cell_contents
4
- Alternate evaluation (e.g., "delayed evaluation): 这是闭包的一种常见用法。通过闭包,可以实现“延迟评估”,即推迟某些操作的执行,直到需要的时候再进行计算。这对于性能优化和避免不必要的计算很有帮助。
- Callback functions: 回调函数是闭包的另一个典型用例。在编程中,回调函数是一种通过将函数作为参数传递给其他函数来实现的机制。闭包可以让回调函数捕获它们被定义时的上下文,以便在稍后的某个时间点执行。
- Code creation (“macros”): 闭包还可以用于动态地创建代码,这就是所谓的“代码生成”或“宏”。通过闭包,可以在运行时生成函数或代码块,并在需要的地方调用。这对于在运行时根据不同条件生成代码非常有用。
错误处理
捕获运行过程异常
try:
statements
...
except RuntimeError as e:
# Handle the runtime error
...
捕获所有异常
try:
# Some complicated operation
...
except Exception as e:
print("Sorry, it didn't work.")
print("Reason:", e)
...
重新引发异常
try:
# Some complicated operation
...
except Exception as e:
print("Sorry, it didn't work.")
print("Reason:", e)
raise
自定义异常
日志记录
import logging
log = logging.getLogger(__name__)
def read_data(filename):
...
try:
name = row[0]
shares = int(row[1])
price = float(row[2])
except ValueError as e:
log.warning("Bad row: %s", row)
log.debug("Reason : %s", e)
测试
断言
-
断言不用于检查用户输入: 断言通常不用来验证用户输入数据,而是用来验证程序内部的条件是否满足。用户输入的数据是不可控的,因此使用断言来检查它们可能不太安全,而且断言会在生产环境中被默认禁用。
-
用于验证程序不变式: 断言应该用于验证程序内部的条件,这些条件被称为“不变式”,即程序执行过程中必须始终保持为真的条件。如果断言失败,意味着程序出现了编程错误,可能是程序员在代码中引入了错误。
-
失败指示编程错误: 断言失败会指示存在编程错误,而不是一般的错误输入。这种方式可以将错误追溯到错误的调用者或部分,以便进行修复。
-
可被禁用: 断言可以通过在 Python 解释器中使用
-O
(大写字母 O)参数来禁用。在启用断言时,如果断言条件不满足,程序将会引发AssertionError
异常。但在禁用断言时,Python 解释器会忽略所有断言语句,这有助于在生产环境中避免性能损失。
def add(x, y):
'''
Adds x and y
'''
assert isinstance(x, int)
assert isinstance(y, int)
return x + y
>>> add(2, 3)
5
>>> add('2', '3')
Traceback (most recent call last):
...
AssertionError
>>>
单元测试
-
在大型应用中可能变得相当复杂: 随着应用程序规模的增加,测试代码的编写可能会变得非常复杂。在大型应用中,需要测试的功能和组件变得更多,测试之间的依赖关系也会增加,因此编写、维护和运行测试会变得复杂。
-
unittest
模块的多样选项: Python 提供了unittest
模块来进行单元测试,但它有很多选项,涉及测试运行器、结果收集和其他测试相关的方面。在编写测试用例时,你需要处理这些选项,这可能会增加代码的复杂性。 -
考虑使用
pytest
作为替代: 提到了pytest
作为替代方案。pytest
是 Python 中流行的一个第三方测试框架,它提供了更简洁的语法和丰富的功能,可以减少测试代码的复杂性。相比unittest
,pytest
的语法更加简洁直观,并且它具有丰富的插件系统和灵活的配置选项,使得测试更加方便。
class TestAdd(unittest.TestCase):
def test_simple(self):
# Test with simple integer arguments
r = simple.add(2, 2)
self.assertEqual(r, 5)
def test_str(self):
# Test with strings
r = simple.add('hello', 'world')
self.assertEqual(r, 'helloworld')