From f20970df75e578032cb8bf0dc726a3fdde19b5d1 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 14 Sep 2024 11:52:18 +0800 Subject: [PATCH 01/59] modify .gitignore & item_01.py --- .gitignore | 22 ++++++++++++++++++++++ example_code/item_01.py | 5 ++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fbfa7d1..ab4f017 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,23 @@ **/*.pyc +bin/ +target/ +.settings/ +.classpath +.project +node_modules/ +package-lock.json +.idea/ +.vscode/ +*.iml +logs/ +*.log +*.whl +release/ +__pycache__/ +custom_models/ +FILE_STORE/ +checkpoints +/pythondemo.ipr +/pythondemo.iws +# Ignore PyCharm/IDEA project settings +.idea/ diff --git a/example_code/item_01.py b/example_code/item_01.py index 585a9c5..0037362 100755 --- a/example_code/item_01.py +++ b/example_code/item_01.py @@ -16,6 +16,7 @@ # Reproduce book environment import random + random.seed(1234) import logging @@ -37,16 +38,18 @@ atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) + def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() -atexit.register(close_open_files) +atexit.register(close_open_files) # Example 1 import sys + print(sys.version_info) print(sys.version) From 8238a115768cd1a26fe023b60be69b7588738eae Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 14 Sep 2024 22:00:36 +0800 Subject: [PATCH 02/59] modify item_01.py& item_03.py --- example_code/item_01.py | 28 ++++++- example_code/item_03.py | 160 ++++++++++++++++++++++++++++------------ 2 files changed, 139 insertions(+), 49 deletions(-) diff --git a/example_code/item_01.py b/example_code/item_01.py index 0037362..0830a2c 100755 --- a/example_code/item_01.py +++ b/example_code/item_01.py @@ -17,39 +17,63 @@ # Reproduce book environment import random +#这设置了 Python 的随机数生成器的种子,以确保每次生成的随机数序列是一致的。 random.seed(1234) +# 这是 Python 的内置日志模块,用于生成日志信息。虽然这里并没有使用 logging,但可能是为了后续的示例或输出。 import logging +# 导入 pprint,用于美化打印 Python 对象,让输出更加可读。 from pprint import pprint +# 将 sys.stdout 重命名为 STDOUT,以便后续代码可以使用 STDOUT 代替 sys.stdout 进行输出。 from sys import stdout as STDOUT # Write all output to a temporary directory +# atexit 模块 提供了在程序正常退出时执行函数的能力。 +# 通过 atexit.register() 函数, +# 你可以注册一些函数,这些函数会在程序正常终止时自动调用。 import atexit +# gc 模块 控制 Python 的垃圾回收器。Python 使用引用计数和垃圾回收机制来管理内存, +# gc 模块提供了一些额外的工具来控制和调试内存管理过程,尤其是循环引用的情况 +# (即两个对象互相引用,导致引用计数永远不为 0,但它们又不再需要时)。 import gc +# io 模块 提供了 Python 的核心 IO 功能,包括文件操作、流操作等。 import io +# os 模块 提供了访问操作系统服务的功能,包括文件操作、进程管理、环境变量等。 import os +# tempfile 模块 提供了创建临时文件和目录的功能。 import tempfile +# 创建一个临时目录,用于存放输出文件。 TEST_DIR = tempfile.TemporaryDirectory() +# 注册一个函数,当程序退出时,清理临时目录。 atexit.register(TEST_DIR.cleanup) # Make sure Windows processes exit cleanly +# 保存当前工作目录,以便在程序退出时恢复 OLD_CWD = os.getcwd() +# 注册一个函数,当程序退出时,恢复当前工作目录 atexit.register(lambda: os.chdir(OLD_CWD)) +# 将当前工作目录切换到临时目录 os.chdir(TEST_DIR.name) - +""" +这个函数用于关闭所有打开的文件。 +它通过 gc.get_objects() 获取当前 Python 进程中的所有对象, +""" def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() - +# 注册一个函数,当程序退出时,关闭所有打开的文件。 atexit.register(close_open_files) # Example 1 +# sys 模块 提供了 Python 解释器的一些变量和函数,包括与 Python 解释器交互的函数。 import sys +# 输出 Python 的版本信息 print(sys.version_info) +# 输出 Python 的版本号 print(sys.version) diff --git a/example_code/item_03.py b/example_code/item_03.py index 24eb0a7..2998360 100755 --- a/example_code/item_03.py +++ b/example_code/item_03.py @@ -28,6 +28,7 @@ import io import os import tempfile +import sys TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) @@ -45,20 +46,42 @@ def close_open_files(): atexit.register(close_open_files) +print(f"\n{'第三条: 了解字节串,字符串与unicode区别':*^50}") -# Example 1 + +# Example 1 --- bytes 对象的表示和打印 +print(f"\n{'Example 1':*^50}") +# 这是一个 bytes 对象,表示字节数据。h\x65llo 中的 \x65 是字符 e 的十六进制表示, +# 因此这个 bytes 对象实际上代表字符串 b'hello'。 a = b'h\x65llo' -print(list(a)) +# list(a):把 bytes 对象转换为一个由字节值组成的列表。 +# 因为 bytes 是一个可迭代对象,它的每个元素是 int 类型的字节值。 +# 输出:[104, 101, 108, 108, 111],这代表 ASCII 值分别为 h, e, l, l, o。 +print(f"{list(a)}") +# 直接输出 bytes 对象,它会以 b'...' 的形式展示出来。 +# 输出:b'hello',这里前面的b表示这是一个 bytes 对象,这个b并不是字节串的实际内容。 print(a) -# Example 2 +# Example 2 --- Unicode 字符串的表示和打印 +print(f"\n{'Example 2':*^50}") +# 这是一个 Unicode 字符串,表示为 à propos。 a = 'a\u0300 propos' -print(list(a)) +# list(a):把 Unicode 字符串转换为一个由字符组成的列表。 +# 因为 Unicode 字符串是一个可迭代对象,它的每个元素是一个字符。 +# 输出:['a', '̀', ' ', 'p', 'r', 'o', 'p', 'o', 's'] +print(f"{list(a)}") +# 直接输出 Unicode 字符串,它会以 '...' 的形式展示出来。 +# 输出:à propos print(a) # Example 3 +print(f"\n{'Example 3':*^50}") + +""" +将输入的字节(bytes)或字符串(str)都转换为字符串。如果是 bytes,则用 UTF-8 进行解码。 +""" def to_str(bytes_or_str): if isinstance(bytes_or_str, bytes): value = bytes_or_str.decode('utf-8') @@ -66,134 +89,177 @@ def to_str(bytes_or_str): value = bytes_or_str return value # Instance of str -print(repr(to_str(b'foo'))) -print(repr(to_str('bar'))) +# to_str 函数:将输入的字节(bytes)或字符串(str)都转换为字符串。 +# repr():返回对象的“官方”字符串表示形式,方便显示特殊字符或类型。 +print(repr(to_str(b'foo'))) # 'b'foo' 被转换为 'foo',原理是字节串换成了字符串 +print(repr(to_str('bar'))) # 字符串 'bar' 直接返回 # Example 4 +print(f"\n{'Example 4':*^50}") + +""" +将输入的字节(bytes)或字符串(str)都转换为字节。如果是字符串,则用 UTF-8 进行编码。 +""" def to_bytes(bytes_or_str): - if isinstance(bytes_or_str, str): + if isinstance(bytes_or_str, str): # 如果是字符串,则用 UTF-8 进行编码 value = bytes_or_str.encode('utf-8') else: value = bytes_or_str return value # Instance of bytes -print(repr(to_bytes(b'foo'))) -print(repr(to_bytes('bar'))) +print(repr(to_bytes(b'foo'))) # 字节串 b'foo' 直接返回 +print(repr(to_bytes('bar'))) # 'bar' 被转换为 b'bar',原理是字符串换成了字节串 # Example 5 +print(f"\n{'Example 5':*^50}") + +# 字节串的连接:b'one' + b'two',这是字节串的简单连接。输出:b'onetwo' +# b 是字节串的前缀,表示这是一个字节对象(bytes 类型)。当你写 b'one' 和 b'two' 时, +# b 只是告诉 Python 这些字面量是字节串,而不是字符串。 print(b'one' + b'two') +# 字符串的连接:'one' + 'two',这是普通字符串的连接。输出:onetwo print('one' + 'two') +# 配置日志将输出到 stdout 而不是 stderr +logging.basicConfig(stream=sys.stdout, level=logging.INFO) # Example 6 +print(f"\n{'Example 6':*^50}") + try: b'one' + 'two' -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False -# Example 7 +#Example 7 +print(f"\n{'Example 7':*^50}") + try: 'one' + b'two' -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False - +print("\nExample 8-10: 比较字节和字符串") # Example 8 +print(f"\n{'Example 8':*^50}") +print(b'red' > b'blue') assert b'red' > b'blue' +print('red' > 'blue') assert 'red' > 'blue' # Example 9 +print(f"\n{'Example 9':*^50}") + try: assert 'red' > b'blue' -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False # Example 10 +print(f"\n{'Example 10':*^50}") + try: assert b'blue' < 'red' -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False +print("\nExample 11: 字节和字符串的相等比较") # Example 11 -print(b'foo' == 'foo') +print(f"\n{'Example 11':*^50}") +print(b'foo' == 'foo') # 字节串和字符串内容可以相同但是不相等 +print("\nExample 12-14: 格式化字符串和字节串") # Example 12 -print(b'red %s' % b'blue') -print('red %s' % 'blue') +print(f"\n{'Example 12':*^50}") +print(b'red %s' % b'blue') # 字节串格式化字节串 +print('red %s' % 'blue') # 字符串格式化字符串 # Example 13 +print(f"\n{'Example 13':*^50}") try: - print(b'red %s' % 'blue') -except: - logging.exception('Expected') + print(b'red %s' % 'blue') # 字节串格式化字符串,是失败的 +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False # Example 14 -print('red %s' % b'blue') - +print(f"\n{'Example 14':*^50}") +print('red %s' % b'blue') # 字符串格式化字节串,是成功的 +print("\nExample 15-18 读写字节串") # Example 15 +# 二进制数据写入文本文件:尝试将字节串写入以文本模式打开的文件会失败, +# 因为文本模式下只能写入字符串,不能写入字节串。 +print(f"\n{'Example 15':*^50}") try: with open('data.bin', 'w') as f: f.write(b'\xf1\xf2\xf3\xf4\xf5') -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False # Example 16 +#以二进制模式写入文件:这时写入成功,因为文件是以 wb(写二进制)模式打开的。 +print(f"\n{'Example 16':*^50}") with open('data.bin', 'wb') as f: f.write(b'\xf1\xf2\xf3\xf4\xf5') # Example 17 +print(f"\n{'Example 17':*^50}") try: # Silently force UTF-8 here to make sure this test fails on # all platforms. cp1252 considers these bytes valid on Windows. + # 这行代码保存了 Python 内置的 open 函数到变量 real_open 中, + # 以便在稍后调用被重新定义的 open 函数时,能够使用原始的 open 函数。 real_open = open + """ + 重新定义 open 函数,使其总是以 UTF-8 编码打开文件。 + """ def open(*args, **kwargs): kwargs['encoding'] = 'utf-8' return real_open(*args, **kwargs) - + with open('data.bin', 'r') as f: data = f.read() -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False -# Example 18 -# Restore the overloaded open above. -open = real_open - -with open('data.bin', 'rb') as f: - data = f.read() - -assert data == b'\xf1\xf2\xf3\xf4\xf5' - - -# Example 19 -with open('data.bin', 'r', encoding='cp1252') as f: - data = f.read() - -assert data == 'ñòóôõ' +# # Example 18 +# # Restore the overloaded open above. +# open = real_open +# +# with open('data.bin', 'rb') as f: +# data = f.read() +# +# assert data == b'\xf1\xf2\xf3\xf4\xf5' +# +# +# # Example 19 +# with open('data.bin', 'r', encoding='cp1252') as f: +# data = f.read() +# +# assert data == 'ñòóôõ' From fb1292d9629fc29dae2c58e001992d4d31b2f14a Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Wed, 18 Sep 2024 08:33:27 +0800 Subject: [PATCH 03/59] modify item_01.py& item_03.py --- example_code/item_03.py | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/example_code/item_03.py b/example_code/item_03.py index 2998360..de342b4 100755 --- a/example_code/item_03.py +++ b/example_code/item_03.py @@ -78,7 +78,6 @@ def close_open_files(): # Example 3 print(f"\n{'Example 3':*^50}") - """ 将输入的字节(bytes)或字符串(str)都转换为字符串。如果是 bytes,则用 UTF-8 进行解码。 """ @@ -97,7 +96,6 @@ def to_str(bytes_or_str): # Example 4 print(f"\n{'Example 4':*^50}") - """ 将输入的字节(bytes)或字符串(str)都转换为字节。如果是字符串,则用 UTF-8 进行编码。 """ @@ -114,7 +112,6 @@ def to_bytes(bytes_or_str): # Example 5 print(f"\n{'Example 5':*^50}") - # 字节串的连接:b'one' + b'two',这是字节串的简单连接。输出:b'onetwo' # b 是字节串的前缀,表示这是一个字节对象(bytes 类型)。当你写 b'one' 和 b'two' 时, # b 只是告诉 Python 这些字面量是字节串,而不是字符串。 @@ -127,7 +124,6 @@ def to_bytes(bytes_or_str): # Example 6 print(f"\n{'Example 6':*^50}") - try: b'one' + 'two' except Exception as e: @@ -138,7 +134,6 @@ def to_bytes(bytes_or_str): #Example 7 print(f"\n{'Example 7':*^50}") - try: 'one' + b'two' except Exception as e: @@ -157,7 +152,6 @@ def to_bytes(bytes_or_str): # Example 9 print(f"\n{'Example 9':*^50}") - try: assert 'red' > b'blue' except Exception as e: @@ -168,7 +162,6 @@ def to_bytes(bytes_or_str): # Example 10 print(f"\n{'Example 10':*^50}") - try: assert b'blue' < 'red' except Exception as e: @@ -248,18 +241,17 @@ def open(*args, **kwargs): assert False -# # Example 18 -# # Restore the overloaded open above. -# open = real_open -# -# with open('data.bin', 'rb') as f: -# data = f.read() -# -# assert data == b'\xf1\xf2\xf3\xf4\xf5' -# -# -# # Example 19 -# with open('data.bin', 'r', encoding='cp1252') as f: -# data = f.read() -# -# assert data == 'ñòóôõ' +# Example 18 +# Restore the overloaded open above. +print(f"\n{'Example 18':*^50}") +open = real_open +with open('data.bin', 'rb') as f: + data = f.read() +assert data == b'\xf1\xf2\xf3\xf4\xf5' + + +# Example 19 +print(f"\n{'Example 19':*^50}") +with open('data.bin', 'r', encoding='cp1252') as f: + data = f.read() +assert data == 'ñòóôõ' From 6dbcb1d070b18b98c68e660182cd7283ffaedf31 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Wed, 18 Sep 2024 11:22:25 +0800 Subject: [PATCH 04/59] modify item_03.py --- example_code/item_03.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/example_code/item_03.py b/example_code/item_03.py index de342b4..fb387e1 100755 --- a/example_code/item_03.py +++ b/example_code/item_03.py @@ -204,7 +204,7 @@ def to_bytes(bytes_or_str): print(f"\n{'Example 15':*^50}") try: with open('data.bin', 'w') as f: - f.write(b'\xf1\xf2\xf3\xf4\xf5') + f.write(b'\xf1\xf2\xf3\xf4\xf5') # 字节串写入文本文件,在没有文件的情况下会创建文件 except Exception as e: logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: @@ -236,6 +236,11 @@ def open(*args, **kwargs): with open('data.bin', 'r') as f: data = f.read() except Exception as e: + # 报错原因分析: + # 文件的内容是二进制字节 b'\xf1\xf2\xf3\xf4\xf5',这五个字节的值对于 UTF-8 编码来说并不合法。 + # 重新定义的 open 函数强制将 encoding='utf-8' 添加到所有文件读取操作,因此在读取文件时, + # Python 尝试将文件中的字节数据用 UTF-8 解码。 + # 由于 b'\xf1\xf2\xf3\xf4\xf5' 不是有效的 UTF-8 编码字节,因此会触发 UnicodeDecodeError。 logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -243,6 +248,9 @@ def open(*args, **kwargs): # Example 18 # Restore the overloaded open above. +# 以二进制方式读取文件:这时读取成功,因为文件是以 rb(读二进制)模式打开的。 +# 由于文件是以二进制模式打开的,因此 Python 不会尝试将文件内容解码为 UTF-8。 +# 因此,你获取的是原始的字节数据,而不是解码后的字符串,所以结果为b'\xf1\xf2\xf3\xf4\xf5'。 print(f"\n{'Example 18':*^50}") open = real_open with open('data.bin', 'rb') as f: @@ -251,6 +259,10 @@ def open(*args, **kwargs): # Example 19 +# 以 cp1252 编码方式打开文件:这时读取成功,因为文件是以 cp1252 编码方式打开的。 +# cp1252 是单字节编码,可以直接将每个字节映射到对应字符,因此能够解码 b'\xf1\xf2\xf3\xf4\xf5'。 +# UTF-8 是多字节编码,需要特定的字节序列格式。 +# 如果字节序列不符合规则(例如 0xF1 需要后续合法字节),则会出现解码错误。 print(f"\n{'Example 19':*^50}") with open('data.bin', 'r', encoding='cp1252') as f: data = f.read() From 66a614a36704a13d586ad4ee3bdf4ac6191c6c3f Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Wed, 18 Sep 2024 22:07:19 +0800 Subject: [PATCH 05/59] modify item_04.py --- example_code/item_04.py | 267 +++++++++++++++++++++++++++++++--------- 1 file changed, 208 insertions(+), 59 deletions(-) diff --git a/example_code/item_04.py b/example_code/item_04.py index d1fca5d..6ba0cf9 100755 --- a/example_code/item_04.py +++ b/example_code/item_04.py @@ -15,28 +15,59 @@ # limitations under the License. # Reproduce book environment +""" +prefer interpreted F-strings over C-style format strings and str.format +优先使用解释型 F-strings 而不是 C 风格的格式化字符串和 str.format +""" + +# 总述:为代码的执行设置一个特定的环境,使得所有代码运行时能够保证输出一致且不受外部因素影响。 +# 在 《Effective Python》 的代码示例中,每个 item 的开头部分有一段类似的代码, +# 这是为了确保每个代码示例在受控、隔离的环境中运行,并生成一致的结果。 + +# 目的:确保示例代码中任何使用随机数的部分都能够生成一致的随机结果。 +# 解释:通过设置 random.seed(1234),即使多次运行代码,或者在不同机器上运行, +# 使用 random 模块生成的随机数序列都会相同。 +# 这在展示和调试代码时特别有用,避免因为随机数的不同导致输出不一致。 import random +import sys + random.seed(1234) +# 总述:配置日志输出。 +# 用于控制日志记录。在示例中,有时需要打印调试信息、异常或错误。这确保日志的输出格式和处理方式一致。 import logging +# 用于格式化打印复杂数据结构,让输出更加易读。 from pprint import pprint +# 这是系统标准输出的别名。在某些情况下,它可能用于重定向日志或输出结果。 from sys import stdout as STDOUT # Write all output to a temporary directory +# 使用临时目录存储文件输出 import atexit import gc import io import os import tempfile - +# 创建一个临时目录,用于存放任何示例代码生成的文件。 +# 这样可以确保每个示例代码运行时,所有文件操作都是在隔离的、 +# 临时的目录中进行,不会污染系统的文件系统或干扰其他文件。 TEST_DIR = tempfile.TemporaryDirectory() +# 注册一个钩子函数,当 Python 程序退出时自动删除这个临时目录及其内容, +# 确保程序运行后不会遗留任何文件或目录。 atexit.register(TEST_DIR.cleanup) # Make sure Windows processes exit cleanly -OLD_CWD = os.getcwd() +# 处理 Windows 的进程退出 +OLD_CWD = os.getcwd() # 获取当前工作目录 +# 注册一个钩子函数,确保程序退出时恢复原来的工作目录 +# 确保当程序退出时,自动将工作目录切换回原始的工作目录。 +# 这样做是为了避免在程序结束后,当前目录仍然停留在临时目录中,影响后续的操作。 atexit.register(lambda: os.chdir(OLD_CWD)) -os.chdir(TEST_DIR.name) +os.chdir(TEST_DIR.name) # 将工作目录切换到临时目录 +""" +总述:确保所有打开的文件都能被正确关闭。 +""" def close_open_files(): everything = gc.get_objects() for obj in everything: @@ -45,124 +76,181 @@ def close_open_files(): atexit.register(close_open_files) +# 配置日志将输出到 stdout 而不是 stderr +logging.basicConfig(stream=sys.stdout, level=logging.INFO) + -# Example 1 + + +# Example 1 --- 二进制和十六进制格式化 +# 目的:展示如何将二进制和十六进制数格式化为十进制输出。 +print(f"\n{'Example 1':*^50}") +# a 是二进制数 10111011 a = 0b10111011 +# b 是十六进制数 c5f b = 0xc5f +# 输出:Binary is 187, hex is 3167 print('Binary is %d, hex is %d' % (a, b)) -# Example 2 +# Example 2 --- 百分号格式化字符串 +# 目的:展示如何使用 % 格式化字符串和浮点数,并控制对齐和小数位数。 +print(f"\n{'Example 2':*^50}") +# key 是 my_var,value 是 1.234 key = 'my_var' value = 1.234 +# %-10s:将字符串 key 左对齐,宽度为 10。 +# %.2f: 将浮点数 value 格式化为小数点后 2 位的浮点数。 formatted = '%-10s = %.2f' % (key, value) print(formatted) -# Example 3 +# Example 3 --- 格式化顺序错误 +# 目的:演示当格式化时参数顺序不匹配时的错误。value 是浮点数,但 %s 期望字符串,反之也是如此。 +print(f"\n{'Example 3':*^50}") try: reordered_tuple = '%-10s = %.2f' % (value, key) -except: - logging.exception('Expected') +except Exception as e: + # ERROR:root:Error type: TypeError, Message: must be real number, not str + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False -# Example 4 +# Example 4 --- 格式化类型不匹配错误 +# 目的:演示当格式化的类型不匹配时的错误。%.2f 期望浮点数,但 key 是字符串。 +print(f"\n{'Example 4':*^50}") try: reordered_string = '%.2f = %-10s' % (key, value) -except: - logging.exception('Expected') +except Exception as e: + # ERROR:root:Error type: TypeError, Message: must be real number, not str + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False -# Example 5 +# Example 5 --- 百分号格式化与 enumerate 结合使用 +# 目的:演示如何使用 % 格式化枚举 (enumerate) 生成的索引和值。 +print(f"\n{'Example 5':*^50}") pantry = [ ('avocados', 1.25), ('bananas', 2.5), ('cherries', 15), ] for i, (item, count) in enumerate(pantry): + # 格式化字符串将索引 i 和项目 item 左对齐,并将 count 格式化为保留两位小数的浮点数。 print('#%d: %-10s = %.2f' % (i, item, count)) -# Example 6 +# Example 6 --- 整数格式化 +# 目的:演示如何将浮点数格式化为整数并四舍五入。 +print(f"\n{'Example 6':*^50}") for i, (item, count) in enumerate(pantry): + # round(count) 对 count 进行四舍五入,并格式化为整数输出。别的参考 Example 5。 print('#%d: %-10s = %d' % ( i + 1, item.title(), - round(count))) + round(count)) + ) -# Example 7 +# Example 7 --- 模板字符串替换 +# 目的:展示如何使用模板字符串中的占位符进行替换。 +print(f"\n{'Example 7':*^50}") template = '%s loves food. See %s cook.' name = 'Max' formatted = template % (name, name) print(formatted) -# Example 8 +# Example 8 --- 使用 title() 格式化名字 +# 目的:展示如何使用 title() 方法格式化名字。 +print(f"\n{'Example 8':*^50}") name = 'brad' formatted = template % (name.title(), name.title()) print(formatted) -# Example 9 +# Example 9 --- 字典键值对格式化 +# 目的:展示如何使用字典键值对进行格式化。占位符根据字典中的键进行替换。 +print(f"\n{'Example 9':*^50}") key = 'my_var' value = 1.234 old_way = '%-10s = %.2f' % (key, value) +new_way = '%(key)-10s = %(value).2f' % {'key': key, 'value': value} +reordered = '%(key)-10s = %(value).2f' % {'value': value, 'key': key} # Swapped -new_way = '%(key)-10s = %(value).2f' % { - 'key': key, 'value': value} # Original - -reordered = '%(key)-10s = %(value).2f' % { - 'value': value, 'key': key} # Swapped - +# 输出 'my_var = 1.23',并确保不同顺序的格式化结果一致。 +print(old_way) +print(new_way) +print(reordered) assert old_way == new_way == reordered -# Example 10 +# Example 10 --- 从 tuple 到 dictionary 的格式化 +# 目的:比较 tuple 和 dictionary 格式化的效果,确保两者输出一致。 +# 元组格式化: +# 顺序严格,每个占位符需要按照顺序对应元组中的元素。 +# 简单快速,适用于小规模的格式化。 +# 可读性较差,在有多个相同类型参数时容易混淆。 +# 字典格式化: +# 基于键名,更灵活且不依赖参数的顺序。 +# 可读性强,格式化时可以直接看到变量名称,清晰易懂。 +# 更适合复杂字符串模板或含有大量变量的场景。 +print(f"\n{'Example 10':*^50}") name = 'Max' - +# 元组格式化 template = '%s loves food. See %s cook.' before = template % (name, name) # Tuple - +# 字典格式化 template = '%(name)s loves food. See %(name)s cook.' after = template % {'name': name} # Dictionary - +# 判断两种格式化方式的结果是否一致。 +print(before) +print(after) assert before == after -# Example 11 +# Example 11 --- 循环与字典格式化 +# 目的:展示如何在 for 循环中使用字典键值对进行格式化。 +print(f"\n{'Example 11':*^50}") for i, (item, count) in enumerate(pantry): + # 元组格式化 before = '#%d: %-10s = %d' % ( i + 1, item.title(), round(count)) - + # 字典格式化 after = '#%(loop)d: %(item)-10s = %(count)d' % { 'loop': i + 1, 'item': item.title(), 'count': round(count), } - + print(f"before ::: {before}") + print(f"after ::: {after}") assert before == after -# Example 12 +# Example 12 --- 简单字典格式化 +# 目的:展示简单的字典占位符替换。 +print(f"\n{'Example 12':*^50}") soup = 'lentil' formatted = 'Today\'s soup is %(soup)s.' % {'soup': soup} +# 输出 'Today's soup is lentil.'。 print(formatted) -# Example 13 +# Example 13 --- 复杂的字典格式化 +# 目的:展示如何使用字典格式化复杂的字符串模板。 +print(f"\n{'Example 13':*^50}") menu = { 'soup': 'lentil', 'oyster': 'kumamoto', 'special': 'schnitzel', } +# 输出 'Today's soup is lentil, buy one, get two kumamoto oysters, +# and our special entrée is schnitzel.'。 template = ('Today\'s soup is %(soup)s, ' 'buy one get two %(oyster)s oysters, ' 'and our special entrée is %(special)s.') @@ -170,66 +258,107 @@ def close_open_files(): print(formatted) -# Example 14 +# Example 14 --- format 函数 +# 目的: 展示 format 函数的多种格式化方式,包括千位分隔符和字符串对齐。 +print(f"\n{'Example 14':*^50}") +# 千位分隔符,保留两位小数 a = 1234.5678 formatted = format(a, ',.2f') print(formatted) - +# 中间对齐 b = 'my string' formatted = format(b, '^20s') print('*', formatted, '*') -# Example 15 +# Example 15 --- 使用 format 函数进行简单格式化 +# 目的:演示使用 .format() 方法进行字符串格式化。 +# 解释: +# {} 占位符会被 format 中的参数依次替换,第一个 {} 替换为 key, +# 第二个 {} 替换为 value。(这个是有顺序约束的) +print(f"\n{'Example 15':*^50}") key = 'my_var' value = 1.234 - formatted = '{} = {}'.format(key, value) print(formatted) -# Example 16 +# Example 16 --- 使用 format 函数对齐和控制小数点 +# 目的:展示如何使用 format 方法对齐和控制小数点位数。 +# 解释: +# {:<10}:key 左对齐,宽度为 10。 +# {:.2f}:将 value 格式化为两位小数的浮点数。 +# 用的是Example 15 中的 key 和 value。 +print(f"\n{'Example 16':*^50}") formatted = '{:<10} = {:.2f}'.format(key, value) print(formatted) -# Example 17 +# Example 17 --- 百分号和 format 混合使用 +# 目的:演示如何在字符串中混合使用 % 和 format 方法。 +# 解释: +# '%.2f%%' % 12.5:格式化 12.5 为两位小数,并在末尾加上百分号 %。 +# '{} replaces {{}}'.format(1.23):{{}} 是转义字符,用来表示单个 {},{} 被 1.23 替换。 +print(f"\n{'Example 17':*^50}") print('%.2f%%' % 12.5) print('{} replaces {{}}'.format(1.23)) -# Example 18 +# Example 18 --- 调换 format 占位符的顺序 +# 目的:展示如何在 format 方法中通过索引调换占位符的顺序。 +# 解释: +# {1}:使用 format 中的第二个参数 value 替换。 +# {0}:使用第一个参数 key 替换。 +# 用的是Example 15 中的 key 和 value。 +print(f"\n{'Example 18':*^50}") formatted = '{1} = {0}'.format(key, value) print(formatted) -# Example 19 +# Example 19 --- 重复使用 format 中的占位符 +# 目的:展示如何在 format 方法中多次使用相同的占位符。 +# 解释: +# {0}:两次使用 name 进行替换。 +# 用的是Example 7 中的 name。 +print(f"\n{'Example 19':*^50}") formatted = '{0} loves food. See {0} cook.'.format(name) print(formatted) -# Example 20 +# Example 20 --- 比较旧式和 format 方法的格式化 +# 目的:比较旧式 % 格式化和 .format() 方法的输出是否一致。 +# 解释: +# 旧式格式化:'%d' % 10 和 '{}'.format(10) 输出一致。 +# 用的是Example5中的参数pantry。 +print(f"\n{'Example 20':*^50}") for i, (item, count) in enumerate(pantry): old_style = '#%d: %-10s = %d' % ( i + 1, item.title(), round(count)) - + print("old_style ::: {}".format(old_style)) new_style = '#{}: {:<10s} = {}'.format( i + 1, item.title(), round(count)) - + print("new_style ::: {}".format(new_style)) assert old_style == new_style -# Example 21 -formatted = 'First letter is {menu[oyster][0]!r}'.format( - menu=menu) +# Example 21 --- 嵌套字典访问 +# 目的:展示如何使用 .format() 访问嵌套字典中的值。 +# 解释: +# menu[oyster][0]:从字典 menu 中取出 oyster 对应的字符串,并获取第一个字符。 +# !r:表示使用 repr() 方式打印字符。 +# (repr():返回对象的开发者可读的字符串表示,主要用于调试和开发,通常是对该对象的正式描述。) +# 结果:输出 'First letter is 'k''。 +formatted = 'First letter is {menu[oyster][0]!r}'.format(menu=menu) print(formatted) -# Example 22 +# Example 22 --- 比较旧式百分号和 .format() 的输出 +# 目的:展示如何将旧式的 % 字典格式化转换为 .format() 方法,并确保两者输出一致。 +print(f"\n{'Example 22':*^50}") old_template = ( 'Today\'s soup is %(soup)s, ' 'buy one get two %(oyster)s oysters, ' @@ -239,6 +368,7 @@ def close_open_files(): 'oyster': 'kumamoto', 'special': 'schnitzel', } +print("old_formatted ::: {} ".format(old_formatted)) new_template = ( 'Today\'s soup is {soup}, ' @@ -249,39 +379,52 @@ def close_open_files(): oyster='kumamoto', special='schnitzel', ) - +print("new_formatted ::: {} ".format(new_formatted)) assert old_formatted == new_formatted -# Example 23 +# Example 23 --- F-strings 格式化 +# 目的:演示使用 f-string 进行格式化,直接在字符串中嵌入变量。 +# 解释: +# f'{key} = {value}':通过 f-string,key 和 value 会自动替换到字符串中。 +# 结果:输出 'my_var = 1.234'。 +print(f"\n{'Example 23':*^50}") key = 'my_var' value = 1.234 - formatted = f'{key} = {value}' print(formatted) -# Example 24 +# Example 24 --- f-string 的对齐与精度控制 +# 目的:展示 f-string 的对齐和精度控制。 +# 解释: +# {key!r:<10}:key 左对齐,宽度 10,并使用 repr() 格式化。 +# {value:.2f}:value 保留两位小数。 +# 结果:输出 'my_var = 1.23'。 +# 用的是Example23中的key和value。 formatted = f'{key!r:<10} = {value:.2f}' print(formatted) -# Example 25 +# Example 25 --- 比较不同格式化方式的输出 +# 目的:比较不同格式化方法(f-string、百分号、format 等)的输出是否一致。 +# 解释: +# f_string:使用 f-string 格式化。 f_string = f'{key:<10} = {value:.2f}' - +# c_tuple:使用 % 元组格式化。 c_tuple = '%-10s = %.2f' % (key, value) - +# str_args 和 str_kw:使用 .format() 方法格式化。 str_args = '{:<10} = {:.2f}'.format(key, value) - str_kw = '{key:<10} = {value:.2f}'.format(key=key, value=value) - +# c_dict:使用 % 字典格式化。 c_dict = '%(key)-10s = %(value).2f' % {'key': key, 'value': value} - assert c_tuple == c_dict == f_string assert str_args == str_kw == f_string # Example 26 +print(f"\n{'Example 26':*^50}") +# 目的:比较旧式、format 和 f-string 的格式化输出是否一致。 for i, (item, count) in enumerate(pantry): old_style = '#%d: %-10s = %d' % ( i + 1, @@ -298,14 +441,20 @@ def close_open_files(): assert old_style == new_style == f_string -# Example 27 +# Example 27 --- 直接使用 f-string 输出 +# 目的:展示如何直接在循环中使用 f-string 进行格式化输出。 +# 解释:将索引、物品名(首字母大写)和数量(四舍五入)使用 f-string 格式化。 for i, (item, count) in enumerate(pantry): print(f'#{i+1}: ' f'{item.title():<10s} = ' f'{round(count)}') -# Example 28 +# Example 28 --- 动态控制小数点位数的 f-string +# 目的:展示如何在 f-string 中动态控制小数点位数。 +# 解释:{number:.{places}f} 使用变量 places 来控制小数点后的位数。 +# 结果:输出 'My number is 1.235',小数保留 3 位。 +print(f"\n{'Example 28':*^50}") places = 3 number = 1.23456 print(f'My number is {number:.{places}f}') From bfe3c03e1db0d46513b3836f4df86fe53fbfebf0 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 19 Sep 2024 17:31:27 +0800 Subject: [PATCH 06/59] modify item_05.py --- example_code/item_05.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/example_code/item_05.py b/example_code/item_05.py index ec407db..b57c081 100755 --- a/example_code/item_05.py +++ b/example_code/item_05.py @@ -15,6 +15,10 @@ # limitations under the License. # Reproduce book environment + +# Write Helper Functions Instead of Complex Expressions +# 使用辅助函数取代复杂的表达式 + import random random.seed(1234) @@ -46,22 +50,31 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 使用 parse_qs 解析查询字符串 +# 目的: 演示如何使用 parse_qs 来解析查询字符串。 from urllib.parse import parse_qs - +# 解释: +# parse_qs 解析了查询字符串 'red=5&blue=0&green=',并返回一个字典。 +# keep_blank_values=True 参数确保即使某些值为空(比如 green),也不会被忽略。 +# 字典中的键是参数名,值是一个列表,比如 'red': ['5']。 my_values = parse_qs('red=5&blue=0&green=', keep_blank_values=True) +# 输出:{'red': ['5'], 'blue': ['0'], 'green': ['']} print(repr(my_values)) -# Example 2 +# Example 2 --- 访问解析后的值 +# 目的: 演示如何从解析后的字典中获取值。 +# 解释: +# my_values.get('red') 获取 'red' 的值,它是 ['5']。 +# my_values.get('green') 返回 [''],即使值为空它也不会被忽略。 +# my_values.get('opacity') 返回 None,因为查询字符串中没有 opacity 这个键。 print('Red: ', my_values.get('red')) print('Green: ', my_values.get('green')) print('Opacity: ', my_values.get('opacity')) -# Example 3 -# For query string 'red=5&blue=0&green=' +# Example 3 --- 元组的不可变性 red = my_values.get('red', [''])[0] or 0 green = my_values.get('green', [''])[0] or 0 opacity = my_values.get('opacity', [''])[0] or 0 From 245a4ed5da3133af9b6c395ff396788fefe2fba1 Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Thu, 19 Sep 2024 18:40:54 +0800 Subject: [PATCH 07/59] modify item_05.py --- example_code/item_05.py | 56 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/example_code/item_05.py b/example_code/item_05.py index b57c081..3daaa85 100755 --- a/example_code/item_05.py +++ b/example_code/item_05.py @@ -57,6 +57,7 @@ def close_open_files(): # parse_qs 解析了查询字符串 'red=5&blue=0&green=',并返回一个字典。 # keep_blank_values=True 参数确保即使某些值为空(比如 green),也不会被忽略。 # 字典中的键是参数名,值是一个列表,比如 'red': ['5']。 +print(f"\n{'Example 1':*^50}") my_values = parse_qs('red=5&blue=0&green=', keep_blank_values=True) # 输出:{'red': ['5'], 'blue': ['0'], 'green': ['']} @@ -69,12 +70,22 @@ def close_open_files(): # my_values.get('red') 获取 'red' 的值,它是 ['5']。 # my_values.get('green') 返回 [''],即使值为空它也不会被忽略。 # my_values.get('opacity') 返回 None,因为查询字符串中没有 opacity 这个键。 +print(f"\n{'Example 2':*^50}") print('Red: ', my_values.get('red')) print('Green: ', my_values.get('green')) print('Opacity: ', my_values.get('opacity')) -# Example 3 --- 元组的不可变性 +# Example 3 --- 处理可能为空的值 +# 目的: 演示如何处理可能为空或不存在的值。 +# 解释: +# my_values.get('red', [''])[0] or 0 会获取 'red' 的第一个值,如果该值为空,就返回 0。 +# 对于 green 和 opacity 也是一样的处理逻辑,这样即使值是空的或者键不存在,也会得到一个默认的 0。 +# 结果: +# Red: '5' +# Green: 0 +# Opacity: 0 +print(f"\n{'Example 3':*^50}") red = my_values.get('red', [''])[0] or 0 green = my_values.get('green', [''])[0] or 0 opacity = my_values.get('opacity', [''])[0] or 0 @@ -83,7 +94,12 @@ def close_open_files(): print(f'Opacity: {opacity!r}') -# Example 4 +# Example 4 --- 将字符串转换为整数 +# 目的: 演示如何将从查询字符串中提取的值转换为整数。 +# 解释: +# int() 用于将值从字符串转换为整数。my_values.get('red', [''])[0] or 0 确保获取的值非空,才能转换成整数。 +# green 和 opacity 都是空的,所以会被转换成 0。 +print(f"\n{'Example 4':*^50}") red = int(my_values.get('red', [''])[0] or 0) green = int(my_values.get('green', [''])[0] or 0) opacity = int(my_values.get('opacity', [''])[0] or 0) @@ -92,7 +108,16 @@ def close_open_files(): print(f'Opacity: {opacity!r}') -# Example 5 +# Example 5 --- 优化字符串到整数的转换 +# 目的: 演示如何优化字符串到整数的转换过程。 +# 解释: +# 这里首先获取字符串,然后判断该字符串是否为空,再决定是否将其转换为整数。 +# 如果是空字符串,则直接返回 0,避免出错。 +# 结果: +# Red: 5 +# Green: 0 +# Opacity: 0 +print(f"\n{'Example 5':*^50}") red_str = my_values.get('red', ['']) red = int(red_str[0]) if red_str[0] else 0 green_str = my_values.get('green', ['']) @@ -104,7 +129,13 @@ def close_open_files(): print(f'Opacity: {opacity!r}') -# Example 6 +# Example 6 --- 使用 if-else 来判断和转换值 +# 目的: 演示如何使用 if-else 来判断值是否为空并进行处理。 +# 解释: +# if green_str[0] 判断是否有值,如果有就转换成整数。 +# 否则,green 被设为 0,确保安全无误。 +# green 又是 0,但这次你是用 if-else 判断出来的,控制力更强~ +print(f"\n{'Example 6':*^50}") green_str = my_values.get('green', ['']) if green_str[0]: green = int(green_str[0]) @@ -113,7 +144,13 @@ def close_open_files(): print(f'Green: {green!r}') -# Example 7 +# Example 7 --- 封装成函数来获取整数 +# 目的: 封装成函数来简化获取整数值的过程。 +# 解释: +# get_first_int 函数封装了之前的逻辑:从查询字符串中提取某个键的值,并将其转换为整数。 +# 如果值不存在或为空,则返回默认值 default。 +# 结果: 没有直接输出,因为这是一个封装好的函数。你可以放心使用它去取各种键的值,简单又高效! +print(f"\n{'Example 7':*^50}") def get_first_int(values, key, default=0): found = values.get(key, ['']) if found[0]: @@ -121,6 +158,13 @@ def get_first_int(values, key, default=0): return default -# Example 8 +# Example 8 --- 使用封装函数获取值 +# 目的: 演示如何使用封装函数来获取值。 +# 解释: +# 通过 get_first_int 来获取 'green' 的值,确保返回的值是整数。 +# 如果 'green' 为空,函数会返回默认值 0。 +print(f"\n{'Example 8':*^50}") +my_values = parse_qs('red=5&blue=0&green=', + keep_blank_values=True) green = get_first_int(my_values, 'green') print(f'Green: {green!r}') From 3be7c4119885ea63b4f82ab02eb0ef18038ff76e Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Thu, 19 Sep 2024 21:56:28 +0800 Subject: [PATCH 08/59] modify item_06.py --- example_code/item_06.py | 90 ++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/example_code/item_06.py b/example_code/item_06.py index beafb39..6864c55 100755 --- a/example_code/item_06.py +++ b/example_code/item_06.py @@ -15,12 +15,16 @@ # limitations under the License. # Reproduce book environment + +# Prefer Multiple Assignment Unpacking Over Indexing +# 把数据结构直接拆分到多个变量中,避免通过下标索引来访问数据结构 + import random +import sys + random.seed(1234) import logging -from pprint import pprint -from sys import stdout as STDOUT # Write all output to a temporary directory import atexit @@ -37,49 +41,77 @@ atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) + def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() + atexit.register(close_open_files) +# 配置日志将输出到 stdout 而不是 stderr +logging.basicConfig(stream=sys.stdout, level=logging.INFO) -# Example 1 +print(f"\nPrefer Multiple Assignment Unpacking Over Indexing") +print(f"\n把数据结构直接拆分到多个变量中,避免通过下标索引来访问数据结构") + +# Example 1 --- 使用 tuple() 将字典项转换为元组 +# 目的: 演示如何将字典的键值对转换为元组。 +# 解释: +# snack_calories.items() 返回字典的 (键, 值) 对。 +# tuple() 将这些 (键, 值) 对转换为不可变的元组。 +# 元组中的每个元素是 ('键', 值) 这种格式。 +print(f"\n{'Example 1':*^50}") snack_calories = { - 'chips': 140, - 'popcorn': 80, - 'nuts': 190, + 'chips': 140, + 'popcorn': 80, + 'nuts': 190, } items = tuple(snack_calories.items()) print(items) - -# Example 2 +# Example 2 --- 元组元素的索引访问 +print(f"\n{'Example 2':*^50}") item = ('Peanut butter', 'Jelly') first = item[0] second = item[1] print(first, 'and', second) - -# Example 3 +# Example 3 --- 元组的不可变性 +# 目的: 展示元组的不可变性,修改元组会抛出异常。 +# 解释: +# 元组是不可变的,不能修改其元素。 +# 尝试修改 pair[0] 会引发 TypeError,except 块捕获该异常,并记录日志。 +# 既然元组是不可变的,那么元组能看做常量么? +print(f"\n{'Example 3':*^50}") try: pair = ('Chocolate', 'Peanut butter') pair[0] = 'Honey' -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False - -# Example 4 +# Example 4 --- 元组解包 +#目的: 演示如何通过解包来提取元组的元素。 +# 解释: +# first, second = item 将元组 item 中的两个元素分别赋值给 first 和 second。 +# 这种方式比通过索引访问更简洁。 +print(f"\n{'Example 4':*^50}") item = ('Peanut butter', 'Jelly') first, second = item # Unpacking print(first, 'and', second) -# Example 5 +# Example 5 --- 多层嵌套元组解包 +# 目的: 演示如何进行多层嵌套的元组解包。 +# 解释: +# favorite_snacks.items() 返回字典的 (键, 值) 对,每个值本身是一个元组。 +# 通过嵌套解包,提取每个小吃的种类、名称和热量。 +# 这种多层解包可以一次性提取所有嵌套元组中的值。 +print(f"\n{'Example 5':*^50}") favorite_snacks = { 'salty': ('pretzels', 100), 'sweet': ('cookies', 180), @@ -95,7 +127,12 @@ def close_open_files(): print(f'Favorite {type3} is {name3} with {cals3} calories') -# Example 6 +# Example 6 --- 冒泡排序实现(不使用交换语法) +# 目的: 演示通过冒泡排序算法对列表进行排序(不使用 Python 的交换语法)。 +# 解释: +# 冒泡排序通过多次遍历列表,相邻元素比较并交换位置,将较大的元素逐渐“冒泡”到末尾。 +# 这里使用了临时变量 temp 进行交换操作。 +print(f"\n{'Example 6':*^50}") def bubble_sort(a): for _ in range(len(a)): for i in range(1, len(a)): @@ -109,7 +146,12 @@ def bubble_sort(a): print(names) -# Example 7 +# Example 7 --- 使用交换语法优化冒泡排序 +# 目的: 使用 Python 的交换语法优化冒泡排序。 +# 解释: +# 使用 a[i-1], a[i] = a[i], a[i-1] 来交换两个元素,简化了交换过程,不需要临时变量。 +# 这样写法更简洁,而且交换效率相同。 +print(f"\n{'Example 7':*^50}") def bubble_sort(a): for _ in range(len(a)): for i in range(1, len(a)): @@ -121,7 +163,12 @@ def bubble_sort(a): print(names) -# Example 8 +# Example 8 --- 传统方式遍历列表 +# 目的: 演示传统方式遍历列表并提取元素。 +# 解释: +# 通过 for i in range(len(snacks)) 迭代列表,手动使用索引访问每个元素。 +# snacks[i] 提取元组,再通过索引获取元组中的各个值。 +print(f"\n{'Example 8':*^50}") snacks = [('bacon', 350), ('donut', 240), ('muffin', 190)] for i in range(len(snacks)): item = snacks[i] @@ -130,6 +177,11 @@ def bubble_sort(a): print(f'#{i+1}: {name} has {calories} calories') -# Example 9 +# Example 9 --- 使用 enumerate() 遍历列表 +# 目的: 演示如何使用 enumerate() 函数遍历列表并同时获取索引和元素。 +# 解释: +# enumerate(snacks, 1) 生成 (索引, 元素) 对,从 1 开始计数。 +# 通过元组解包,直接提取 name 和 calories,不需要手动索引。 +print(f"\n{'Example 9':*^50}") for rank, (name, calories) in enumerate(snacks, 1): print(f'#{rank}: {name} has {calories} calories') From c09fd44acaee356537d164e1163c9c5acad09a0b Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Thu, 19 Sep 2024 22:01:59 +0800 Subject: [PATCH 09/59] modify item_06.py --- example_code/item_06.py | 7 ++++--- example_code/item_07.py | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/example_code/item_06.py b/example_code/item_06.py index 6864c55..b2aae0c 100755 --- a/example_code/item_06.py +++ b/example_code/item_06.py @@ -15,9 +15,10 @@ # limitations under the License. # Reproduce book environment - -# Prefer Multiple Assignment Unpacking Over Indexing -# 把数据结构直接拆分到多个变量中,避免通过下标索引来访问数据结构 +""" +Prefer Multiple Assignment Unpacking Over Indexing +把数据结构直接拆分到多个变量中,避免通过下标索引来访问数据结构 +""" import random import sys diff --git a/example_code/item_07.py b/example_code/item_07.py index c1f4239..9506e78 100755 --- a/example_code/item_07.py +++ b/example_code/item_07.py @@ -15,6 +15,12 @@ # limitations under the License. # Reproduce book environment + +""" +Prefer enumerate Over range +使用 enumerate 代替 range +""" + import random random.seed(1234) From 812f2be8729e3a4115ccf0c25589b8a879fc3b77 Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Fri, 20 Sep 2024 17:18:18 +0800 Subject: [PATCH 10/59] modify item_07.py,item_08.py,item_09.py --- example_code/item_07.py | 73 ++++++++++++++++++++++++++++++++---- example_code/item_08.py | 65 ++++++++++++++++++++++++++++---- example_code/item_09.py | 83 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 198 insertions(+), 23 deletions(-) diff --git a/example_code/item_07.py b/example_code/item_07.py index 9506e78..4297907 100755 --- a/example_code/item_07.py +++ b/example_code/item_07.py @@ -52,40 +52,97 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 生成随机的 32 位二进制数 +# 目的:演示如何通过位运算生成一个随机的 32 位二进制数。 +# 解释: +# random_bits = 0 初始化一个全为 0 的整数,准备通过随机位操作生成一个随机数。 +# for i in range(32) 循环 32 次,表示我们要生成一个 32 位数。 +# randint(0, 1) 随机生成 0 或 1,决定是否设置当前位为 1。 +# 1 << i 将数字 1 左移 i 位,表示要在第 i 位上放置一个 1。 +# random_bits |= 1 << i 使用按位或操作符 |=,将 1 << i 对应位置的 1 添加到 random_bits 中。 +# 结果:输出的是一个随机生成的 32 位二进制数,比如:0b11010011010101111001100111001110。 from random import randint - +print(f"\n{'Example 1':*^50}") random_bits = 0 for i in range(32): if randint(0, 1): random_bits |= 1 << i - print(bin(random_bits)) -# Example 2 +# Example 2 --- 遍历并打印列表元素 +# 目的:演示如何遍历列表并打印其中的元素。 +# 解释: +# flavor_list 是一个包含 4 种口味的字符串列表。 +# for flavor in flavor_list 逐一遍历 flavor_list 中的元素,并将其赋值给 flavor。 +# print(f'{flavor} is delicious') 打印每种口味,并附上 "is delicious"。 +# 结果:依次打印每种口味和它的描述,比如: +# vanilla is delicious +# chocolate is delicious +# pecan is delicious +# strawberry is delicious +print(f"\n{'Example 2':*^50}") flavor_list = ['vanilla', 'chocolate', 'pecan', 'strawberry'] for flavor in flavor_list: print(f'{flavor} is delicious') -# Example 3 +# Example 3 --- 使用索引遍历并打印列表元素 +# 目的:演示如何通过索引遍历列表,并打印元素和其对应的索引。 +# 解释: +# for i in range(len(flavor_list)) 创建了一个索引范围,i 是 flavor_list 的索引。 +# flavor = flavor_list[i] 通过索引 i 访问列表中的元素。 +# print(f'{i + 1}: {flavor}') 打印索引(加 1,使其从 1 开始)和对应的元素。 +# 结果:输出将每种口味按照它们在列表中的位置列出: +# 1: vanilla +# 2: chocolate +# 3: pecan +# 4: strawberry +print(f"\n{'Example 3':*^50}") for i in range(len(flavor_list)): flavor = flavor_list[i] print(f'{i + 1}: {flavor}') -# Example 4 +# Example 4 --- 使用 enumerate 获取迭代器并手动提取 +# 目的:展示如何使用 enumerate() 创建一个迭代器,并通过 next() 获取下一个元素。 +# 解释: +# it = enumerate(flavor_list) 使用 enumerate() 将 flavor_list 生成一个迭代器,返回 (索引, 元素) 对。 +# print(next(it)) 打印迭代器的下一个元素,next(it) 每次调用都会返回下一个 (索引, 元素) 对。 +# 结果:只打印了前两个 (索引, 元素) 对,比如: +# (0, 'vanilla') +# (1, 'chocolate') +print(f"\n{'Example 4':*^50}") it = enumerate(flavor_list) print(next(it)) print(next(it)) -# Example 5 +# Example 5 --- 使用 enumerate 遍历并打印带索引的元素 +# 目的:展示如何使用 enumerate() 通过索引和元素遍历列表。 +# 解释: +# for i, flavor in enumerate(flavor_list) 使用 enumerate() 将 flavor_list 生成的 (索引, 元素) 对解包成 i 和 flavor。 +# print(f'{i + 1}: {flavor}') 打印 i + 1 和对应的口味。 +# 结果:输出结果类似于 Example 3,但更简洁,不需要手动使用索引,比如: +# 1: vanilla +# 2: chocolate +# 3: pecan +# 4: strawberry +print(f"\n{'Example 5':*^50}") for i, flavor in enumerate(flavor_list): print(f'{i + 1}: {flavor}') -# Example 6 +# Example 6 --- 使用 enumerate 设置起始索引遍历列表 +# 目的:展示如何使用 enumerate() 设置起始索引值。 +# 解释: +# for i, flavor in enumerate(flavor_list, 1) 使用 enumerate(),并将起始索引设置为 1(默认从 0 开始)。 +# print(f'{i}: {flavor}') 打印索引 i 和对应的口味。 +# 结果:输出和 Example 5 相同,但 enumerate() 从 1 开始计数,比如: +# 1: vanilla +# 2: chocolate +# 3: pecan +# 4: strawberry +print(f"\n{'Example 6':*^50}") for i, flavor in enumerate(flavor_list, 1): print(f'{i}: {flavor}') diff --git a/example_code/item_08.py b/example_code/item_08.py index c22151c..8e7231f 100755 --- a/example_code/item_08.py +++ b/example_code/item_08.py @@ -15,6 +15,12 @@ # limitations under the License. # Reproduce book environment + +""" +Prefer zip and enumerate Over range and index +优先使用 zip 和 enumerate 代替 range 和索引进行迭代 +""" + import random random.seed(1234) @@ -46,26 +52,44 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 生成名字长度的列表 +# 目的:演示如何通过列表推导式计算每个名字的长度。 +# 解释: +# names 是一个包含名字的字符串列表。 +# counts 是一个列表推导式,计算 names 中每个名字的长度。 +# 结果:输出一个包含名字长度的列表,比如: +# [7, 4, 5] # Cecilia 长度 7,Lise 长度 4,Marie 长度 5 +print(f"\n{'Example 1':*^50}") names = ['Cecilia', 'Lise', 'Marie'] counts = [len(n) for n in names] print(counts) -# Example 2 +# Example 2 --- 通过索引遍历查找最长名字 +# 目的:演示如何通过索引遍历两个列表,查找长度最长的名字。 +# 解释: +# longest_name 和 max_count 初始化为 None 和 0,用于保存最长名字及其长度。 +# 通过索引 i 遍历 names 和 counts 列表,找到最长的名字。 +# 结果:输出最长的名字,比如: +# Cecilia +print(f"\n{'Example 2':*^50}") longest_name = None max_count = 0 - for i in range(len(names)): count = counts[i] if count > max_count: longest_name = names[i] max_count = count - print(longest_name) -# Example 3 +# Example 3 --- 使用 enumerate() 遍历列表 +# 目的:演示如何使用 enumerate() 通过索引和元素同时遍历列表。 +# 解释: +# 使用 enumerate() 遍历 names 列表,i 是索引,name 是元素。 +# count = counts[i] 获取 names 对应的名字长度,找到最长的名字。 +# 结果:验证 longest_name 是否为 Cecilia。 +print(f"\n{'Example 3':*^50}") longest_name = None max_count = 0 for i, name in enumerate(names): @@ -73,10 +97,17 @@ def close_open_files(): if count > max_count: longest_name = name max_count = count +print(longest_name) assert longest_name == 'Cecilia' -# Example 4 +# Example 4 --- 使用 zip() 同时遍历两个列表 +# 目的:演示如何使用 zip() 同时遍历 names 和 counts 两个列表。 +# 解释: +# 使用 zip(names, counts) 将两个列表打包成元组,分别获取名字和对应长度。 +# 通过比较 count,找到最长的名字。 +# 结果:验证 longest_name 是否为 Cecilia。 +print(f"\n{'Example 4':*^50}") longest_name = None max_count = 0 for name, count in zip(names, counts): @@ -86,13 +117,31 @@ def close_open_files(): assert longest_name == 'Cecilia' -# Example 5 +# Example 5 --- zip() 遍历时列表长度不一致的情况 +# 目的:演示当 zip() 遍历的两个列表长度不一致时的行为。 +# 解释: +# names 列表追加了 'Rosalind',而 counts 列表长度不变。 +# zip(names, counts) 只会遍历到较短的列表为止。 +# 结果:输出前 3 个名字,比如: +# Cecilia +# Lise +# Marie +print(f"\n{'Example 5':*^50}") names.append('Rosalind') for name, count in zip(names, counts): print(name) -# Example 6 +# Example 6 --- 使用 itertools.zip_longest() 处理长度不一致的情况 +# 目的:演示如何使用 itertools.zip_longest() 来处理两个列表长度不一致的情况。 +# 解释: +# itertools.zip_longest(names, counts) 可以在两个列表长度不一致时继续遍历,短的列表会使用 None 补齐。 +# 结果:输出每个名字及其长度,比如: +# Cecilia: 7 +# Lise: 4 +# Marie: 5 +# Rosalind: None # counts 中没有对应的长度,所以为 None +print(f"\n{'Example 6':*^50}") import itertools for name, count in itertools.zip_longest(names, counts): diff --git a/example_code/item_09.py b/example_code/item_09.py index d96d033..de182b6 100755 --- a/example_code/item_09.py +++ b/example_code/item_09.py @@ -15,6 +15,20 @@ # limitations under the License. # Reproduce book environment + +""" +Understand the Else Block in Loops +理解循环中的 Else 代码块 +# 全局描述: +# 主题:理解循环中的 Else 代码块 +# 描述:这段代码通过多个例子演示了 for 和 while 循环中的 else 代码块的行为,尤其是在循环正常完成和使用 break 提前退出时的区别。 +# 示例解释: +# Example 1 演示了 for 循环正常结束时 else 代码块的执行。 +# Example 2 介绍了使用 break 提前退出时,else 代码块不会执行。 +# Example 3 和 4 进一步展示了在空循环或 while False 的情况下,else 块仍然会运行。 +# Example 5 - 7 则深入到如何利用 for-else 逻辑检查两个数是否互质,以及将逻辑封装到函数中。 +""" + import random random.seed(1234) @@ -46,14 +60,32 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- for 循环中执行 Else 代码块 +# 目的:演示如何在 for 循环执行完所有迭代后运行 Else 代码块。 +# 解释: +# 当 for 循环顺利执行完时,Else 代码块会被触发。 +# 结果:for 循环完成 3 次迭代后,Else 代码块被触发。 +# 输出: +# Loop 0 +# Loop 1 +# Loop 2 +# Else block! +print(f"\n{'Example 1':*^50}") for i in range(3): print('Loop', i) else: print('Else block!') -# Example 2 +# Example 2 --- for 循环中使用 break +# 目的:演示在 for 循环中遇到 break 时,Else 代码块不会执行。 +# 解释: +# 当 for 循环中使用 break 提前终止时,Else 代码块不会被触发。 +# 结果:在第二次迭代时 break,Else 代码块不会被执行。 +# 输出: +# Loop 0 +# Loop 1 +print(f"\n{'Example 2':*^50}") for i in range(3): print('Loop', i) if i == 1: @@ -62,21 +94,46 @@ def close_open_files(): print('Else block!') -# Example 3 +# Example 3 --- for 循环迭代空列表 +# 目的:演示当 for 循环的可迭代对象为空时,直接执行 Else 代码块。 +# 解释: +# 如果 for 循环的可迭代对象是空的,循环体不会执行,但 Else 代码块会直接运行。 +# 结果:循环体不会执行,但 Else 代码块会被触发。 +# 输出: +# For Else block! +print(f"\n{'Example 3':*^50}") for x in []: print('Never runs') else: print('For Else block!') -# Example 4 +# Example 4 --- while 循环中的 Else 代码块 +# 目的:演示 while 循环条件为 False 时,直接执行 Else 代码块。 +# 解释: +# 当 while 循环条件一开始就是 False 时,Else 代码块会直接执行。 +# 结果:循环体不会执行,但 Else 代码块会被触发。 +# 输出: +# While Else block! +print(f"\n{'Example 4':*^50}") while False: print('Never runs') else: print('While Else block!') -# Example 5 +# Example 5 --- 检查两个数是否互质 +# 目的:演示如何使用 for 和 else 代码块检查两个数是否互质。 +# 解释: +# 通过 for 循环从 2 到 min(a, b),检查 a 和 b 是否有共同的因子。 +# 如果发现共同因子,直接 break;否则,Else 代码块表示 a 和 b 是互质数。 +# 结果: +# 4 和 9 是互质数,循环正常结束,Else 代码块执行。 +# 输出: +# Testing 2 +# Testing 3 +# Coprime +print(f"\n{'Example 5':*^50}") a = 4 b = 9 @@ -89,7 +146,13 @@ def close_open_files(): print('Coprime') -# Example 6 +# Example 6 --- 函数版互质判断 +# 目的:将检查互质的逻辑封装到函数中。 +# 解释: +# 函数 coprime(a, b) 使用 for 循环检查 a 和 b 是否有共同因子。 +# 如果找到共同因子,返回 False;否则返回 True,表示 a 和 b 是互质。 +# 结果:4 和 9 是互质,3 和 6 不是。 +print(f"\n{'Example 6':*^50}") def coprime(a, b): for i in range(2, min(a, b) + 1): if a % i == 0 and b % i == 0: @@ -100,7 +163,13 @@ def coprime(a, b): assert not coprime(3, 6) -# Example 7 +# Example 7 --- 使用布尔变量代替 else +# 目的:演示如何使用布尔变量代替 Else 代码块判断互质性。 +# 解释: +# 通过布尔变量 is_coprime 标识是否找到共同因子。如果找到,提前返回 False。 +# 否则,返回 is_coprime 的值,表示 a 和 b 是否互质。 +# 结果:4 和 9 是互质,3 和 6 不是。 +print(f"\n{'Example 7':*^50}") def coprime_alternate(a, b): is_coprime = True for i in range(2, min(a, b) + 1): From 927fdacc014861979f302f19f851a4629e0de44c Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 21 Sep 2024 14:41:47 +0800 Subject: [PATCH 11/59] modify item_10.py --- example_code/item_10.py | 112 ++++++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 15 deletions(-) diff --git a/example_code/item_10.py b/example_code/item_10.py index a106c38..9dc0c5c 100755 --- a/example_code/item_10.py +++ b/example_code/item_10.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +""" +Prevent Repetition with Assignment Expressions +使用赋值表达式防止代码重复 +描述: +这段代码展示了如何使用赋值表达式 := 来避免重复获取变量值的操作, +简化代码逻辑,尤其是在处理水果库存时的简化效果。 +""" + import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 创建水果库存字典 +# 目的:演示如何创建字典来存储水果库存。 +# 解释: +# fresh_fruit 是一个字典,保存了不同种类水果的数量。 +# 结果:水果库存分别为 10 个苹果,8 个香蕉,5 个柠檬。 +print(f"\n{'Example 1':*^50}") fresh_fruit = { 'apple': 10, 'banana': 8, @@ -54,7 +68,13 @@ def close_open_files(): } -# Example 2 +# Example 2 --- 获取柠檬库存并制作柠檬水 +# 目的:展示如何从字典中获取水果库存,并根据库存做出决定。 +# 解释: +# 使用 fresh_fruit.get('lemon', 0) 获取柠檬数量,默认值为 0。 +# 如果有柠檬,调用 make_lemonade(),否则调用 out_of_stock()。 +# 结果:根据柠檬库存调用不同的函数。 +print(f"\n{'Example 2':*^50}") def make_lemonade(count): print(f'Making {count} lemons into lemonade') @@ -68,14 +88,24 @@ def out_of_stock(): out_of_stock() -# Example 3 +# Example 3 --- 使用赋值表达式获取柠檬库存 +# 目的:演示如何使用赋值表达式简化变量的获取和判断。 +# 解释: +# 使用赋值表达式 := 同时进行赋值和判断,简化代码。 +# 结果:简化了库存判断的代码结构。 +print(f"\n{'Example 3':*^50}") if count := fresh_fruit.get('lemon', 0): make_lemonade(count) else: out_of_stock() -# Example 4 +# Example 4 --- 获取苹果库存并制作苹果汁 +# 目的:展示如何根据水果库存的条件执行特定操作。 +# 解释: +# 使用 fresh_fruit.get('apple', 0) 获取苹果数量,如果数量大于等于 4,就制作苹果汁。 +# 结果:根据苹果库存调用 make_cider() 或 out_of_stock()。 +print(f"\n{'Example 4':*^50}") def make_cider(count): print(f'Making cider with {count} apples') @@ -86,14 +116,25 @@ def make_cider(count): out_of_stock() -# Example 5 +# Example 5 --- 使用赋值表达式优化获取苹果库存的操作 +# 目的:演示如何使用赋值表达式优化条件判断和变量赋值。 +# 解释: +# 使用赋值表达式 := 同时获取苹果库存和进行条件判断,简化代码。 +# 结果:简化了代码结构。 +print(f"\n{'Example 5':*^50}") if (count := fresh_fruit.get('apple', 0)) >= 4: make_cider(count) else: out_of_stock() -# Example 6 +# Example 6 --- 制作香蕉奶昔(带异常处理) +# 目的:演示如何结合水果库存和异常处理进行操作。 +# 解释: +# 首先检查香蕉库存是否满足制作香蕉奶昔的条件,调用 slice_bananas() 函数切片。 +# 之后尝试制作奶昔,如果没有足够的香蕉,抛出 OutOfBananas 异常。 +# 结果:根据香蕉库存制作奶昔或处理库存不足的情况。 +print(f"\n{'Example 6':*^50}") def slice_bananas(count): print(f'Slicing {count} bananas') return count * 4 @@ -102,7 +143,7 @@ class OutOfBananas(Exception): pass def make_smoothies(count): - print(f'Making a smoothies with {count} banana slices') + print(f'Making a smoothie with {count} banana slices') pieces = 0 count = fresh_fruit.get('banana', 0) @@ -115,7 +156,12 @@ def make_smoothies(count): out_of_stock() -# Example 7 +# Example 7 --- 用 else 语句处理库存不足的情况 +# 目的:展示如何使用 else 语句处理水果库存不足的情况。 +# 解释: +# 如果水果库存不足,通过 else 语句将 pieces 赋值为 0 并进行异常处理。 +# 结果:在香蕉库存不足的情况下正常处理。 +print(f"\n{'Example 7':*^50}") count = fresh_fruit.get('banana', 0) if count >= 2: pieces = slice_bananas(count) @@ -128,7 +174,12 @@ def make_smoothies(count): out_of_stock() -# Example 8 +# Example 8 --- 使用赋值表达式优化水果库存检查 +# 目的:演示如何使用赋值表达式简化库存检查和变量赋值。 +# 解释: +# 使用赋值表达式 := 获取香蕉库存并判断是否制作香蕉奶昔。 +# 结果:代码简化,结构更清晰。 +print(f"\n{'Example 8':*^50}") pieces = 0 if (count := fresh_fruit.get('banana', 0)) >= 2: pieces = slice_bananas(count) @@ -139,7 +190,12 @@ def make_smoothies(count): out_of_stock() -# Example 9 +# Example 9 --- 综合使用赋值表达式和 else 语句 +# 目的:展示如何结合赋值表达式和 else 语句处理不同条件下的逻辑。 +# 解释: +# 使用赋值表达式获取香蕉库存,并通过 else 语句处理库存不足的情况。 +# 结果:结构简洁明了,处理不同条件下的水果库存。 +print(f"\n{'Example 9':*^50}") if (count := fresh_fruit.get('banana', 0)) >= 2: pieces = slice_bananas(count) else: @@ -151,7 +207,12 @@ def make_smoothies(count): out_of_stock() -# Example 10 +# Example 10 --- 处理多个水果库存 +# 目的:演示如何处理多种水果库存并选择合适的操作。 +# 解释: +# 首先检查香蕉库存,如果不足再检查苹果,最后检查柠檬库存。 +# 结果:根据水果库存依次制作奶昔、苹果汁或柠檬水。 +print(f"\n{'Example 10':*^50}") count = fresh_fruit.get('banana', 0) if count >= 2: pieces = slice_bananas(count) @@ -168,7 +229,12 @@ def make_smoothies(count): to_enjoy = 'Nothing' -# Example 11 +# Example 11 --- 使用赋值表达式处理多个水果库存 +# 目的:演示如何使用赋值表达式优化多种水果库存的处理逻辑。 +# 解释: +# 首先检查香蕉库存,如果足够多则制作香蕉奶昔,否则检查苹果和柠檬库存,依次决定制作苹果汁或柠檬水。 +# 结果:根据水果库存依次制作对应的饮品,如果没有足够水果,返回 'Nothing'。 +print(f"\n{'Example 11':*^50}") if (count := fresh_fruit.get('banana', 0)) >= 2: pieces = slice_bananas(count) to_enjoy = make_smoothies(pieces) @@ -180,7 +246,13 @@ def make_smoothies(count): to_enjoy = 'Nothing' -# Example 12 +# Example 12 --- 模拟从多个水果中选择并制作果汁 +# 目的:演示如何从一系列水果中逐个取出并制作果汁。 +# 解释: +# FRUIT_TO_PICK 是一个列表,包含了多个水果的库存。 +# pick_fruit() 从列表中取出一个字典(表示一种水果及其数量),并在空时返回空列表。 +# 结果:将每次制作的果汁添加到 bottles 列表中,最后打印所有制作的果汁。 +print(f"\n{'Example 12':*^50}") FRUIT_TO_PICK = [ {'apple': 1, 'banana': 3}, {'lemon': 2, 'lime': 5}, @@ -207,7 +279,12 @@ def make_juice(fruit, count): print(bottles) -# Example 13 +# Example 13 --- 使用 while 循环和 break 来控制果汁制作过程 +# 目的:演示如何使用无限循环和 break 语句处理果汁制作的过程。 +# 解释: +# 使用 while True 创建无限循环,通过在列表为空时调用 break 提前退出循环。 +# 结果:每次从 FRUIT_TO_PICK 中取出一种水果,制作果汁并添加到 bottles 列表中,最后打印结果。 +print(f"\n{'Example 13':*^50}") FRUIT_TO_PICK = [ {'apple': 1, 'banana': 3}, {'lemon': 2, 'lime': 5}, @@ -226,7 +303,12 @@ def make_juice(fruit, count): print(bottles) -# Example 14 +# Example 14 --- 使用赋值表达式优化果汁制作循环 +# 目的:演示如何使用赋值表达式简化 while 循环的逻辑。 +# 解释: +# 使用赋值表达式同时获取 fresh_fruit 并进行判断,避免了在 while 循环中使用 break 提前退出。 +# 结果:简化了无限循环的逻辑,同时保持了同样的功能。 +print(f"\n{'Example 14':*^50}") FRUIT_TO_PICK = [ {'apple': 1, 'banana': 3}, {'lemon': 2, 'lime': 5}, From 6786a39d937ac21b2740ec8704fbb5c0879c2230 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 21 Sep 2024 16:14:55 +0800 Subject: [PATCH 12/59] modify item_08.py --- example_code/item_08.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/example_code/item_08.py b/example_code/item_08.py index 8e7231f..e5cb59a 100755 --- a/example_code/item_08.py +++ b/example_code/item_08.py @@ -110,6 +110,8 @@ def close_open_files(): print(f"\n{'Example 4':*^50}") longest_name = None max_count = 0 +# [('Cecilia', 7), ('Lise', 4), ('Marie', 5)] 这个厉害的,居然是打包成这个样子 +print(list(zip(names, counts))) for name, count in zip(names, counts): if count > max_count: longest_name = name @@ -128,6 +130,8 @@ def close_open_files(): # Marie print(f"\n{'Example 5':*^50}") names.append('Rosalind') +print(f"names ::: {names}") +print(f"zip(names,counts) ::: {list(zip(names,counts))}") for name, count in zip(names, counts): print(name) @@ -141,6 +145,7 @@ def close_open_files(): # Lise: 4 # Marie: 5 # Rosalind: None # counts 中没有对应的长度,所以为 None +# 针对上面的一个场景特例的处理应对机制 print(f"\n{'Example 6':*^50}") import itertools From ab2c4594ad942502879f0b0f277fd000f02193b7 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 23 Sep 2024 09:54:41 +0800 Subject: [PATCH 13/59] modify item_09.py --- example_code/item_09.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/example_code/item_09.py b/example_code/item_09.py index de182b6..921d4e4 100755 --- a/example_code/item_09.py +++ b/example_code/item_09.py @@ -21,7 +21,9 @@ 理解循环中的 Else 代码块 # 全局描述: # 主题:理解循环中的 Else 代码块 -# 描述:这段代码通过多个例子演示了 for 和 while 循环中的 else 代码块的行为,尤其是在循环正常完成和使用 break 提前退出时的区别。 +# 描述: +# 这段代码通过多个例子演示了 for 和 while 循环中的 else 代码块的行为, +# 尤其是在循环正常完成和使用 break 提前退出时的区别。 # 示例解释: # Example 1 演示了 for 循环正常结束时 else 代码块的执行。 # Example 2 介绍了使用 break 提前退出时,else 代码块不会执行。 @@ -158,7 +160,8 @@ def coprime(a, b): if a % i == 0 and b % i == 0: return False return True - +print(f"coprime(4, 9) ::: {coprime(4, 9)}") +print(f"coprime(3, 6) ::: {coprime(3, 6)}") assert coprime(4, 9) assert not coprime(3, 6) @@ -177,6 +180,7 @@ def coprime_alternate(a, b): is_coprime = False break return is_coprime - +print(f"coprime_alternate(4, 9) ::: {coprime_alternate(4, 9)}") +print(f"coprime_alternate(3, 6) ::: {coprime_alternate(3, 6)}") assert coprime_alternate(4, 9) assert not coprime_alternate(3, 6) From 3a63a2c6268fff0385aa9247adcc8ed5e40f36b8 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 23 Sep 2024 11:06:45 +0800 Subject: [PATCH 14/59] modify item_11-18.py --- example_code/item_11.py | 94 +++++++++++++++++++++++++++---- example_code/item_12.py | 58 ++++++++++++++++--- example_code/item_13.py | 100 ++++++++++++++++++++++++++++----- example_code/item_14.py | 121 ++++++++++++++++++++++++++++++++++------ example_code/item_15.py | 119 ++++++++++++++++++++++++++++++++------- example_code/item_16.py | 93 ++++++++++++++++++++++++++---- example_code/item_17.py | 47 ++++++++++++++-- example_code/item_18.py | 68 +++++++++++++++++----- 8 files changed, 598 insertions(+), 102 deletions(-) diff --git a/example_code/item_11.py b/example_code/item_11.py index f502f34..4679b85 100755 --- a/example_code/item_11.py +++ b/example_code/item_11.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 11: Use Slicing to Access List Subsections +# 军规 11: 使用切片访问列表的子部分 + +""" +Use Slicing to Access List Subsections +使用切片访问列表的子部分 +""" + import random random.seed(1234) @@ -46,21 +55,42 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 使用切片提取列表的子部分 +# 目的:演示如何通过切片获取列表的中间部分或去掉两端的元素。 +# 解释: +# a[3:5] 提取列表索引 3 和 4 的元素。 +# a[1:7] 提取索引从 1 到 6 的元素,去掉首尾。 +# 结果:返回列表的相应子部分。 +print(f"\n{'Example 1':*^50}") a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] print('Middle two: ', a[3:5]) print('All but ends:', a[1:7]) -# Example 2 +# Example 2 --- 使用缺省开始索引提取前几项 +# 目的:演示切片时缺省的起始索引会自动从 0 开始。 +# 解释: +# a[:5] 等价于 a[0:5],提取列表的前 5 项。 +# 结果:a[:5] 和 a[0:5] 完全相同。 +print(f"\n{'Example 2':*^50}") assert a[:5] == a[0:5] -# Example 3 +# Example 3 --- 使用缺省结束索引提取剩余部分 +# 目的:演示切片时缺省的结束索引会自动扩展到列表末尾。 +# 解释: +# a[5:] 等价于 a[5:len(a)],从索引 5 开始,提取到列表末尾。 +# 结果:a[5:] 和 a[5:len(a)] 完全相同。 +print(f"\n{'Example 3':*^50}") assert a[5:] == a[5:len(a)] -# Example 4 +# Example 4 --- 各种切片用法示例 +# 目的:展示多种切片用法,使用正索引和负索引。 +# 解释: +# 使用正负索引和不同的起始、结束位置,提取列表的不同子部分。 +# 结果:输出对应的切片结果。 +print(f"\n{'Example 4':*^50}") print(a[:]) print(a[:5]) print(a[:-1]) @@ -71,7 +101,12 @@ def close_open_files(): print(a[-3:-1]) -# Example 5 +# Example 5 --- 切片结果对照 +# 目的:通过注释展示不同切片的结果。 +# 解释: +# a[:] 提取整个列表,a[:5] 提取前 5 项,a[4:] 从索引 4 提取到末尾,等等。 +# 结果:展示各种切片的结果。 +print(f"\n{'Example 5':*^50}") a[:] # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] a[:5] # ['a', 'b', 'c', 'd', 'e'] a[:-1] # ['a', 'b', 'c', 'd', 'e', 'f', 'g'] @@ -82,12 +117,22 @@ def close_open_files(): a[-3:-1] # ['f', 'g'] -# Example 6 +# Example 6 --- 切片索引越界 +# 目的:展示当切片索引超出列表长度时的行为。 +# 解释: +# 尽管列表长度不够 20,a[:20] 和 a[-20:] 不会引发错误,而是返回现有的部分。 +# 结果:超出索引范围的切片自动处理,不抛出异常。 +print(f"\n{'Example 6':*^50}") first_twenty_items = a[:20] last_twenty_items = a[-20:] -# Example 7 +# Example 7 --- 列表索引越界时引发异常 +# 目的:展示当使用单个索引超出列表长度时会引发 IndexError。 +# 解释: +# a[20] 超出列表索引范围,抛出 IndexError 异常。 +# 结果:捕获并记录异常信息。 +print(f"\n{'Example 7':*^50}") try: a[20] except: @@ -96,7 +141,12 @@ def close_open_files(): assert False -# Example 8 +# Example 8 --- 切片与原列表无关 +# 目的:演示切片是创建新列表,与原列表没有关联。 +# 解释: +# b 是 a 的切片,修改 b 的元素不会影响 a。 +# 结果:修改 b 后,a 保持不变。 +print(f"\n{'Example 8':*^50}") b = a[3:] print('Before: ', b) b[1] = 99 @@ -104,24 +154,44 @@ def close_open_files(): print('No change:', a) -# Example 9 +# Example 9 --- 用切片替换列表部分内容 +# 目的:展示如何通过切片替换列表中的部分内容。 +# 解释: +# a[2:7] 替换为 [99, 22, 14],覆盖索引 2 到 6 的部分内容。 +# 结果:替换后列表 a 发生变化。 +print(f"\n{'Example 9':*^50}") print('Before ', a) a[2:7] = [99, 22, 14] print('After ', a) -# Example 10 +# Example 10 --- 替换单个元素为多个元素 +# 目的:展示如何通过切片替换单个元素为多个元素。 +# 解释: +# a[2:3] 用 [47, 11] 替换,将索引 2 的元素替换为 47 和 11。 +# 结果:列表长度增加。 +print(f"\n{'Example 10':*^50}") print('Before ', a) a[2:3] = [47, 11] print('After ', a) -# Example 11 +# Example 11 --- 通过切片复制列表 +# 目的:展示如何通过切片复制整个列表。 +# 解释: +# b = a[:] 复制列表 a,b 是新列表,但内容相同。 +# 结果:b 和 a 内容相同,但不是同一个对象。 +print(f"\n{'Example 11':*^50}") b = a[:] assert b == a and b is not a -# Example 12 +# Example 12 --- 切片赋值影响列表对象 +# 目的:演示当通过切片赋值时,列表对象仍然保持相同。 +# 解释: +# b = a 使 a 和 b 指向同一个列表对象,修改 a 的内容会影响 b。 +# 结果:a 和 b 都发生了内容变化,但它们仍然是同一个列表对象。 +print(f"\n{'Example 12':*^50}") b = a print('Before a', a) print('Before b', b) diff --git a/example_code/item_12.py b/example_code/item_12.py index 7744737..0cfcc4f 100755 --- a/example_code/item_12.py +++ b/example_code/item_12.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 12: Avoid Striding and Slicing in a Single Expression +# 军规 12: 避免在一个表达式中同时使用步长和切片 + +""" +Avoid Striding and Slicing in a Single Expression +避免在一个表达式中同时使用步长和切片 +""" + import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 使用步长切片提取奇偶项 +# 目的:演示如何通过步长切片提取列表的奇数和偶数项。 +# 解释: +# x[::2] 提取列表中的奇数项(步长为 2),x[1::2] 提取偶数项。 +# 结果:分别输出奇数项和偶数项。 +print(f"\n{'Example 1':*^50}") x = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'] odds = x[::2] evens = x[1::2] @@ -54,19 +68,34 @@ def close_open_files(): print(evens) -# Example 2 +# Example 2 --- 对字节串使用步长切片 +# 目的:展示如何使用步长切片反转字节串。 +# 解释: +# 对字节串 x 进行切片操作 x[::-1],可以反转字节串。 +# 结果:输出字节串 'mongoose' 的反转版本。 +print(f"\n{'Example 2':*^50}") x = b'mongoose' y = x[::-1] print(y) -# Example 3 +# Example 3 --- 对字符串使用步长切片 +# 目的:展示如何对字符串进行步长切片。 +# 解释: +# 字符串和字节串类似,使用 x[::-1] 可以反转整个字符串。 +# 结果:输出字符串 '寿司' 的反转结果。 +print(f"\n{'Example 3':*^50}") x = '寿司' y = x[::-1] print(y) -# Example 4 +# Example 4 --- 对字节串使用步长切片引发编码错误 +# 目的:演示对字节串进行反转后尝试解码为 UTF-8 时引发的错误。 +# 解释: +# 对 UTF-8 编码的字节串进行步长切片后,字节顺序会被打乱,导致解码失败。 +# 结果:引发 UnicodeDecodeError,记录异常。 +print(f"\n{'Example 4':*^50}") try: w = '寿司' x = w.encode('utf-8') @@ -78,20 +107,35 @@ def close_open_files(): assert False -# Example 5 +# Example 5 --- 更多步长切片示例 +# 目的:演示如何通过步长正反向切片获取列表的不同子集。 +# 解释: +# x[::2] 提取列表的奇数项,x[::-2] 提取反向偶数项。 +# 结果:分别输出对应的切片结果。 +print(f"\n{'Example 5':*^50}") x = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] x[::2] # ['a', 'c', 'e', 'g'] x[::-2] # ['h', 'f', 'd', 'b'] -# Example 6 +# Example 6 --- 结合步长和切片操作 +# 目的:展示如何通过结合步长和切片操作获取列表的不同部分。 +# 解释: +# 通过不同的起始、结束位置和步长,提取列表的不同部分。 +# 结果:分别展示了各种步长切片的结果。 +print(f"\n{'Example 6':*^50}") x[2::2] # ['c', 'e', 'g'] x[-2::-2] # ['g', 'e', 'c', 'a'] x[-2:2:-2] # ['g', 'e'] x[2:2:-2] # [] -# Example 7 +# Example 7 --- 步长切片的多层切片 +# 目的:展示步长切片的多层操作。 +# 解释: +# 通过对步长切片 y 再次进行切片,提取其中的一部分元素。 +# 结果:分别展示原始列表 x 和步长切片 y 及其子集 z。 +print(f"\n{'Example 7':*^50}") y = x[::2] # ['a', 'c', 'e', 'g'] z = y[1:-1] # ['c', 'e'] print(x) diff --git a/example_code/item_13.py b/example_code/item_13.py index d2b9349..0fcc04a 100755 --- a/example_code/item_13.py +++ b/example_code/item_13.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 13: Prefer Unpacking Over Indexing +# 军规 13: 优先使用解包操作代替索引访问 + +""" +Prefer Unpacking Over Indexing +优先使用解包操作代替索引访问 +""" + import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 解包操作不足引发异常 +# 目的:展示当解包操作无法分配足够值时会引发错误。 +# 解释: +# car_ages_descending 列表有 10 个元素,但只尝试解包两个值,导致 ValueError。 +# 结果:记录异常,程序不会崩溃。 +print(f"\n{'Example 1':*^50}") try: car_ages = [0, 9, 4, 8, 7, 20, 19, 1, 6, 15] car_ages_descending = sorted(car_ages, reverse=True) @@ -57,19 +71,35 @@ def close_open_files(): assert False -# Example 2 +# Example 2 --- 使用索引访问列表元素 +# 目的:演示通过索引访问列表的前两个元素和剩余部分。 +# 解释: +# 使用 car_ages_descending[0] 和 car_ages_descending[1] 获取前两个元素,其余部分使用切片获取。 +# 结果:输出最老和次老的车辆,以及剩余车辆。 +print(f"\n{'Example 2':*^50}") oldest = car_ages_descending[0] second_oldest = car_ages_descending[1] others = car_ages_descending[2:] print(oldest, second_oldest, others) -# Example 3 +# Example 3 --- 使用解包替代索引访问 +# 目的:展示通过解包操作替代索引访问,获取前两个元素和剩余部分。 +# 解释: +# oldest, second_oldest, *others 通过解包操作一次性获取列表的前两个元素和剩余部分。 +# 结果:输出最老和次老的车辆,以及剩余车辆。 +print(f"\n{'Example 3':*^50}") oldest, second_oldest, *others = car_ages_descending print(oldest, second_oldest, others) -# Example 4 +# Example 4 --- 不同的解包方式 +# 目的:展示如何通过解包操作提取列表的头尾元素和中间部分。 +# 解释: +# oldest, *others, youngest 获取列表的首尾元素,中间部分存入 others。 +# *others, second_youngest, youngest 获取列表的最后两个元素和剩余部分。 +# 结果:分别输出首尾元素和中间部分,以及最后两个元素。 +print(f"\n{'Example 4':*^50}") oldest, *others, youngest = car_ages_descending print(oldest, youngest, others) @@ -77,7 +107,12 @@ def close_open_files(): print(youngest, second_youngest, others) -# Example 5 +# Example 5 --- 不允许只有剩余值的解包 +# 目的:展示当解包操作只使用剩余值部分时会引发错误。 +# 解释: +# *others = car_ages_descending 这种解包操作无效,必须有至少一个显式的变量。 +# 结果:引发 SyntaxError 异常,记录并处理。 +print(f"\n{'Example 5':*^50}") try: # This will not compile source = """*others = car_ages_descending""" @@ -88,7 +123,12 @@ def close_open_files(): assert False -# Example 6 +# Example 6 --- 不允许多个剩余值解包 +# 目的:展示当解包操作中有多个剩余值时会引发错误。 +# 解释: +# first, *middle, *second_middle, last = [1, 2, 3, 4] 是无效的语法,因为解包中只能有一个剩余值变量。 +# 结果:引发 SyntaxError 异常,记录并处理。 +print(f"\n{'Example 6':*^50}") try: # This will not compile source = """first, *middle, *second_middle, last = [1, 2, 3, 4]""" @@ -99,7 +139,12 @@ def close_open_files(): assert False -# Example 7 +# Example 7 --- 在嵌套结构中使用解包 +# 目的:展示如何在嵌套结构中使用解包操作提取多个值。 +# 解释: +# car_inventory 是一个嵌套结构,解包 loc1, best1, *rest1 提取位置和最佳汽车,剩余汽车存入 rest。 +# 结果:输出两个地点的最佳汽车和剩余汽车数量。 +print(f"\n{'Example 7':*^50}") car_inventory = { 'Downtown': ('Silver Shadow', 'Pinto', 'DMC'), 'Airport': ('Skyline', 'Viper', 'Gremlin', 'Nova'), @@ -112,19 +157,36 @@ def close_open_files(): print(f'Best at {loc2} is {best2}, {len(rest2)} others') -# Example 8 +# Example 8 --- 处理解包不足的情况 +# 目的:展示当列表元素不足时如何处理解包操作。 +# 解释: +# short_list 只有两个元素,但通过 *rest 可以避免解包失败,剩余部分为 []。 +# 结果:输出前两个元素和剩余部分(空列表)。 +print(f"\n{'Example 8':*^50}") short_list = [1, 2] first, second, *rest = short_list print(first, second, rest) -# Example 9 +# Example 9 --- 迭代器无法自动解包 +# 目的:展示迭代器无法直接通过解包操作获取多个元素。 +# 解释: +# iter(range(1, 3)) 是一个迭代器,不能像列表那样直接解包多个值。 +# 结果:引发 TypeError 异常。 +print(f"\n{'Example 9':*^50}") it = iter(range(1, 3)) -first, second = it -print(f'{first} and {second}') +try: + first, second = it +except TypeError as e: + print(f"Error: {e}") -# Example 10 +# Example 10 --- 使用生成器生成 CSV 行 +# 目的:演示如何通过生成器动态生成 CSV 行数据。 +# 解释: +# generate_csv() 是一个生成器,逐行生成 CSV 数据,可以节省内存。 +# 结果:生成 CSV 的每一行,包括标题和 100 条数据。 +print(f"\n{'Example 10':*^50}") def generate_csv(): yield ('Date', 'Make' , 'Model', 'Year', 'Price') for i in range(100): @@ -132,7 +194,12 @@ def generate_csv(): yield ('2019-03-26', 'Ford', 'F150' , '2008', '$2400') -# Example 11 +# Example 11 --- 从生成器中提取 CSV 数据 +# 目的:展示如何将生成器的结果转换为列表,并通过解包提取标题和数据。 +# 解释: +# all_csv_rows 列表存储生成器生成的所有行,header 保存标题行,rows 保存剩余数据。 +# 结果:输出 CSV 的标题和数据行数。 +print(f"\n{'Example 11':*^50}") all_csv_rows = list(generate_csv()) header = all_csv_rows[0] rows = all_csv_rows[1:] @@ -140,7 +207,12 @@ def generate_csv(): print('Row count: ', len(rows)) -# Example 12 +# Example 12 --- 直接从生成器解包提取 CSV 数据 +# 目的:演示如何直接从生成器解包提取标题和数据。 +# 解释: +# 通过解包操作从生成器 it 中提取标题行和剩余数据行。 +# 结果:输出 CSV 的标题和数据行数。 +print(f"\n{'Example 12':*^50}") it = generate_csv() header, *rows = it print('CSV Header:', header) diff --git a/example_code/item_14.py b/example_code/item_14.py index 271663a..2867e16 100755 --- a/example_code/item_14.py +++ b/example_code/item_14.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 14: Sort by Complex Criteria Using the key Parameter +# 军规 14: 使用 key 参数根据复杂标准排序 + +""" +Sort by Complex Criteria Using the key Parameter +使用 key 参数根据复杂标准排序 +""" + import random random.seed(1234) @@ -46,13 +55,23 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 简单数字排序 +# 目的:演示对数字列表进行排序。 +# 解释: +# numbers.sort() 直接对数字列表进行排序,默认升序排列。 +# 结果:输出排序后的数字列表。 +print(f"\n{'Example 1':*^50}") numbers = [93, 86, 11, 68, 70] numbers.sort() print(numbers) -# Example 2 +# Example 2 --- 创建一个自定义类 Tool +# 目的:展示如何定义一个类,并创建该类的实例列表。 +# 解释: +# Tool 类包含 name 和 weight 属性,__repr__ 方法用于打印对象的详细信息。 +# 结果:创建工具对象,并将其存入列表。 +print(f"\n{'Example 2':*^50}") class Tool: def __init__(self, name, weight): self.name = name @@ -69,7 +88,12 @@ def __repr__(self): ] -# Example 3 +# Example 3 --- 无法直接对自定义对象排序 +# 目的:展示无法对没有定义比较方法的自定义类对象排序。 +# 解释: +# tools.sort() 尝试对 Tool 对象进行排序,但 Tool 类没有定义排序的标准,导致 TypeError。 +# 结果:捕获并记录异常信息。 +print(f"\n{'Example 3':*^50}") try: tools.sort() except: @@ -78,18 +102,33 @@ def __repr__(self): assert False -# Example 4 +# Example 4 --- 使用 key 参数按名称排序 +# 目的:展示如何使用 key 参数按对象的某个属性进行排序。 +# 解释: +# tools.sort(key=lambda x: x.name) 根据工具的 name 属性对工具进行排序。 +# 结果:输出按名称升序排序的工具列表。 +print(f"\n{'Example 4':*^50}") print('Unsorted:', repr(tools)) tools.sort(key=lambda x: x.name) print('\nSorted: ', tools) -# Example 5 +# Example 5 --- 按重量排序 +# 目的:展示如何使用 key 参数根据工具的重量排序。 +# 解释: +# tools.sort(key=lambda x: x.weight) 按重量升序排序。 +# 结果:输出按重量排序后的工具列表。 +print(f"\n{'Example 5':*^50}") tools.sort(key=lambda x: x.weight) print('By weight:', tools) -# Example 6 +# Example 6 --- 大小写敏感和不敏感排序 +# 目的:展示如何对字符串进行大小写敏感和不敏感的排序。 +# 解释: +# places.sort() 默认大小写敏感,places.sort(key=lambda x: x.lower()) 忽略大小写。 +# 结果:输出大小写敏感和不敏感的排序结果。 +print(f"\n{'Example 6':*^50}") places = ['home', 'work', 'New York', 'Paris'] places.sort() print('Case sensitive: ', places) @@ -97,7 +136,12 @@ def __repr__(self): print('Case insensitive:', places) -# Example 7 +# Example 7 --- 创建电动工具列表 +# 目的:创建一个新的工具列表用于后续的排序演示。 +# 解释: +# 创建了一个 power_tools 列表,存储不同重量的电动工具。 +# 结果:电动工具列表已创建。 +print(f"\n{'Example 7':*^50}") power_tools = [ Tool('drill', 4), Tool('circular saw', 5), @@ -106,13 +150,23 @@ def __repr__(self): ] -# Example 8 +# Example 8 --- 直接比较元组 +# 目的:展示如何通过元组的元素逐个比较大小。 +# 解释: +# 元组比较会首先比较第一个元素,如果相等,再比较第二个元素。 +# 结果:验证 (40, 'jackhammer') 比 (5, 'circular saw') 大。 +print(f"\n{'Example 8':*^50}") saw = (5, 'circular saw') jackhammer = (40, 'jackhammer') assert not (jackhammer < saw) # Matches expectations -# Example 9 +# Example 9 --- 元组的多层比较 +# 目的:展示在元组中如何根据多个字段进行比较。 +# 解释: +# drill 和 sander 的重量相同,因此会比较它们的名称。 +# 结果:验证 drill 排在 sander 之前,因为字母顺序在前。 +print(f"\n{'Example 9':*^50}") drill = (4, 'drill') sander = (4, 'sander') assert drill[0] == sander[0] # Same weight @@ -120,23 +174,43 @@ def __repr__(self): assert drill < sander # Thus, drill comes first -# Example 10 +# Example 10 --- 根据多个标准排序 +# 目的:展示如何根据多个标准对对象进行排序。 +# 解释: +# power_tools.sort(key=lambda x: (x.weight, x.name)) 首先根据重量排序,如果重量相同,则根据名称排序。 +# 结果:输出按重量和名称排序的结果。 +print(f"\n{'Example 10':*^50}") power_tools.sort(key=lambda x: (x.weight, x.name)) print(power_tools) -# Example 11 +# Example 11 --- 反向排序 +# 目的:展示如何对多个标准的排序结果进行反向排序。 +# 解释: +# 使用 reverse=True 对排序结果进行反转,使所有标准的排序都变为降序。 +# 结果:输出按重量和名称降序排列的结果。 +print(f"\n{'Example 11':*^50}") power_tools.sort(key=lambda x: (x.weight, x.name), reverse=True) # Makes all criteria descending print(power_tools) -# Example 12 +# Example 12 --- 使用负数实现部分反向排序 +# 目的:展示如何通过对数值使用负号实现部分标准的降序排序。 +# 解释: +# power_tools.sort(key=lambda x: (-x.weight, x.name)) 使重量降序,名称升序。 +# 结果:输出按重量降序、名称升序排列的结果。 +print(f"\n{'Example 12':*^50}") power_tools.sort(key=lambda x: (-x.weight, x.name)) print(power_tools) -# Example 13 +# Example 13 --- 处理无效的标准组合 +# 目的:展示当排序标准无效时会引发错误。 +# 解释: +# lambda x: (x.weight, -x.name) 试图对字符串使用负号操作是无效的,导致 TypeError。 +# 结果:捕获并记录异常。 +print(f"\n{'Example 13':*^50}") try: power_tools.sort(key=lambda x: (x.weight, -x.name), reverse=True) @@ -146,7 +220,12 @@ def __repr__(self): assert False -# Example 14 +# Example 14 --- 通过多次调用 sort 实现复杂排序 +# 目的:展示如何通过多次调用 sort 方法实现复杂的排序。 +# 解释: +# 先按名称升序排序,然后按重量降序排序。 +# 结果:输出最终排序结果。 +print(f"\n{'Example 14':*^50}") power_tools.sort(key=lambda x: x.name) # Name ascending power_tools.sort(key=lambda x: x.weight, # Weight descending @@ -155,12 +234,22 @@ def __repr__(self): print(power_tools) -# Example 15 +# Example 15 --- 按名称排序 +# 目的:展示按单一标准(名称)进行排序。 +# 解释: +# power_tools.sort(key=lambda x: x.name) 按名称升序排序。 +# 结果:输出按名称排序的结果。 +print(f"\n{'Example 15':*^50}") power_tools.sort(key=lambda x: x.name) print(power_tools) -# Example 16 +# Example 16 --- 按重量降序排序 +# 目的:展示按单一标准(重量)降序排序。 +# 解释: +# power_tools.sort(key=lambda x: x.weight, reverse=True) 按重量降序排序。 +# 结果:输出按重量降序排列的结果。 +print(f"\n{'Example 16':*^50}") power_tools.sort(key=lambda x: x.weight, reverse=True) print(power_tools) diff --git a/example_code/item_15.py b/example_code/item_15.py index ef1ab90..69710fd 100755 --- a/example_code/item_15.py +++ b/example_code/item_15.py @@ -15,7 +15,17 @@ # limitations under the License. # Reproduce book environment + +# 军规 15: Know How to Use the key Parameter to Sort Dictionaries +# 军规 15: 理解如何使用 key 参数对字典进行排序 + +""" +Know How to Use the key Parameter to Sort Dictionaries +理解如何使用 key 参数对字典进行排序 +""" + import random + random.seed(1234) import logging @@ -37,58 +47,93 @@ atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) + def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() -atexit.register(close_open_files) +atexit.register(close_open_files) -# Example 2 +# Example 2 --- 创建并打印字典 +# 目的:演示如何创建一个简单的字典,并打印出字典内容。 +# 解释: +# baby_names 是一个字典,存储宠物名称和它们的幼崽名。 +# 结果:输出字典内容。 +print(f"\n{'Example 2':*^50}") baby_names = { 'cat': 'kitten', 'dog': 'puppy', } print(baby_names) - -# Example 4 +# Example 4 --- 获取字典的键、值、项,并使用 popitem 移除最后一项 +# 目的:展示如何获取字典的键、值、项列表,并使用 popitem 移除最后插入的项。 +# 解释: +# keys() 返回字典的键,values() 返回值,items() 返回键值对。 +# popitem() 移除并返回字典中最后插入的键值对。 +# 结果:分别输出字典的键、值、项列表,最后移除并打印最后一项。 +print(f"\n{'Example 4':*^50}") print(list(baby_names.keys())) print(list(baby_names.values())) print(list(baby_names.items())) print(baby_names.popitem()) # Last item inserted +# Example 6 --- 使用关键字参数的函数 +# 目的:演示如何使用 **kwargs 关键字参数接收任意数量的命名参数。 +# 解释: +# my_func 使用 **kwargs 关键字参数,遍历并打印每个参数的键值对。 +# 结果:输出 'goose' 和 'kangaroo' 的幼崽名称。 +print(f"\n{'Example 6':*^50}") + -# Example 6 def my_func(**kwargs): for key, value in kwargs.items(): print(f'{key} = {value}') + my_func(goose='gosling', kangaroo='joey') +# Example 8 --- 使用类中的 __dict__ 属性获取实例的属性和值 +# 目的:展示如何通过 __dict__ 属性获取实例的属性和值。 +# 解释: +# MyClass 包含两个属性,使用 __dict__.items() 遍历实例的属性和值。 +# 结果:输出实例的所有属性和值。 +print(f"\n{'Example 8':*^50}") + -# Example 8 class MyClass: def __init__(self): self.alligator = 'hatchling' self.elephant = 'calf' + a = MyClass() for key, value in a.__dict__.items(): print(f'{key} = {value}') - -# Example 9 +# Example 9 --- 创建投票字典 +# 目的:演示如何创建一个存储投票结果的字典。 +# 解释: +# votes 字典存储动物名称及其对应的票数。 +# 结果:投票字典创建完成。 +print(f"\n{'Example 9':*^50}") votes = { 'otter': 1281, 'polar bear': 587, 'fox': 863, } +# Example 10 --- 根据投票结果生成排名 +# 目的:展示如何根据投票结果生成动物的排名。 +# 解释: +# populate_ranks 函数根据投票数对动物进行排序,并将其排名存入 ranks 字典。 +# 结果:生成并存储动物排名。 +print(f"\n{'Example 10':*^50}") + -# Example 10 def populate_ranks(votes, ranks): names = list(votes.keys()) names.sort(key=votes.get, reverse=True) @@ -96,22 +141,39 @@ def populate_ranks(votes, ranks): ranks[name] = i -# Example 11 +# Example 11 --- 获取排名最高的动物 +# 目的:演示如何从字典中获取排名最高的动物。 +# 解释: +# get_winner 函数使用 next 和 iter 从字典中获取第一个键,即排名最高的动物。 +# 结果:返回排名最高的动物名称。 +print(f"\n{'Example 11':*^50}") + + def get_winner(ranks): return next(iter(ranks)) -# Example 12 +# Example 12 --- 生成排名并获取胜者 +# 目的:展示如何生成动物排名并获取胜者。 +# 解释: +# ranks 是一个空字典,populate_ranks 函数填充它,最后通过 get_winner 获取排名最高的动物。 +# 结果:输出生成的排名和排名第一的动物。 +print(f"\n{'Example 12':*^50}") ranks = {} populate_ranks(votes, ranks) print(ranks) winner = get_winner(ranks) print(winner) - -# Example 13 +# Example 13 --- 创建支持排序的字典类 +# 目的:展示如何通过继承 MutableMapping 实现一个支持排序的字典类。 +# 解释: +# SortedDict 继承自 MutableMapping,实现了常见的字典操作,并按键进行排序。 +# 结果:可以像普通字典一样使用 SortedDict,并支持按键排序。 +print(f"\n{'Example 13':*^50}") from collections.abc import MutableMapping + class SortedDict(MutableMapping): def __init__(self): self.data = {} @@ -134,6 +196,7 @@ def __iter__(self): def __len__(self): return len(self.data) + my_dict = SortedDict() my_dict['otter'] = 1 my_dict['cheeta'] = 2 @@ -151,34 +214,50 @@ def __len__(self): assert not isinstance(my_dict, dict) - -# Example 14 +# Example 14 --- 使用 SortedDict 存储并获取排名 +# 目的:展示如何使用自定义的 SortedDict 类存储并获取排名。 +# 解释: +# populate_ranks 使用 SortedDict 存储排名数据,get_winner 获取排名第一的动物。 +# 结果:输出排名和胜者。 +print(f"\n{'Example 14':*^50}") sorted_ranks = SortedDict() populate_ranks(votes, sorted_ranks) print(sorted_ranks.data) winner = get_winner(sorted_ranks) print(winner) +# Example 15 --- 自定义获取胜者的逻辑 +# 目的:展示如何通过遍历字典自定义获取胜者的逻辑。 +# 解释: +# get_winner 函数遍历 ranks 字典,查找排名为 1 的动物并返回。 +# 结果:输出排名第一的动物。 +print(f"\n{'Example 15':*^50}") + -# Example 15 def get_winner(ranks): for name, rank in ranks.items(): if rank == 1: return name + winner = get_winner(sorted_ranks) print(winner) - -# Example 16 +# Example 16 --- 检查字典类型并处理异常 +# 目的:展示如何检查字典类型并处理类型不匹配的情况。 +# 解释: +# get_winner 函数检查传入参数是否为 dict 类型,如果不是,则引发 TypeError。 +# 结果:捕获并记录异常信息。 +print(f"\n{'Example 16':*^50}") try: def get_winner(ranks): if not isinstance(ranks, dict): raise TypeError('must provide a dict instance') return next(iter(ranks)) - + + assert get_winner(ranks) == 'otter' - + get_winner(sorted_ranks) except: logging.exception('Expected') diff --git a/example_code/item_16.py b/example_code/item_16.py index cf616ad..29a8aa8 100755 --- a/example_code/item_16.py +++ b/example_code/item_16.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 16: Prefer get Over in and KeyError to Handle Missing Dictionary Keys +# 军规 16: 使用 get 代替 in 和 KeyError 来处理字典缺失的键 + +""" +Prefer get Over in and KeyError to Handle Missing Dictionary Keys +使用 get 代替 in 和 KeyError 来处理字典缺失的键 +""" + import random random.seed(1234) @@ -46,14 +55,24 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 初始化计数器字典 +# 目的:演示如何创建并初始化一个字典。 +# 解释: +# counters 是一个字典,用来记录不同种类面包的数量。 +# 结果:字典初始化完成,包含两种面包。 +print(f"\n{'Example 1':*^50}") counters = { 'pumpernickel': 2, 'sourdough': 1, } -# Example 2 +# Example 2 --- 使用 in 操作符判断键是否存在 +# 目的:展示如何使用 in 操作符判断字典中是否存在某个键。 +# 解释: +# 如果键 'wheat' 存在,则获取其值;否则,将其初始化为 0 并更新计数。 +# 结果:更新后输出字典。 +print(f"\n{'Example 2':*^50}") key = 'wheat' if key in counters: @@ -66,7 +85,12 @@ def close_open_files(): print(counters) -# Example 3 +# Example 3 --- 使用 try-except 处理 KeyError 异常 +# 目的:展示如何使用 try-except 捕获字典中不存在的键引发的 KeyError。 +# 解释: +# 如果字典中不存在 'brioche',则捕获 KeyError 并将其初始化为 0,再更新计数。 +# 结果:更新后输出字典。 +print(f"\n{'Example 3':*^50}") key = 'brioche' try: @@ -79,7 +103,12 @@ def close_open_files(): print(counters) -# Example 4 +# Example 4 --- 使用 get 方法处理缺失的键 +# 目的:展示如何使用 get 方法避免 KeyError。 +# 解释: +# get 方法可以在键不存在时返回一个默认值,在这里返回 0。 +# 结果:更新后输出字典。 +print(f"\n{'Example 4':*^50}") key = 'multigrain' count = counters.get(key, 0) @@ -88,7 +117,12 @@ def close_open_files(): print(counters) -# Example 5 +# Example 5 --- 多种处理缺失键的方法 +# 目的:展示多种处理字典中缺失键的方式。 +# 解释: +# 通过 if 语句、try-except 和 get 方法处理不存在的键,并更新计数。 +# 结果:更新后输出字典。 +print(f"\n{'Example 5':*^50}") key = 'baguette' if key not in counters: @@ -112,7 +146,12 @@ def close_open_files(): print(counters) -# Example 6 +# Example 6 --- 处理列表值的缺失键 +# 目的:演示如何处理字典中列表类型的值,并处理缺失键。 +# 解释: +# votes 字典存储每个面包种类的投票人,若键不存在则创建一个空列表。 +# 结果:更新后输出字典。 +print(f"\n{'Example 6':*^50}") votes = { 'baguette': ['Bob', 'Alice'], 'ciabatta': ['Coco', 'Deb'], @@ -130,7 +169,12 @@ def close_open_files(): print(votes) -# Example 7 +# Example 7 --- 使用 try-except 处理列表类型的缺失键 +# 目的:展示如何使用 try-except 捕获字典中缺失键,并创建空列表。 +# 解释: +# 如果字典中不存在键 'rye',则捕获 KeyError 并将其初始化为空列表。 +# 结果:更新后输出字典。 +print(f"\n{'Example 7':*^50}") key = 'rye' who = 'Felix' @@ -144,7 +188,12 @@ def close_open_files(): print(votes) -# Example 8 +# Example 8 --- 使用 get 方法处理缺失键 +# 目的:展示如何使用 get 方法处理缺失键并创建空列表。 +# 解释: +# 通过 votes.get() 方法获取键对应的值,若键不存在则返回 None,进而创建空列表。 +# 结果:更新后输出字典。 +print(f"\n{'Example 8':*^50}") key = 'wheat' who = 'Gertrude' @@ -157,7 +206,12 @@ def close_open_files(): print(votes) -# Example 9 +# Example 9 --- 使用赋值表达式简化缺失键的处理 +# 目的:展示如何使用赋值表达式(海象运算符)简化字典键的处理。 +# 解释: +# 使用赋值表达式简化键不存在时的处理逻辑,一行代码完成键值的初始化和更新。 +# 结果:更新后输出字典。 +print(f"\n{'Example 9':*^50}") key = 'brioche' who = 'Hugh' @@ -169,7 +223,12 @@ def close_open_files(): print(votes) -# Example 10 +# Example 10 --- 使用 setdefault 方法简化字典的操作 +# 目的:展示如何使用 setdefault 方法处理字典中缺失的键。 +# 解释: +# setdefault 方法可以同时检查键是否存在并初始化该键为一个空列表,简化代码逻辑。 +# 结果:更新后输出字典。 +print(f"\n{'Example 10':*^50}") key = 'cornbread' who = 'Kirk' @@ -179,7 +238,12 @@ def close_open_files(): print(votes) -# Example 11 +# Example 11 --- 使用 setdefault 可能导致意外行为 +# 目的:展示使用 setdefault 可能导致的副作用。 +# 解释: +# 当 setdefault 使用一个可变对象(如列表)作为默认值时,即使未修改字典,该对象仍然保留在字典中。 +# 结果:输出修改前后的字典。 +print(f"\n{'Example 11':*^50}") data = {} key = 'foo' value = [] @@ -189,7 +253,12 @@ def close_open_files(): print('After: ', data) -# Example 12 +# Example 12 --- 使用 setdefault 处理计数器字典 +# 目的:展示如何使用 setdefault 方法处理计数器字典中的缺失键。 +# 解释: +# 使用 setdefault 方法为键 'dutch crunch' 初始化为 0,并更新计数。 +# 结果:更新后输出字典。 +print(f"\n{'Example 12':*^50}") key = 'dutch crunch' count = counters.setdefault(key, 0) diff --git a/example_code/item_17.py b/example_code/item_17.py index b0e86ff..4c0c0e5 100755 --- a/example_code/item_17.py +++ b/example_code/item_17.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 17: Prefer defaultdict Over setdefault to Handle Missing Items in Internal State +# 军规 17: 使用 defaultdict 代替 setdefault 处理内部状态中缺失的项 + +""" +Prefer defaultdict Over setdefault to Handle Missing Items in Internal State +使用 defaultdict 代替 setdefault 处理内部状态中缺失的项 +""" + import random random.seed(1234) @@ -46,14 +55,25 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 初始化一个包含访问记录的字典 +# 目的:展示如何初始化一个包含访问城市的字典。 +# 解释: +# visits 是一个字典,其中每个国家对应一个集合,存储访问过的城市。 +# 结果:字典初始化完成,包含墨西哥和日本的访问城市记录。 +print(f"\n{'Example 1':*^50}") visits = { 'Mexico': {'Tulum', 'Puerto Vallarta'}, 'Japan': {'Hakone'}, } -# Example 2 +# Example 2 --- 使用 setdefault 和 get 添加访问记录 +# 目的:展示如何使用 setdefault 方法和 get 方法处理缺失的键,并添加访问记录。 +# 解释: +# visits.setdefault('France', set()).add('Arles') 将 'France' 键初始化为一个空集合并添加城市 'Arles'。 +# 如果 'Japan' 键存在,则获取其值并添加城市 'Kyoto';否则,初始化该键为集合并添加城市。 +# 结果:输出更新后的访问记录。 +print(f"\n{'Example 2':*^50}") visits.setdefault('France', set()).add('Arles') # Short if (japan := visits.get('Japan')) is None: # Long @@ -66,7 +86,12 @@ def close_open_files(): print = original_print -# Example 3 +# Example 3 --- 自定义类实现访问记录 +# 目的:展示如何通过自定义类管理访问记录。 +# 解释: +# Visits 类使用字典管理国家和访问的城市,add 方法通过 setdefault 添加城市到相应的国家。 +# 结果:输出更新后的访问记录。 +print(f"\n{'Example 3':*^50}") class Visits: def __init__(self): self.data = {} @@ -76,14 +101,24 @@ def add(self, country, city): city_set.add(city) -# Example 4 +# Example 4 --- 使用自定义类添加访问记录 +# 目的:演示如何使用自定义的 Visits 类添加访问记录。 +# 解释: +# 调用 visits.add() 方法添加访问记录,并通过 print 输出字典的内容。 +# 结果:更新后的访问记录包含俄罗斯和坦桑尼亚的城市。 +print(f"\n{'Example 4':*^50}") visits = Visits() visits.add('Russia', 'Yekaterinburg') visits.add('Tanzania', 'Zanzibar') print(visits.data) -# Example 5 +# Example 5 --- 使用 defaultdict 简化字典操作 +# 目的:展示如何通过 defaultdict 简化缺失键的处理。 +# 解释: +# defaultdict(set) 自动为不存在的键创建一个空集合,简化了添加城市的操作。 +# 结果:输出更新后的访问记录。 +print(f"\n{'Example 5':*^50}") from collections import defaultdict class Visits: @@ -96,4 +131,4 @@ def add(self, country, city): visits = Visits() visits.add('England', 'Bath') visits.add('England', 'London') -print(visits.data) +print(visits.data) \ No newline at end of file diff --git a/example_code/item_18.py b/example_code/item_18.py index 2d9814f..e5f8c41 100755 --- a/example_code/item_18.py +++ b/example_code/item_18.py @@ -15,7 +15,17 @@ # limitations under the License. # Reproduce book environment + +# 军规 18: Use defaultdict for Missing Items Only When Key Access Is Common +# 军规 18: 仅在键访问频繁时使用 defaultdict 来处理缺失项 + +""" +Use defaultdict for Missing Items Only When Key Access Is Common +仅在键访问频繁时使用 defaultdict 来处理缺失项 +""" + import random + random.seed(1234) import logging @@ -37,16 +47,22 @@ atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) + def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() -atexit.register(close_open_files) +atexit.register(close_open_files) -# Example 1 +# Example 1 --- 使用赋值表达式打开图片文件 +# 目的:展示如何使用赋值表达式(海象运算符)打开图片文件并避免重复打开文件。 +# 解释: +# 通过 pictures.get() 检查文件是否已经打开,若未打开则打开并存储在字典中。 +# 结果:输出图片文件句柄和文件内容。 +print(f"\n{'Example 1':*^50}") pictures = {} path = 'profile_1234.png' @@ -68,9 +84,13 @@ def close_open_files(): print(pictures) print(image_data) - -# Example 2 -# Examples using in and KeyError +# Example 2 --- 使用 in 和 KeyError 处理缺失文件 +# 目的:展示如何使用 in 操作符和 try-except 捕获 KeyError 处理缺失的文件。 +# 解释: +# 首先使用 in 操作符判断文件是否已打开,如果未打开则尝试打开并存入字典;另一种方式是捕获 KeyError 进行处理。 +# 结果:输出图片文件句柄和文件内容。 +print(f"\n{'Example 2':*^50}") +# 使用 in 操作符 pictures = {} path = 'profile_9991.png' @@ -94,6 +114,7 @@ def close_open_files(): print(pictures) print(image_data) +# 使用 KeyError pictures = {} path = 'profile_9922.png' @@ -117,8 +138,12 @@ def close_open_files(): print(pictures) print(image_data) - -# Example 3 +# Example 3 --- 使用 setdefault 打开文件 +# 目的:展示如何使用 setdefault 方法处理缺失的文件句柄。 +# 解释: +# pictures.setdefault(path, open(path, 'a+b')) 尝试打开文件,如果文件句柄不存在则打开文件并存入字典。 +# 结果:输出图片文件句柄和文件内容。 +print(f"\n{'Example 3':*^50}") pictures = {} path = 'profile_9239.png' @@ -137,23 +162,29 @@ def close_open_files(): print(pictures) print(image_data) - -# Example 4 +# Example 4 --- 使用 defaultdict 简化文件打开操作 +# 目的:展示如何通过 defaultdict 来自动处理文件的缺失。 +# 解释: +# defaultdict(open_picture) 在缺少键时自动调用 open_picture 函数打开文件并存储文件句柄。 +# 结果:输出文件句柄和文件内容,若文件不存在则捕获并记录异常。 +print(f"\n{'Example 4':*^50}") try: path = 'profile_4555.csv' - + with open(path, 'wb') as f: f.write(b'image data here 9239') - + from collections import defaultdict - + + def open_picture(profile_path): try: return open(profile_path, 'a+b') except OSError: print(f'Failed to open path {profile_path}') raise - + + pictures = defaultdict(open_picture) handle = pictures[path] handle.seek(0) @@ -163,13 +194,18 @@ def open_picture(profile_path): else: assert False - -# Example 5 +# Example 5 --- 自定义字典类处理文件缺失 +# 目的:展示如何通过自定义字典类处理缺失的文件句柄。 +# 解释: +# Pictures 类继承自 dict,通过实现 __missing__ 方法在键缺失时自动打开文件并存储文件句柄。 +# 结果:使用 Pictures 类处理文件缺失时自动打开文件并输出文件内容。 +print(f"\n{'Example 5':*^50}") path = 'account_9090.csv' with open(path, 'wb') as f: f.write(b'image data here 9090') + def open_picture(profile_path): try: return open(profile_path, 'a+b') @@ -177,12 +213,14 @@ def open_picture(profile_path): print(f'Failed to open path {profile_path}') raise + class Pictures(dict): def __missing__(self, key): value = open_picture(key) self[key] = value return value + pictures = Pictures() handle = pictures[path] handle.seek(0) From 128cfead67748462be87012a87d47b34495fddd0 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 23 Sep 2024 16:23:18 +0800 Subject: [PATCH 15/59] modify item_19-21.py --- example_code/item_19.py | 51 +++++++++++++++++++++++++---- example_code/item_20.py | 72 +++++++++++++++++++++++++++++++++++------ example_code/item_21.py | 72 +++++++++++++++++++++++++++++++++++------ 3 files changed, 171 insertions(+), 24 deletions(-) diff --git a/example_code/item_19.py b/example_code/item_19.py index 35f20c7..2a7231a 100755 --- a/example_code/item_19.py +++ b/example_code/item_19.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 19: Unpack Elements from Iterables of Arbitrary Length +# 军规 19: 从任意长度的可迭代对象中解包元素 + +""" +Unpack Elements from Iterables of Arbitrary Length +从任意长度的可迭代对象中解包元素 +""" + import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 返回多个值 +# 目的:展示如何通过函数返回多个值并解包这些值。 +# 解释: +# get_stats 函数返回最小值和最大值,调用函数时可以通过解包接收多个返回值。 +# 结果:输出最小值和最大值。 +print(f"\n{'Example 1':*^50}") def get_stats(numbers): minimum = min(numbers) maximum = max(numbers) @@ -59,7 +73,12 @@ def get_stats(numbers): print(f'Min: {minimum}, Max: {maximum}') -# Example 2 +# Example 2 --- 解包多个返回值 +# 目的:展示如何通过解包接收函数的多个返回值。 +# 解释: +# 通过返回多个值并使用解包方式获取这些值,避免了单独访问每个值。 +# 结果:验证解包的值是否正确。 +print(f"\n{'Example 2':*^50}") first, second = 1, 2 assert first == 1 assert second == 2 @@ -72,7 +91,12 @@ def my_function(): assert second == 2 -# Example 3 +# Example 3 --- 解包带有剩余元素的可迭代对象 +# 目的:展示如何使用解包从一个可迭代对象中提取第一个、最后一个和剩余的中间元素。 +# 解释: +# get_avg_ratio 函数返回缩放后的排序列表,通过解包获取列表中的第一个、最后一个和中间元素。 +# 结果:输出最长和最短的比例。 +print(f"\n{'Example 3':*^50}") def get_avg_ratio(numbers): average = sum(numbers) / len(numbers) scaled = [x / average for x in numbers] @@ -85,7 +109,12 @@ def get_avg_ratio(numbers): print(f'Shortest: {shortest:>4.0%}') -# Example 4 +# Example 4 --- 返回并解包统计数据 +# 目的:展示如何返回并解包多个统计值,如最小值、最大值、平均值等。 +# 解释: +# get_stats 返回多种统计数据(最小值、最大值、平均值、中位数、计数),调用时通过解包接收这些值。 +# 结果:输出所有统计数据,并验证其正确性。 +print(f"\n{'Example 4':*^50}") def get_stats(numbers): minimum = min(numbers) maximum = max(numbers) @@ -120,7 +149,12 @@ def get_stats(numbers): assert count == 3 -# Example 5 +# Example 5 --- 解包顺序错误 +# 目的:展示解包时顺序错误会导致的潜在问题。 +# 解释: +# 解包时需要注意返回值的顺序,如果顺序错误,会导致不正确的结果。 +# 结果:展示错误的解包顺序导致的意外情况。 +print(f"\n{'Example 5':*^50}") # Correct: minimum, maximum, average, median, count = get_stats(lengths) @@ -128,7 +162,12 @@ def get_stats(numbers): minimum, maximum, median, average, count = get_stats(lengths) -# Example 6 +# Example 6 --- 多行解包 +# 目的:展示如何将解包操作分多行进行。 +# 解释: +# 当返回的值较多时,可以通过多行进行解包,保持代码的可读性。 +# 结果:展示多行解包的不同写法。 +print(f"\n{'Example 6':*^50}") minimum, maximum, average, median, count = get_stats( lengths) diff --git a/example_code/item_20.py b/example_code/item_20.py index 4a9639b..f72127a 100755 --- a/example_code/item_20.py +++ b/example_code/item_20.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 20: Know How Closures Interact with Variable Scope +# 军规 20: 理解闭包如何与变量作用域交互 + +""" +Know How Closures Interact with Variable Scope +理解闭包如何与变量作用域交互 +""" + import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 初步处理异常的除法函数 +# 目的:展示如何使用 try-except 捕获异常,处理除零的情况。 +# 解释: +# careful_divide 函数尝试除法运算,若发生 ZeroDivisionError 则返回 None。 +# 结果:处理不同的除法情况,若除零则返回 None。 +print(f"\n{'Example 1':*^50}") def careful_divide(a, b): try: return a / b @@ -59,7 +73,12 @@ def careful_divide(a, b): assert careful_divide(1, 0) == None -# Example 2 +# Example 2 --- 检查结果是否为 None +# 目的:展示如何通过检查函数返回值为 None 来判断输入是否有效。 +# 解释: +# 通过检查 careful_divide 的返回值是否为 None 来判断输入的有效性,若为 None 则表示除零错误。 +# 结果:根据除法结果输出对应的消息。 +print(f"\n{'Example 2':*^50}") x, y = 1, 0 result = careful_divide(x, y) if result is None: @@ -68,7 +87,12 @@ def careful_divide(a, b): print('Result is %.1f' % result) -# Example 3 +# Example 3 --- 检查 False 值导致的误判 +# 目的:展示如何直接使用 if 判断返回值可能导致误判。 +# 解释: +# 因为 0 也被视作 False,因此直接使用 if 语句判断可能会误判合法的返回值。 +# 结果:错误地将 0 视为无效输入。 +print(f"\n{'Example 3':*^50}") x, y = 0, 5 result = careful_divide(x, y) if not result: @@ -77,7 +101,12 @@ def careful_divide(a, b): assert False -# Example 4 +# Example 4 --- 使用元组返回成功标记和结果 +# 目的:展示如何通过元组返回成功标记和计算结果来避免误判。 +# 解释: +# careful_divide 函数返回一个元组,第一个元素表示运算是否成功,第二个元素为结果值。 +# 结果:通过 success 标志来判断是否成功。 +print(f"\n{'Example 4':*^50}") def careful_divide(a, b): try: return True, a / b @@ -90,21 +119,36 @@ def careful_divide(a, b): assert careful_divide(1, 0) == (False, None) -# Example 5 +# Example 5 --- 使用成功标志判断结果 +# 目的:展示如何通过返回的成功标志来判断除法是否有效。 +# 解释: +# 通过 careful_divide 返回的成功标志来判断输入是否有效。 +# 结果:成功标志为 False 时,输出无效输入。 +print(f"\n{'Example 5':*^50}") x, y = 5, 0 success, result = careful_divide(x, y) if not success: print('Invalid inputs') -# Example 6 +# Example 6 --- 直接使用返回的结果判断 +# 目的:展示如何忽略成功标志,直接使用返回的结果来判断。 +# 解释: +# 直接检查 careful_divide 返回的结果是否为 None 来判断除法是否有效。 +# 结果:若结果为 None,输出无效输入。 +print(f"\n{'Example 6':*^50}") x, y = 5, 0 _, result = careful_divide(x, y) if not result: print('Invalid inputs') -# Example 7 +# Example 7 --- 引发自定义异常 +# 目的:展示如何通过引发自定义异常来处理无效输入。 +# 解释: +# 当发生 ZeroDivisionError 时,通过 raise 引发自定义的 ValueError,指示无效输入。 +# 结果:处理无效输入时抛出 ValueError。 +print(f"\n{'Example 7':*^50}") def careful_divide(a, b): try: return a / b @@ -112,7 +156,12 @@ def careful_divide(a, b): raise ValueError('Invalid inputs') -# Example 8 +# Example 8 --- 捕获自定义异常 +# 目的:展示如何捕获自定义异常并处理无效输入。 +# 解释: +# 调用 careful_divide 时捕获自定义的 ValueError 异常,并根据异常输出无效输入消息。 +# 结果:处理无效输入时捕获并输出错误信息。 +print(f"\n{'Example 8':*^50}") x, y = 5, 2 try: result = careful_divide(x, y) @@ -122,7 +171,12 @@ def careful_divide(a, b): print('Result is %.1f' % result) -# Example 9 +# Example 9 --- 使用类型提示和文档字符串 +# 目的:展示如何使用类型提示和文档字符串为函数添加额外的说明。 +# 解释: +# careful_divide 函数使用类型提示指示参数和返回值的类型,并在文档字符串中说明异常的引发情况。 +# 结果:除零时引发自定义的 ValueError,并通过 assert 进行异常测试。 +print(f"\n{'Example 9':*^50}") def careful_divide(a: float, b: float) -> float: """Divides a by b. diff --git a/example_code/item_21.py b/example_code/item_21.py index 10f97ae..1595953 100755 --- a/example_code/item_21.py +++ b/example_code/item_21.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 21: Know How Closures Interact with Variable Scope +# 军规 21: 理解闭包如何与变量作用域交互 + +""" +Know How Closures Interact with Variable Scope +理解闭包如何与变量作用域交互 +""" + import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 使用闭包进行排序 +# 目的:展示如何通过嵌套函数(闭包)对列表进行排序。 +# 解释: +# sort_priority 函数通过 helper 闭包,根据元素是否属于 group 进行排序,属于 group 的元素优先。 +# 结果:对 numbers 列表进行排序,优先排序 group 中的元素。 +print(f"\n{'Example 1':*^50}") def sort_priority(values, group): def helper(x): if x in group: @@ -55,14 +69,24 @@ def helper(x): values.sort(key=helper) -# Example 2 +# Example 2 --- 调用闭包排序函数 +# 目的:展示如何调用 sort_priority 函数对列表进行排序。 +# 解释: +# numbers 列表将根据 group 中的元素优先排序,属于 group 的元素优先排列在前。 +# 结果:排序后的 numbers 列表。 +print(f"\n{'Example 2':*^50}") numbers = [8, 3, 1, 2, 5, 4, 7, 6] group = {2, 3, 5, 7} sort_priority(numbers, group) print(numbers) -# Example 3 +# Example 3 --- 闭包对外部作用域的影响 +# 目的:展示闭包如何无法影响外部作用域中的变量。 +# 解释: +# helper 闭包尝试修改外部作用域中的 found 变量,但由于作用域问题,修改不会生效。 +# 结果:found 变量仍为 False,排序结果仍然正确。 +print(f"\n{'Example 3':*^50}") def sort_priority2(numbers, group): found = False def helper(x): @@ -74,14 +98,24 @@ def helper(x): return found -# Example 4 +# Example 4 --- 调用闭包并返回标志变量 +# 目的:展示如何通过 sort_priority2 函数进行排序,并返回 found 标志变量。 +# 解释: +# 虽然在 helper 中修改了 found,但由于作用域问题,返回的 found 仍为 False。 +# 结果:输出排序后的列表及 found 的值(False)。 +print(f"\n{'Example 4':*^50}") numbers = [8, 3, 1, 2, 5, 4, 7, 6] found = sort_priority2(numbers, group) print('Found:', found) print(numbers) -# Example 5 +# Example 5 --- 捕获意料之外的异常 +# 目的:展示如何捕获代码中预期发生的异常。 +# 解释: +# 代码尝试使用未定义的变量(does_not_exist),会触发 NameError 异常,并被捕获。 +# 结果:记录异常日志。 +print(f"\n{'Example 5':*^50}") try: foo = does_not_exist * 5 except: @@ -90,7 +124,12 @@ def helper(x): assert False -# Example 6 +# Example 6 --- 闭包变量作用域问题 +# 目的:展示闭包修改外部变量时的作用域问题。 +# 解释: +# helper 尝试修改外部作用域中的 found 变量,但由于作用域限制,修改不会生效。 +# 结果:返回的 found 变量仍为 False。 +print(f"\n{'Example 6':*^50}") def sort_priority2(numbers, group): found = False # Scope: 'sort_priority2' def helper(x): @@ -102,7 +141,12 @@ def helper(x): return found -# Example 7 +# Example 7 --- 使用 nonlocal 解决作用域问题 +# 目的:展示如何通过 nonlocal 关键字解决闭包修改外部变量的作用域问题。 +# 解释: +# nonlocal 关键字允许在闭包中修改外部作用域的变量。 +# 结果:排序时正确修改 found 变量并返回 True。 +print(f"\n{'Example 7':*^50}") def sort_priority3(numbers, group): found = False def helper(x): @@ -115,14 +159,24 @@ def helper(x): return found -# Example 8 +# Example 8 --- 调用 sort_priority3 并正确返回 found 标志 +# 目的:展示通过 sort_priority3 函数正确排序并返回 found 标志。 +# 解释: +# 通过 nonlocal 关键字,helper 函数正确修改了外部作用域中的 found 变量。 +# 结果:found 变量为 True,排序后的列表正确。 +print(f"\n{'Example 8':*^50}") numbers = [8, 3, 1, 2, 5, 4, 7, 6] found = sort_priority3(numbers, group) assert found assert numbers == [2, 3, 5, 7, 1, 4, 6, 8] -# Example 9 +# Example 9 --- 使用类解决闭包问题 +# 目的:展示如何通过类和 __call__ 方法代替闭包进行变量修改。 +# 解释: +# Sorter 类通过 __call__ 方法实现与闭包相似的行为,并通过实例变量正确维护 found 状态。 +# 结果:found 变量为 True,排序后的列表正确。 +print(f"\n{'Example 9':*^50}") numbers = [8, 3, 1, 2, 5, 4, 7, 6] class Sorter: def __init__(self, group): From 4fc28cf1cfa93869b9be917733cbd79c6a1dc3ee Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 23 Sep 2024 17:43:03 +0800 Subject: [PATCH 16/59] modify item_19-21.py --- example_code/item_22.py | 44 ++++++++-- example_code/item_23.py | 121 +++++++++++++++++++++++---- example_code/item_24.py | 65 +++++++++++++-- example_code/item_24_example_09.py | 18 +++- example_code/item_25.py | 128 +++++++++++++++++++++++++---- example_code/item_26.py | 100 ++++++++++++++++++---- 6 files changed, 410 insertions(+), 66 deletions(-) diff --git a/example_code/item_22.py b/example_code/item_22.py index 4b8abbd..75b46c0 100755 --- a/example_code/item_22.py +++ b/example_code/item_22.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 22: Avoid Using More Than Two Positional Arguments +# 军规 22: 避免使用两个以上的位置参数 + +""" +Avoid Using More Than Two Positional Arguments +避免使用两个以上的位置参数 +""" + import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 初始实现的日志函数 +# 目的:展示如何通过位置参数传递消息和列表值。 +# 解释: +# log 函数接受两个参数,message 和 values,如果 values 列表为空,仅打印消息,否则打印消息和 values。 +# 结果:输出日志消息和数值列表。 +print(f"\n{'Example 1':*^50}") def log(message, values): if not values: print(message) @@ -58,7 +72,12 @@ def log(message, values): log('Hi there', []) -# Example 2 +# Example 2 --- 使用 *args 简化日志函数 +# 目的:展示如何通过 *args 实现可变数量参数的传递。 +# 解释: +# 通过使用 *args,可以传递任意数量的参数,函数内部将其视作元组处理,这样可以避免必须提供一个列表作为参数。 +# 结果:调用时无需显式传递列表,可以直接传递多个数值。 +print(f"\n{'Example 2':*^50}") def log(message, *values): # The only difference if not values: print(message) @@ -70,12 +89,22 @@ def log(message, *values): # The only difference log('Hi there') # Much better -# Example 3 +# Example 3 --- 使用 *args 进行解包 +# 目的:展示如何使用 *args 解包列表并传递给函数。 +# 解释: +# 使用 *favorites 解包列表,将其元素作为单独的参数传递给 log 函数,避免手动传递列表。 +# 结果:通过解包列表,log 函数接收到多个数值。 +print(f"\n{'Example 3':*^50}") favorites = [7, 33, 99] log('Favorite colors', *favorites) -# Example 4 +# Example 4 --- 使用 *args 解包生成器 +# 目的:展示如何通过 *args 解包生成器并将其元素传递给函数。 +# 解释: +# my_generator 是一个生成器,通过 *it 解包生成器,将其所有元素传递给 my_func,函数接收到的参数为生成器的所有元素。 +# 结果:输出生成器中所有生成的值。 +print(f"\n{'Example 4':*^50}") def my_generator(): for i in range(10): yield i @@ -87,7 +116,12 @@ def my_func(*args): my_func(*it) -# Example 5 +# Example 5 --- 结合顺序参数和 *args +# 目的:展示如何在函数中结合位置参数和 *args。 +# 解释: +# log 函数的第一个参数为顺序参数 sequence,第二个为消息,后面的所有参数通过 *args 接收并处理。 +# 结果:能够根据传入的参数情况打印消息和数值,若只提供消息则只打印消息。 +print(f"\n{'Example 5':*^50}") def log(sequence, message, *values): if not values: print(f'{sequence} - {message}') diff --git a/example_code/item_23.py b/example_code/item_23.py index 99c6fe8..6270709 100755 --- a/example_code/item_23.py +++ b/example_code/item_23.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 23: Provide Optional Behavior with Keyword Arguments +# 军规 23: 使用关键字参数提供可选行为 + +""" +Provide Optional Behavior with Keyword Arguments +使用关键字参数提供可选行为 +""" + import random random.seed(1234) @@ -46,21 +55,36 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 基本函数使用位置参数 +# 目的:展示如何通过位置参数调用函数。 +# 解释: +# remainder 函数通过两个位置参数 number 和 divisor 计算余数。 +# 结果:输出 20 除以 7 的余数。 +print(f"\n{'Example 1':*^50}") def remainder(number, divisor): return number % divisor assert remainder(20, 7) == 6 -# Example 2 +# Example 2 --- 使用关键字参数调用函数 +# 目的:展示如何使用关键字参数调用函数。 +# 解释: +# remainder 函数可以通过关键字参数调用,使得参数顺序无关紧要。 +# 结果:通过不同方式传递参数,计算结果相同。 +print(f"\n{'Example 2':*^50}") remainder(20, 7) remainder(20, divisor=7) remainder(number=20, divisor=7) remainder(divisor=7, number=20) -# Example 3 +# Example 3 --- 关键字参数与位置参数的冲突 +# 目的:展示如何避免在使用关键字参数时出现语法错误。 +# 解释: +# 在位置参数之后不能再使用位置参数(如 remainder(number=20, 7)),这会导致语法错误。 +# 结果:捕获并记录该错误。 +print(f"\n{'Example 3':*^50}") try: # This will not compile source = """remainder(number=20, 7)""" @@ -71,7 +95,12 @@ def remainder(number, divisor): assert False -# Example 4 +# Example 4 --- 位置参数和关键字参数的重复问题 +# 目的:展示如何避免重复传递参数。 +# 解释: +# 位置参数和关键字参数不能重复使用同一个参数名,如 remainder(20, number=7) 会导致冲突。 +# 结果:捕获并记录该错误。 +print(f"\n{'Example 4':*^50}") try: remainder(20, number=7) except: @@ -80,7 +109,12 @@ def remainder(number, divisor): assert False -# Example 5 +# Example 5 --- 使用 **kwargs 解包字典参数 +# 目的:展示如何使用 **kwargs 将字典参数解包并传递给函数。 +# 解释: +# **my_kwargs 解包字典并将其作为关键字参数传递给 remainder 函数。 +# 结果:通过字典解包的方式调用函数并获得正确的结果。 +print(f"\n{'Example 5':*^50}") my_kwargs = { 'number': 20, 'divisor': 7, @@ -88,14 +122,24 @@ def remainder(number, divisor): assert remainder(**my_kwargs) == 6 -# Example 6 +# Example 6 --- 部分解包字典并传递额外参数 +# 目的:展示如何结合字典解包和额外的关键字参数调用函数。 +# 解释: +# 通过 **kwargs 解包部分参数,剩余参数直接传递给函数,实现灵活调用。 +# 结果:正确解包和传递参数。 +print(f"\n{'Example 6':*^50}") my_kwargs = { 'divisor': 7, } assert remainder(number=20, **my_kwargs) == 6 -# Example 7 +# Example 7 --- 多个字典的解包与合并 +# 目的:展示如何将多个字典解包并传递给函数。 +# 解释: +# 通过 **my_kwargs 和 **other_kwargs 解包多个字典并合并成关键字参数传递给 remainder 函数。 +# 结果:正确解包多个字典并传递参数。 +print(f"\n{'Example 7':*^50}") my_kwargs = { 'number': 20, } @@ -105,7 +149,12 @@ def remainder(number, divisor): assert remainder(**my_kwargs, **other_kwargs) == 6 -# Example 8 +# Example 8 --- 使用 **kwargs 传递任意数量的关键字参数 +# 目的:展示如何使用 **kwargs 接受并处理任意数量的关键字参数。 +# 解释: +# print_parameters 函数使用 **kwargs 接收并打印任意数量的关键字参数。 +# 结果:输出传入的关键字参数及其对应值。 +print(f"\n{'Example 8':*^50}") def print_parameters(**kwargs): for key, value in kwargs.items(): print(f'{key} = {value}') @@ -113,7 +162,12 @@ def print_parameters(**kwargs): print_parameters(alpha=1.5, beta=9, gamma=4) -# Example 9 +# Example 9 --- 函数使用位置参数 +# 目的:展示如何通过位置参数计算流速。 +# 解释: +# flow_rate 函数通过 weight_diff 和 time_diff 计算流速,单位为千克每秒。 +# 结果:输出计算的流速。 +print(f"\n{'Example 9':*^50}") def flow_rate(weight_diff, time_diff): return weight_diff / time_diff @@ -123,39 +177,74 @@ def flow_rate(weight_diff, time_diff): print(f'{flow:.3} kg per second') -# Example 10 +# Example 10 --- 函数使用额外的参数 +# 目的:展示如何通过额外的参数 period 计算不同时间段的流速。 +# 解释: +# flow_rate 函数新增 period 参数,用于指定计算流速的时间段。 +# 结果:根据不同的 period 计算流速。 +print(f"\n{'Example 10':*^50}") def flow_rate(weight_diff, time_diff, period): return (weight_diff / time_diff) * period -# Example 11 +# Example 11 --- 调用 flow_rate 函数计算每秒流速 +# 目的:展示如何使用 flow_rate 函数计算每秒的流速。 +# 解释: +# 通过传入 period=1 计算每秒的流速。 +# 结果:计算并输出每秒的流速。 +print(f"\n{'Example 11':*^50}") flow_per_second = flow_rate(weight_diff, time_diff, 1) -# Example 12 +# Example 12 --- 使用默认参数提供可选行为 +# 目的:展示如何通过默认参数提供可选的计算行为。 +# 解释: +# flow_rate 函数新增 period 的默认值为 1,若调用时不提供 period,则默认计算每秒的流速。 +# 结果:通过默认参数简化函数调用。 +print(f"\n{'Example 12':*^50}") def flow_rate(weight_diff, time_diff, period=1): return (weight_diff / time_diff) * period -# Example 13 +# Example 13 --- 计算每秒和每小时的流速 +# 目的:展示如何通过 period 参数计算不同时间段的流速。 +# 解释: +# 通过传入不同的 period 参数,分别计算每秒和每小时的流速。 +# 结果:输出每秒和每小时的流速。 +print(f"\n{'Example 13':*^50}") flow_per_second = flow_rate(weight_diff, time_diff) flow_per_hour = flow_rate(weight_diff, time_diff, period=3600) print(flow_per_second) print(flow_per_hour) -# Example 14 +# Example 14 --- 使用多个默认参数提供灵活行为 +# 目的:展示如何通过多个默认参数提供灵活的计算行为。 +# 解释: +# flow_rate 函数新增 units_per_kg 参数,用于转换单位(如从千克转换为磅)。 +# 结果:通过 units_per_kg 和 period 提供更灵活的流速计算。 +print(f"\n{'Example 14':*^50}") def flow_rate(weight_diff, time_diff, period=1, units_per_kg=1): return ((weight_diff * units_per_kg) / time_diff) * period -# Example 15 +# Example 15 --- 使用自定义单位计算每小时流速 +# 目的:展示如何通过传入不同的 units_per_kg 参数计算自定义单位的流速。 +# 解释: +# 通过传入 units_per_kg=2.2 将流速从千克转换为磅,并计算每小时的流速。 +# 结果:输出每小时的流速(单位为磅)。 +print(f"\n{'Example 15':*^50}") pounds_per_hour = flow_rate(weight_diff, time_diff, period=3600, units_per_kg=2.2) print(pounds_per_hour) -# Example 16 +# Example 16 --- 位置参数调用含有多个默认参数的函数 +# 目的:展示如何通过位置参数调用含有多个默认参数的函数。 +# 解释: +# 通过位置参数传入 period 和 units_per_kg 参数,计算流速。 +# 结果:输出每小时的流速(单位为磅)。 +print(f"\n{'Example 16':*^50}") pounds_per_hour = flow_rate(weight_diff, time_diff, 3600, 2.2) print(pounds_per_hour) diff --git a/example_code/item_24.py b/example_code/item_24.py index 124da9b..4fa6c41 100755 --- a/example_code/item_24.py +++ b/example_code/item_24.py @@ -15,6 +15,15 @@ # limitations under the License. # Reproduce book environment + +# 军规 24: Use None and Docstrings to Specify Dynamic Default Arguments +# 军规 24: 使用 None 和文档字符串来指定动态默认参数 + +""" +Use None and Docstrings to Specify Dynamic Default Arguments +使用 None 和文档字符串来指定动态默认参数 +""" + import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 错误使用可变默认参数 +# 目的:展示如何错误地使用动态数据(如 datetime.now())作为默认参数。 +# 解释: +# log 函数使用 datetime.now() 作为默认参数,导致当函数在不同时间被调用时,时间戳相同,因为默认参数只在函数定义时求值一次。 +# 结果:两次调用的时间戳相同,虽然有延迟。 +print(f"\n{'Example 1':*^50}") from time import sleep from datetime import datetime @@ -58,7 +72,12 @@ def log(message, when=datetime.now()): log('Hello again!') -# Example 2 +# Example 2 --- 使用 None 指定动态默认参数 +# 目的:展示如何通过将 None 作为默认值来实现动态参数。 +# 解释: +# 通过将 when 参数的默认值设为 None,并在函数内部判断是否为 None 来执行动态操作(即在每次调用时使用当前时间)。 +# 结果:每次调用 log 函数都会生成不同的时间戳。 +print(f"\n{'Example 2':*^50}") def log(message, when=None): """Log a message with a timestamp. @@ -72,13 +91,23 @@ def log(message, when=None): print(f'{when}: {message}') -# Example 3 +# Example 3 --- 动态参数正常工作 +# 目的:展示改进后的 log 函数如何正确生成动态时间戳。 +# 解释: +# 通过将默认参数设为 None,每次调用函数时都会使用当前时间。 +# 结果:两次调用显示不同的时间戳。 +print(f"\n{'Example 3':*^50}") log('Hi there!') sleep(0.1) log('Hello again!') -# Example 4 +# Example 4 --- 使用可变对象作为默认参数 +# 目的:展示在默认参数中使用可变对象(如字典)可能导致的错误。 +# 解释: +# decode 函数使用一个空字典作为默认值,这导致每次调用 decode 时都返回同一个字典实例。 +# 结果:不同的调用共用同一个字典对象,导致数据混乱。 +print(f"\n{'Example 4':*^50}") import json def decode(data, default={}): @@ -88,7 +117,12 @@ def decode(data, default={}): return default -# Example 5 +# Example 5 --- 调用 decode 函数并修改结果 +# 目的:展示使用可变默认参数时导致的错误行为。 +# 解释: +# 两次调用 decode 函数时,返回的字典是同一个对象,修改 foo 也会影响 bar。 +# 结果:输出的 foo 和 bar 共享了同一个字典,导致混乱。 +print(f"\n{'Example 5':*^50}") foo = decode('bad data') foo['stuff'] = 5 bar = decode('also bad') @@ -97,11 +131,21 @@ def decode(data, default={}): print('Bar:', bar) -# Example 6 +# Example 6 --- 断言 foo 和 bar 是同一个对象 +# 目的:展示 foo 和 bar 实际上是同一个对象。 +# 解释: +# 使用 assert 语句验证 foo 和 bar 是同一个字典实例。 +# 结果:断言通过,说明两次调用返回的是同一个字典。 +print(f"\n{'Example 6':*^50}") assert foo is bar -# Example 7 +# Example 7 --- 使用 None 解决可变默认参数问题 +# 目的:展示如何通过将默认参数设为 None 来避免共用可变对象。 +# 解释: +# decode 函数的默认参数设为 None,若出现解码错误则在函数内部创建新的字典对象,避免共用同一个对象。 +# 结果:每次调用 decode 函数都会返回不同的字典对象。 +print(f"\n{'Example 7':*^50}") def decode(data, default=None): """Load JSON data from a string. @@ -118,7 +162,12 @@ def decode(data, default=None): return default -# Example 8 +# Example 8 --- 调用 decode 函数并修改结果 +# 目的:展示使用 None 作为默认参数后,如何避免数据共享问题。 +# 解释: +# 通过为 default 参数提供 None,确保每次 decode 出错时都会创建一个新的字典。 +# 结果:foo 和 bar 不再共享同一个字典实例。 +print(f"\n{'Example 8':*^50}") foo = decode('bad data') foo['stuff'] = 5 bar = decode('also bad') diff --git a/example_code/item_24_example_09.py b/example_code/item_24_example_09.py index 7cbaa71..78f5977 100755 --- a/example_code/item_24_example_09.py +++ b/example_code/item_24_example_09.py @@ -15,9 +15,21 @@ # limitations under the License. - -# Example 9 -# Check types in this file with: python -m mypy +# 军规 24: Use None and Docstrings to Specify Dynamic Default Arguments +# 军规 24: 使用 None 和文档字符串来指定动态默认参数 + +""" +Use None and Docstrings to Specify Dynamic Default Arguments +使用 None 和文档字符串来指定动态默认参数 +""" + +# Example 9 --- 使用类型注解和 Optional 指定动态默认参数 +# 目的:展示如何通过类型注解和 Optional 来确保参数类型及其动态默认值。 +# 解释: +# 使用 `Optional[datetime]` 表示 when 参数可以是 `datetime` 对象或 `None`。如果 `when` 为 `None`,则在函数内部动态设置为当前时间。 +# 通过类型注解提高代码的可读性和安全性,`mypy` 可以检查类型是否正确。 +# 结果:每次调用函数时都会根据参数情况输出带时间戳的日志消息。 +print(f"\n{'Example 9':*^50}") from datetime import datetime from time import sleep diff --git a/example_code/item_25.py b/example_code/item_25.py index 2f1cce2..73a0c07 100755 --- a/example_code/item_25.py +++ b/example_code/item_25.py @@ -14,6 +14,15 @@ # See the License for the specific language governing permissions and # limitations under the License. + +# 军规 25: Enforce Clarity with Keyword-Only and Positional-Only Arguments +# 军规 25: 使用仅限关键字参数和仅限位置参数来保证代码清晰 + +""" +Enforce Clarity with Keyword-Only and Positional-Only Arguments +使用仅限关键字参数和仅限位置参数来保证代码清晰 +""" + # Reproduce book environment import random random.seed(1234) @@ -46,7 +55,12 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 位置参数调用 +# 目的:展示通过位置参数调用函数。 +# 解释: +# 函数 safe_division 接受 number 和 divisor 作为位置参数,ignore_overflow 和 ignore_zero_division 控制是否忽略异常。 +# 结果:当除数为 0 时会触发 ZeroDivisionError,通过参数控制是否忽略。 +print(f"\n{'Example 1':*^50}") def safe_division(number, divisor, ignore_overflow, ignore_zero_division): @@ -64,17 +78,32 @@ def safe_division(number, divisor, raise -# Example 2 +# Example 2 --- 使用位置参数调用函数 +# 目的:展示通过位置参数忽略溢出错误。 +# 解释: +# 通过传入 ignore_overflow=True,函数会忽略 OverflowError。 +# 结果:函数返回 0,表示忽略了溢出。 +print(f"\n{'Example 2':*^50}") result = safe_division(1.0, 10**500, True, False) print(result) -# Example 3 +# Example 3 --- 忽略 ZeroDivisionError +# 目的:展示通过位置参数忽略 ZeroDivisionError。 +# 解释: +# 当除数为 0 时,通过 ignore_zero_division=True 来忽略除零错误。 +# 结果:返回正无穷大表示忽略了除零错误。 +print(f"\n{'Example 3':*^50}") result = safe_division(1.0, 0, False, True) print(result) -# Example 4 +# Example 4 --- 使用默认参数提供可选行为 +# 目的:通过默认参数简化函数调用。 +# 解释: +# 通过为 ignore_overflow 和 ignore_zero_division 提供默认值,使得调用函数时无需显式传递这些参数。 +# 结果:即使不传递参数,函数依旧工作正常。 +print(f"\n{'Example 4':*^50}") def safe_division_b(number, divisor, ignore_overflow=False, # Changed ignore_zero_division=False): # Changed @@ -92,7 +121,12 @@ def safe_division_b(number, divisor, raise -# Example 5 +# Example 5 --- 调用带有关键字参数的函数 +# 目的:通过关键字参数调用函数并控制异常处理。 +# 解释: +# 通过 ignore_overflow=True 忽略溢出错误,通过 ignore_zero_division=True 忽略除零错误。 +# 结果:正确处理溢出和除零情况。 +print(f"\n{'Example 5':*^50}") result = safe_division_b(1.0, 10**500, ignore_overflow=True) print(result) @@ -100,11 +134,21 @@ def safe_division_b(number, divisor, print(result) -# Example 6 +# Example 6 --- 保持旧代码兼容性 +# 目的:确保改进后的函数仍与之前的代码兼容。 +# 解释: +# 通过传递旧版函数调用所需的参数,确保函数仍然能够处理位置参数的传递。 +# 结果:验证旧代码仍然有效。 +print(f"\n{'Example 6':*^50}") assert safe_division_b(1.0, 10**500, True, False) == 0 -# Example 7 +# Example 7 --- 使用仅限关键字参数保证代码清晰 +# 目的:通过将部分参数限制为仅限关键字,提升函数调用的清晰度。 +# 解释: +# 使用 `*` 指定 ignore_overflow 和 ignore_zero_division 必须通过关键字传递,避免位置参数带来的混淆。 +# 结果:必须通过关键字调用这些参数,代码更具可读性。 +print(f"\n{'Example 7':*^50}") def safe_division_c(number, divisor, *, # Changed ignore_overflow=False, ignore_zero_division=False): @@ -122,7 +166,12 @@ def safe_division_c(number, divisor, *, # Changed raise -# Example 8 +# Example 8 --- 位置参数调用不再允许 +# 目的:展示通过位置参数调用时会报错。 +# 解释: +# 因为 ignore_overflow 和 ignore_zero_division 被设定为仅限关键字,位置参数传递会报错。 +# 结果:捕获并记录位置参数传递导致的错误。 +print(f"\n{'Example 8':*^50}") try: safe_division_c(1.0, 10**500, True, False) except: @@ -131,7 +180,12 @@ def safe_division_c(number, divisor, *, # Changed assert False -# Example 9 +# Example 9 --- 正确使用关键字参数 +# 目的:展示如何正确地使用关键字参数调用函数。 +# 解释: +# 通过关键字参数调用 ignore_zero_division,避免除零错误并返回正无穷大。 +# 结果:函数正确返回正无穷大。 +print(f"\n{'Example 9':*^50}") result = safe_division_c(1.0, 0, ignore_zero_division=True) assert result == float('inf') @@ -143,13 +197,23 @@ def safe_division_c(number, divisor, *, # Changed assert False -# Example 10 +# Example 10 --- 使用关键字参数调用 +# 目的:展示通过关键字参数调用并确保结果正确。 +# 解释: +# 通过关键字参数调用函数,确保参数顺序无关紧要且结果正确。 +# 结果:三种不同调用方式都能返回正确结果。 +print(f"\n{'Example 10':*^50}") assert safe_division_c(number=2, divisor=5) == 0.4 assert safe_division_c(divisor=5, number=2) == 0.4 assert safe_division_c(2, divisor=5) == 0.4 -# Example 11 +# Example 11 --- 函数参数命名一致性 +# 目的:通过一致的参数命名保证代码的清晰和可读性。 +# 解释: +# 将 number 和 divisor 更名为 numerator 和 denominator 以符合数学术语。 +# 结果:增强代码的一致性和可理解性。 +print(f"\n{'Example 11':*^50}") def safe_division_c(numerator, denominator, *, # Changed ignore_overflow=False, ignore_zero_division=False): @@ -167,7 +231,12 @@ def safe_division_c(numerator, denominator, *, # Changed raise -# Example 12 +# Example 12 --- 旧参数名调用会报错 +# 目的:展示使用旧参数名调用会导致错误。 +# 解释: +# 函数参数名改为 numerator 和 denominator,使用旧的 number 和 divisor 参数名会导致 KeyError。 +# 结果:捕获并记录此错误。 +print(f"\n{'Example 12':*^50}") try: safe_division_c(number=2, divisor=5) except: @@ -176,7 +245,12 @@ def safe_division_c(numerator, denominator, *, # Changed assert False -# Example 13 +# Example 13 --- 引入位置参数仅限符号 +# 目的:展示如何使用 `/` 指定仅限位置参数,保证函数的调用清晰度。 +# 解释: +# numerator 和 denominator 参数被设为仅限位置参数,无法通过关键字传递。 +# 结果:只能通过位置参数传递 numerator 和 denominator,代码更具可读性。 +print(f"\n{'Example 13':*^50}") def safe_division_d(numerator, denominator, /, *, # Changed ignore_overflow=False, ignore_zero_division=False): @@ -194,11 +268,21 @@ def safe_division_d(numerator, denominator, /, *, # Changed raise -# Example 14 +# Example 14 --- 使用位置参数调用 +# 目的:展示如何通过位置参数调用 numerator 和 denominator。 +# 解释: +# numerator 和 denominator 是仅限位置参数,必须通过位置参数传递。 +# 结果:返回正确的除法结果。 +print(f"\n{'Example 14':*^50}") assert safe_division_d(2, 5) == 0.4 -# Example 15 +# Example 15 --- 通过关键字参数调用会报错 +# 目的:展示使用关键字参数传递仅限位置参数时会报错。 +# 解释: +# numerator 和 denominator 被定义为仅限位置参数,通过关键字传递会导致错误。 +# 结果:捕获并记录此错误。 +print(f"\n{'Example 15':*^50}") try: safe_division_d(numerator=2, denominator=5) except: @@ -207,7 +291,12 @@ def safe_division_d(numerator, denominator, /, *, # Changed assert False -# Example 16 +# Example 16 --- 引入额外参数 +# 目的:展示如何通过位置参数传递额外参数。 +# 解释: +# 引入 ndigits 参数控制返回值的小数位数。numerator 和 denominator 依旧是仅限位置参数。 +# 结果:返回四舍五入到指定小数位数的结果。 +print(f"\n{'Example 16':*^50}") def safe_division_e(numerator, denominator, /, ndigits=10, *, # Changed ignore_overflow=False, @@ -227,7 +316,12 @@ def safe_division_e(numerator, denominator, /, raise -# Example 17 +# Example 17 --- 使用默认和自定义精度 +# 目的:展示如何通过 ndigits 参数控制返回结果的精度。 +# 解释: +# 通过位置参数传递 numerator 和 denominator,通过 ndigits 参数控制返回结果的精度。 +# 结果:返回四舍五入到不同小数位数的结果。 +print(f"\n{'Example 17':*^50}") result = safe_division_e(22, 7) print(result) diff --git a/example_code/item_26.py b/example_code/item_26.py index 105b47b..13b9def 100755 --- a/example_code/item_26.py +++ b/example_code/item_26.py @@ -14,8 +14,18 @@ # See the License for the specific language governing permissions and # limitations under the License. + +# 军规 26: Use functools.wraps to Improve Decorators +# 军规 26: 使用 functools.wraps 改善装饰器 + +""" +Use functools.wraps to Improve Decorators +使用 functools.wraps 改善装饰器 +""" + # Reproduce book environment import random + random.seed(1234) import logging @@ -37,26 +47,42 @@ atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) + def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() + atexit.register(close_open_files) +# Example 1 --- 基本装饰器示例 +# 目的:展示一个简单的装饰器,它包装一个函数并打印调用信息。 +# 解释: +# trace 是一个装饰器函数,它接受一个函数并返回一个新的包装函数。包装函数调用原函数,并在调用前后打印调用信息。 +# 结果:装饰后的函数每次被调用时都会打印参数和值。 +print(f"\n{'Example 1':*^50}") + -# Example 1 def trace(func): def wrapper(*args, **kwargs): result = func(*args, **kwargs) print(f'{func.__name__}({args!r}, {kwargs!r}) ' f'-> {result!r}') return result + return wrapper -# Example 2 +# Example 2 --- 使用装饰器装饰 Fibonacci 函数 +# 目的:展示如何通过 @trace 装饰器装饰 Fibonacci 函数。 +# 解释: +# Fibonacci 函数被装饰后,调用时不仅会计算 Fibonacci 数列,还会打印每次递归调用的输入和输出。 +# 结果:每次 Fibonacci 函数被调用时,都会打印递归调用的详细信息。 +print(f"\n{'Example 2':*^50}") + + @trace def fibonacci(n): """Return the n-th Fibonacci number""" @@ -65,42 +91,71 @@ def fibonacci(n): return (fibonacci(n - 2) + fibonacci(n - 1)) -# Example 3 +# Example 3 --- 手动应用装饰器 +# 目的:展示如何手动将装饰器应用于函数。 +# 解释: +# 通过显式调用 trace 函数,手动将 Fibonacci 函数进行装饰。 +# 结果:与使用 @trace 的效果相同。 +print(f"\n{'Example 3':*^50}") + + def fibonacci(n): """Return the n-th Fibonacci number""" if n in (0, 1): return n return fibonacci(n - 2) + fibonacci(n - 1) -fibonacci = trace(fibonacci) +fibonacci = trace(fibonacci) -# Example 4 +# Example 4 --- 测试装饰器 +# 目的:展示装饰器的实际调用效果。 +# 解释: +# 调用 Fibonacci(4) 时,装饰器会打印每次递归调用的参数和返回值。 +# 结果:每次递归调用都会输出参数和返回值。 +print(f"\n{'Example 4':*^50}") fibonacci(4) - -# Example 5 +# Example 5 --- 打印装饰后的函数 +# 目的:展示装饰后的函数本质上是装饰器返回的包装函数,而不是原始的 Fibonacci 函数。 +# 解释: +# 打印函数时显示的是包装函数而不是原始函数。 +# 结果:输出显示装饰后的函数是 wrapper 而不是 Fibonacci。 +print(f"\n{'Example 5':*^50}") print(fibonacci) - -# Example 6 +# Example 6 --- 检查装饰后的函数的帮助信息 +# 目的:展示使用装饰器后,原始函数的帮助信息可能丢失。 +# 解释: +# 调用 help 时,输出的是包装函数的信息,而不是原始函数的 docstring 或参数信息。 +# 结果:help 显示的是 wrapper 函数的默认信息。 +print(f"\n{'Example 6':*^50}") help(fibonacci) - -# Example 7 +# Example 7 --- 装饰器和序列化的兼容性问题 +# 目的:展示装饰器可能导致函数序列化失败。 +# 解释: +# 由于包装函数是新的对象,它不再是原始函数,这可能会导致像 pickle 这样的序列化工具报错。 +# 结果:尝试序列化装饰后的函数时会引发错误。 +print(f"\n{'Example 7':*^50}") try: import pickle - + pickle.dumps(fibonacci) except: logging.exception('Expected') else: assert False - -# Example 8 +# Example 8 --- 使用 functools.wraps 修复装饰器问题 +# 目的:通过 functools.wraps 保留原始函数的元数据。 +# 解释: +# @wraps 装饰器会将原始函数的元数据(如名称、docstring)复制到包装函数上,避免信息丢失。 +# 结果:装饰器不再改变原始函数的名称、docstring 和其他元数据。 +print(f"\n{'Example 8':*^50}") from functools import wraps + def trace(func): @wraps(func) def wrapper(*args, **kwargs): @@ -108,8 +163,10 @@ def wrapper(*args, **kwargs): print(f'{func.__name__}({args!r}, {kwargs!r}) ' f'-> {result!r}') return result + return wrapper + @trace def fibonacci(n): """Return the n-th Fibonacci number""" @@ -118,9 +175,18 @@ def fibonacci(n): return fibonacci(n - 2) + fibonacci(n - 1) -# Example 9 +# Example 9 --- 使用 wraps 后的帮助信息 +# 目的:展示使用 functools.wraps 后,函数的帮助信息保留完整。 +# 解释: +# @wraps 保留了 Fibonacci 函数的元数据,因此 help 输出的是原始函数的帮助信息,而不是包装函数的信息。 +# 结果:help 正确显示 Fibonacci 函数的 docstring 和参数信息。 +print(f"\n{'Example 9':*^50}") help(fibonacci) - -# Example 10 +# Example 10 --- 使用 wraps 后可以正常序列化 +# 目的:展示使用 functools.wraps 后,函数可以正常进行序列化。 +# 解释: +# @wraps 保留了原始函数的身份信息,因此序列化工具(如 pickle)能够正确处理函数对象。 +# 结果:装饰后的函数可以成功序列化。 +print(f"\n{'Example 10':*^50}") print(pickle.dumps(fibonacci)) From 7a6a01163bb41610f3aaa40b2005e5360871074c Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Tue, 24 Sep 2024 09:24:28 +0800 Subject: [PATCH 17/59] modify item_27-29.py --- example_code/item_27.py | 47 +++++++++++++++++++++++------ example_code/item_28.py | 39 +++++++++++++++++++----- example_code/item_29.py | 66 ++++++++++++++++++++++++++++++++--------- 3 files changed, 122 insertions(+), 30 deletions(-) diff --git a/example_code/item_27.py b/example_code/item_27.py index c1a4847..b57a648 100755 --- a/example_code/item_27.py +++ b/example_code/item_27.py @@ -14,6 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 军规 27: Prefer list comprehensions and generator expressions to map and filter. +# 军规 27: 优先使用列表推导式和生成器表达式,而不是 map 和 filter。 + +""" +Prefer list comprehensions and generator expressions to map and filter +优先使用列表推导式和生成器表达式,而不是 map 和 filter +""" + # Reproduce book environment import random random.seed(1234) @@ -46,7 +54,10 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 使用循环生成平方列表 +# 目的:展示传统方法生成平方列表的方式。 +# 结果:输出列表中每个元素的平方。 +print(f"\n{'Example 1':*^50}") a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] squares = [] for x in a: @@ -54,37 +65,55 @@ def close_open_files(): print(squares) -# Example 2 +# Example 2 --- 列表推导式生成平方列表 +# 目的:展示使用列表推导式生成平方列表的简洁方式。 +# 结果:输出列表中每个元素的平方。 +print(f"\n{'Example 2':*^50}") squares = [x**2 for x in a] # List comprehension print(squares) -# Example 3 +# Example 3 --- 使用 map 生成平方列表 +# 目的:展示使用 map 函数生成平方列表的方式,并进行比较。 +# 结果:确保 map 生成的结果与之前的列表相同。 +print(f"\n{'Example 3':*^50}") alt = map(lambda x: x ** 2, a) assert list(alt) == squares, f'{alt} {squares}' -# Example 4 +# Example 4 --- 列表推导式生成偶数平方列表 +# 目的:展示如何使用列表推导式筛选偶数并生成其平方。 +# 结果:输出列表中偶数元素的平方。 +print(f"\n{'Example 4':*^50}") even_squares = [x**2 for x in a if x % 2 == 0] print(even_squares) -# Example 5 +# Example 5 --- 使用 map 和 filter 生成偶数平方列表 +# 目的:展示使用 map 和 filter 结合生成偶数平方的方式。 +# 结果:确保 filter 和 map 组合的结果与之前的列表相同。 +print(f"\n{'Example 5':*^50}") alt = map(lambda x: x**2, filter(lambda x: x % 2 == 0, a)) assert even_squares == list(alt) -# Example 6 +# Example 6 --- 使用字典推导式和集合推导式 +# 目的:展示如何使用字典推导式和集合推导式。 +# 结果:输出偶数平方的字典和可被3整除的数的立方的集合。 +print(f"\n{'Example 6':*^50}") even_squares_dict = {x: x**2 for x in a if x % 2 == 0} threes_cubed_set = {x**3 for x in a if x % 3 == 0} print(even_squares_dict) print(threes_cubed_set) -# Example 7 +# Example 7 --- 使用 map 和 filter 生成字典和集合 +# 目的:展示如何使用 map 和 filter 组合生成字典和集合,并进行比较。 +# 结果:确保字典和集合的结果与之前的推导式相同。 +print(f"\n{'Example 7':*^50}") alt_dict = dict(map(lambda x: (x, x**2), - filter(lambda x: x % 2 == 0, a))) + filter(lambda x: x % 2 == 0, a))) alt_set = set(map(lambda x: x**3, - filter(lambda x: x % 3 == 0, a))) + filter(lambda x: x % 3 == 0, a))) assert even_squares_dict == alt_dict assert threes_cubed_set == alt_set diff --git a/example_code/item_28.py b/example_code/item_28.py index d1efe24..70ac238 100755 --- a/example_code/item_28.py +++ b/example_code/item_28.py @@ -14,6 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 军规 28: 用列表推导式代替嵌套循环 +# 军规 28: Use list comprehensions instead of nested loops + +""" +Use list comprehensions instead of nested loops +用列表推导式代替嵌套循环 +""" + # Reproduce book environment import random random.seed(1234) @@ -46,18 +54,27 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 将二维数组扁平化 +# 目的:将一个二维矩阵扁平化为一维列表 +# 结果:输出扁平化后的列表。 +print(f"\n{'Example 1':*^50}") matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] flat = [x for row in matrix for x in row] print(flat) -# Example 2 +# Example 2 --- 将二维数组中的元素平方 +# 目的:将每个元素平方并保持二维结构 +# 结果:输出平方后的二维列表。 +print(f"\n{'Example 2':*^50}") squared = [[x**2 for x in row] for row in matrix] print(squared) -# Example 3 +# Example 3 --- 扁平化多层嵌套列表 +# 目的:将多层嵌套的列表扁平化 +# 结果:输出扁平化后的列表。 +print(f"\n{'Example 3':*^50}") my_lists = [ [[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]], @@ -68,7 +85,10 @@ def close_open_files(): print(flat) -# Example 4 +# Example 4 --- 使用循环扩展列表 +# 目的:使用嵌套循环将多层嵌套的列表扁平化 +# 结果:输出扁平化后的列表。 +print(f"\n{'Example 4':*^50}") flat = [] for sublist1 in my_lists: for sublist2 in sublist1: @@ -76,7 +96,10 @@ def close_open_files(): print(flat) -# Example 5 +# Example 5 --- 使用条件生成列表 +# 目的:展示使用多个条件生成列表的效果 +# 结果:输出符合条件的列表。 +print(f"\n{'Example 5':*^50}") a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] b = [x for x in a if x > 4 if x % 2 == 0] c = [x for x in a if x > 4 and x % 2 == 0] @@ -86,8 +109,10 @@ def close_open_files(): assert b == c -# Example 6 -matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] +# Example 6 --- 使用条件过滤二维数组 +# 目的:展示如何根据条件过滤二维数组 +# 结果:输出符合条件的二维数组。 +print(f"\n{'Example 6':*^50}") filtered = [[x for x in row if x % 3 == 0] for row in matrix if sum(row) >= 10] print(filtered) diff --git a/example_code/item_29.py b/example_code/item_29.py index 207faf1..3bfa503 100755 --- a/example_code/item_29.py +++ b/example_code/item_29.py @@ -14,6 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 军规 29: 只在表达式上下文中使用赋值表达式 +# 军规 29: Use assignment expressions only in expression contexts + +""" +Use assignment expressions only in expression contexts +只在表达式上下文中使用赋值表达式 +""" + # Reproduce book environment import random random.seed(1234) @@ -46,7 +54,10 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 计算批次 +# 目的:计算每种物品可以分成多少批 +# 结果:输出可以分成的批次数。 +print(f"\n{'Example 1':*^50}") stock = { 'nails': 125, 'screws': 35, @@ -61,22 +72,28 @@ def get_batches(count, size): result = {} for name in order: - count = stock.get(name, 0) - batches = get_batches(count, 8) - if batches: - result[name] = batches + count = stock.get(name, 0) + batches = get_batches(count, 8) + if batches: + result[name] = batches print(result) -# Example 2 +# Example 2 --- 使用字典推导式 +# 目的:使用字典推导式计算每种物品的批次数 +# 结果:输出符合条件的字典。 +print(f"\n{'Example 2':*^50}") found = {name: get_batches(stock.get(name, 0), 8) for name in order if get_batches(stock.get(name, 0), 8)} print(found) -# Example 3 +# Example 3 --- 计算批次的另一种方式 +# 目的:展示另一种计算批次的方法 +# 结果:输出符合条件的字典。 +print(f"\n{'Example 3':*^50}") has_bug = {name: get_batches(stock.get(name, 0), 4) for name in order if get_batches(stock.get(name, 0), 8)} @@ -85,13 +102,19 @@ def get_batches(count, size): print('Found: ', has_bug) -# Example 4 +# Example 4 --- 使用赋值表达式 +# 目的:使用赋值表达式简化代码 +# 结果:验证结果是否正确。 +print(f"\n{'Example 4':*^50}") found = {name: batches for name in order if (batches := get_batches(stock.get(name, 0), 8))} assert found == {'screws': 4, 'wingnuts': 1}, found -# Example 5 +# Example 5 --- 赋值表达式错误示例 +# 目的:展示不当使用赋值表达式时的错误 +# 结果:抛出异常并记录日志。 +print(f"\n{'Example 5':*^50}") try: result = {name: (tenth := count // 10) for name, count in stock.items() if tenth > 0} @@ -101,24 +124,36 @@ def get_batches(count, size): assert False -# Example 6 +# Example 6 --- 正确使用赋值表达式 +# 目的:使用赋值表达式正确计算物品数量 +# 结果:输出符合条件的字典。 +print(f"\n{'Example 6':*^50}") result = {name: tenth for name, count in stock.items() if (tenth := count // 10) > 0} print(result) -# Example 7 +# Example 7 --- 使用赋值表达式 +# 目的:展示在列表推导式中使用赋值表达式 +# 结果:输出最后一项。 +print(f"\n{'Example 7':*^50}") half = [(last := count // 2) for count in stock.values()] print(f'Last item of {half} is {last}') -# Example 8 +# Example 8 --- 漏洞示例 +# 目的:展示循环变量的泄漏问题 +# 结果:输出最后一项。 +print(f"\n{'Example 8':*^50}") for count in stock.values(): # Leaks loop variable pass print(f'Last item of {list(stock.values())} is {count}') -# Example 9 +# Example 9 --- 赋值表达式和异常 +# 目的:展示循环变量不会泄漏 +# 结果:抛出异常并记录日志。 +print(f"\n{'Example 9':*^50}") try: del count half = [count // 2 for count in stock.values()] @@ -130,7 +165,10 @@ def get_batches(count, size): assert False -# Example 10 +# Example 10 --- 使用生成器表达式 +# 目的:展示生成器表达式的用法 +# 结果:输出生成器的下一个元素。 +print(f"\n{'Example 10':*^50}") found = ((name, batches) for name in order if (batches := get_batches(stock.get(name, 0), 8))) print(next(found)) From 0e034fa7b7364b6e964a76d98ec2007b6a64b97e Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Tue, 24 Sep 2024 10:03:38 +0800 Subject: [PATCH 18/59] modify item_30-34.py --- example_code/item_30.py | 29 +++++--- example_code/item_31.py | 78 +++++++++++++++------- example_code/item_32.py | 22 +++++-- example_code/item_33.py | 67 +++++++++++-------- example_code/item_34.py | 143 ++++++++++++++++++++-------------------- 5 files changed, 202 insertions(+), 137 deletions(-) diff --git a/example_code/item_30.py b/example_code/item_30.py index ba4fa9b..9e53889 100755 --- a/example_code/item_30.py +++ b/example_code/item_30.py @@ -46,7 +46,9 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 索引单词 +# 目的:通过空格索引文本中的单词 +# 结果:返回单词的索引列表。 def index_words(text): result = [] if text: @@ -57,14 +59,17 @@ def index_words(text): return result -# Example 2 -address = 'Four score and seven years ago...' +# Example 2 --- 使用函数 +# 目的:索引一段文本中的单词 +# 结果:输出前10个单词的索引。 address = 'Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal.' result = index_words(address) print(result[:10]) -# Example 3 +# Example 3 --- 生成器实现 +# 目的:使用生成器索引文本中的单词 +# 结果:返回索引的生成器。 def index_words_iter(text): if text: yield 0 @@ -73,18 +78,24 @@ def index_words_iter(text): yield index + 1 -# Example 4 +# Example 4 --- 获取生成器的值 +# 目的:从生成器中获取单个索引 +# 结果:输出生成器的前两个值。 it = index_words_iter(address) print(next(it)) print(next(it)) -# Example 5 +# Example 5 --- 列表转换 +# 目的:将生成器的所有值转换为列表 +# 结果:输出前10个索引。 result = list(index_words_iter(address)) print(result[:10]) -# Example 6 +# Example 6 --- 索引文件内容 +# 目的:通过文件句柄索引文件中的每一行 +# 结果:返回每一行的起始偏移量。 def index_file(handle): offset = 0 for line in handle: @@ -96,7 +107,9 @@ def index_file(handle): yield offset -# Example 7 +# Example 7 --- 写入并读取文件 +# 目的:将文本写入文件并读取索引 +# 结果:输出文件前10个单词的索引。 address_lines = """Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, diff --git a/example_code/item_31.py b/example_code/item_31.py index 122d480..039a713 100755 --- a/example_code/item_31.py +++ b/example_code/item_31.py @@ -46,7 +46,9 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# Example 1 --- 规范化数字 +# 目的:计算给定数字列表的百分比 +# 结果:返回每个数字占总和的百分比。 def normalize(numbers): total = sum(numbers) result = [] @@ -56,14 +58,18 @@ def normalize(numbers): return result -# Example 2 +# Example 2 --- 测试规范化 +# 目的:使用示例数据进行测试 +# 结果:输出每个数字的百分比。 visits = [15, 35, 80] percentages = normalize(visits) print(percentages) assert sum(percentages) == 100.0 -# Example 3 +# Example 3 --- 从文件读取数据 +# 目的:将数字写入文件 +# 结果:创建包含数字的文本文件。 path = 'my_numbers.txt' with open(path, 'w') as f: for i in (15, 35, 80): @@ -75,21 +81,27 @@ def read_visits(data_path): yield int(line) -# Example 4 +# Example 4 --- 使用生成器读取数据 +# 目的:从文件中读取数字并规范化 +# 结果:输出每个数字的百分比。 it = read_visits('my_numbers.txt') percentages = normalize(it) print(percentages) -# Example 5 +# Example 5 --- 生成器耗尽 +# 目的:演示生成器只能迭代一次 +# 结果:显示生成器已耗尽。 it = read_visits('my_numbers.txt') print(list(it)) -print(list(it)) # Already exhausted +print(list(it)) # 已经耗尽 -# Example 6 +# Example 6 --- 复制迭代器 +# 目的:在规范化中复制迭代器 +# 结果:输出每个数字的百分比。 def normalize_copy(numbers): - numbers_copy = list(numbers) # Copy the iterator + numbers_copy = list(numbers) # 复制迭代器 total = sum(numbers_copy) result = [] for value in numbers_copy: @@ -98,31 +110,39 @@ def normalize_copy(numbers): return result -# Example 7 +# Example 7 --- 使用复制的迭代器 +# 目的:测试复制迭代器的规范化 +# 结果:输出每个数字的百分比。 it = read_visits('my_numbers.txt') percentages = normalize_copy(it) print(percentages) assert sum(percentages) == 100.0 -# Example 8 +# Example 8 --- 使用函数获取迭代器 +# 目的:使用提供的函数获取新的迭代器 +# 结果:输出每个数字的百分比。 def normalize_func(get_iter): - total = sum(get_iter()) # New iterator + total = sum(get_iter()) # 新的迭代器 result = [] - for value in get_iter(): # New iterator + for value in get_iter(): # 新的迭代器 percent = 100 * value / total result.append(percent) return result -# Example 9 +# Example 9 --- 从文件中规范化 +# 目的:从文件读取数字并规范化 +# 结果:输出每个数字的百分比。 path = 'my_numbers.txt' percentages = normalize_func(lambda: read_visits(path)) print(percentages) assert sum(percentages) == 100.0 -# Example 10 +# Example 10 --- 自定义迭代器类 +# 目的:创建一个可迭代的类 +# 结果:能够从文件中读取数字。 class ReadVisits: def __init__(self, data_path): self.data_path = data_path @@ -133,16 +153,20 @@ def __iter__(self): yield int(line) -# Example 11 +# Example 11 --- 使用自定义迭代器 +# 目的:从自定义迭代器读取数据并规范化 +# 结果:输出每个数字的百分比。 visits = ReadVisits(path) percentages = normalize(visits) print(percentages) assert sum(percentages) == 100.0 -# Example 12 +# Example 12 --- 防御性编程 +# 目的:确保输入为容器而非迭代器 +# 结果:抛出类型错误。 def normalize_defensive(numbers): - if iter(numbers) is numbers: # An iterator -- bad! + if iter(numbers) is numbers: # 这是一个迭代器 -- 不允许! raise TypeError('Must supply a container') total = sum(numbers) result = [] @@ -152,7 +176,7 @@ def normalize_defensive(numbers): return result visits = [15, 35, 80] -normalize_defensive(visits) # No error +normalize_defensive(visits) # 无错误 it = iter(visits) try: @@ -163,11 +187,13 @@ def normalize_defensive(numbers): assert False -# Example 13 -from collections.abc import Iterator +# Example 13 --- 使用Iterator检查类型 +# 目的:确保输入为容器而非迭代器 +# 结果:抛出类型错误。 +from collections.abc import Iterator def normalize_defensive(numbers): - if isinstance(numbers, Iterator): # Another way to check + if isinstance(numbers, Iterator): # 另一种检查方法 raise TypeError('Must supply a container') total = sum(numbers) result = [] @@ -177,7 +203,7 @@ def normalize_defensive(numbers): return result visits = [15, 35, 80] -normalize_defensive(visits) # No error +normalize_defensive(visits) # 无错误 it = iter(visits) try: @@ -188,7 +214,9 @@ def normalize_defensive(numbers): assert False -# Example 14 +# Example 14 --- 测试防御性编程 +# 目的:确保容器的输入正确 +# 结果:测试各种情况确保正确性。 visits = [15, 35, 80] percentages = normalize_defensive(visits) assert sum(percentages) == 100.0 @@ -198,7 +226,9 @@ def normalize_defensive(numbers): assert sum(percentages) == 100.0 -# Example 15 +# Example 15 --- 捕获预期异常 +# 目的:捕获类型错误 +# 结果:记录错误信息。 try: visits = [15, 35, 80] it = iter(visits) diff --git a/example_code/item_32.py b/example_code/item_32.py index 1cefe9b..bc1c01e 100755 --- a/example_code/item_32.py +++ b/example_code/item_32.py @@ -46,9 +46,9 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 -import random - +# Example 1 --- 创建文件并写入随机长度的字符串 +# 目的:生成一个文件,其中每一行是随机长度的'a'字符 +# 结果:输出每行字符的长度。 with open('my_file.txt', 'w') as f: for _ in range(10): f.write('a' * random.randint(0, 100)) @@ -58,19 +58,27 @@ def close_open_files(): print(value) -# Example 2 +# Example 2 --- 使用生成器表达式 +# 目的:创建一个生成器来获取文件中每行的长度 +# 结果:生成器本身不会立即计算。 it = (len(x) for x in open('my_file.txt')) print(it) -# Example 3 +# Example 3 --- 获取生成器的下一个值 +# 目的:演示如何从生成器中获取值 +# 结果:输出第一行的长度。 print(next(it)) print(next(it)) -# Example 4 +# Example 4 --- 计算平方根 +# 目的:为每个长度计算平方根并生成元组 +# 结果:创建一个新的生成器。 roots = ((x, x**0.5) for x in it) -# Example 5 +# Example 5 --- 获取平方根生成器的下一个值 +# 目的:输出当前的平方根值 +# 结果:显示平方根的值,但注意此时生成器已经耗尽。 print(next(roots)) diff --git a/example_code/item_33.py b/example_code/item_33.py index d7bd5c6..e11a7b0 100755 --- a/example_code/item_33.py +++ b/example_code/item_33.py @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 军规33:尽量减少复杂性,优先选择简单的解决方案,保持代码可读性和可维护性。 + # Reproduce book environment import random random.seed(1234) @@ -30,88 +32,99 @@ import tempfile TEST_DIR = tempfile.TemporaryDirectory() -atexit.register(TEST_DIR.cleanup) +atexit.register(TEST_DIR.cleanup) # 程序结束时清理临时目录 # Make sure Windows processes exit cleanly OLD_CWD = os.getcwd() -atexit.register(lambda: os.chdir(OLD_CWD)) -os.chdir(TEST_DIR.name) +atexit.register(lambda: os.chdir(OLD_CWD)) # 程序结束时恢复工作目录 +os.chdir(TEST_DIR.name) # 切换到临时目录 def close_open_files(): - everything = gc.get_objects() + everything = gc.get_objects() # 获取所有对象 for obj in everything: - if isinstance(obj, io.IOBase): - obj.close() - -atexit.register(close_open_files) + if isinstance(obj, io.IOBase): # 检查是否为文件对象 + obj.close() # 关闭文件 +atexit.register(close_open_files) # 程序结束时关闭文件 # Example 1 +print(f"\n{'Example 1':*^50}") +# 定义一个生成器函数,模拟移动 def move(period, speed): for _ in range(period): - yield speed + yield speed # 生成速度值 +# 定义一个生成器函数,模拟暂停 def pause(delay): for _ in range(delay): - yield 0 - + yield 0 # 生成0,表示暂停 # Example 2 +print(f"\n{'Example 2':*^50}") +# 定义一个动画函数,组合移动和暂停 def animate(): - for delta in move(4, 5.0): + for delta in move(4, 5.0): # 移动4个单位,速度为5.0 yield delta - for delta in pause(3): + for delta in pause(3): # 暂停3个单位 yield delta - for delta in move(2, 3.0): + for delta in move(2, 3.0): # 移动2个单位,速度为3.0 yield delta - # Example 3 +print(f"\n{'Example 3':*^50}") +# 渲染函数,输出每次的delta值 def render(delta): - print(f'Delta: {delta:.1f}') - # Move the images onscreen + print(f'Delta: {delta:.1f}') # 输出delta值 +# 运行函数,调用传入的生成器函数 def run(func): - for delta in func(): - render(delta) - -run(animate) + for delta in func(): # 遍历生成器 + render(delta) # 渲染delta值 +run(animate) # 运行动画函数 # Example 4 +print(f"\n{'Example 4':*^50}") +# 使用yield from简化动画组合 def animate_composed(): - yield from move(4, 5.0) - yield from pause(3) - yield from move(2, 3.0) - -run(animate_composed) + yield from move(4, 5.0) # 移动 + yield from pause(3) # 暂停 + yield from move(2, 3.0) # 移动 +run(animate_composed) # 运行组合动画函数 # Example 5 +print(f"\n{'Example 5':*^50}") import timeit +# 定义生成器,产生1000000个数字 def child(): for i in range(1_000_000): yield i +# 手动嵌套的生成器 def slow(): for i in child(): yield i +# 简化嵌套的生成器 def fast(): yield from child() +# 测试手动嵌套的性能 baseline = timeit.timeit( stmt='for _ in slow(): pass', globals=globals(), number=50) print(f'Manual nesting {baseline:.2f}s') +# 测试简化嵌套的性能 comparison = timeit.timeit( stmt='for _ in fast(): pass', globals=globals(), number=50) print(f'Composed nesting {comparison:.2f}s') +# 计算时间差异 reduction = -(comparison - baseline) / baseline -print(f'{reduction:.1%} less time') +print(f'{reduction:.1%} less time') # 输出减少的时间百分比 diff --git a/example_code/item_34.py b/example_code/item_34.py index 6a82a35..a514ac7 100755 --- a/example_code/item_34.py +++ b/example_code/item_34.py @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 军规33:尽量减少复杂性,优先选择简单的解决方案,保持代码可读性和可维护性。 + # Reproduce book environment import random random.seed(1234) @@ -30,142 +32,141 @@ import tempfile TEST_DIR = tempfile.TemporaryDirectory() -atexit.register(TEST_DIR.cleanup) +atexit.register(TEST_DIR.cleanup) # 程序结束时清理临时目录 # Make sure Windows processes exit cleanly OLD_CWD = os.getcwd() -atexit.register(lambda: os.chdir(OLD_CWD)) -os.chdir(TEST_DIR.name) +atexit.register(lambda: os.chdir(OLD_CWD)) # 程序结束时恢复工作目录 +os.chdir(TEST_DIR.name) # 切换到临时目录 def close_open_files(): - everything = gc.get_objects() + everything = gc.get_objects() # 获取所有对象 for obj in everything: - if isinstance(obj, io.IOBase): - obj.close() - -atexit.register(close_open_files) + if isinstance(obj, io.IOBase): # 检查是否为文件对象 + obj.close() # 关闭文件 +atexit.register(close_open_files) # 程序结束时关闭文件 # Example 1 +print(f"\n{'Example 1':*^50}") import math def wave(amplitude, steps): - step_size = 2 * math.pi / steps + step_size = 2 * math.pi / steps # 计算步长 for step in range(steps): - radians = step * step_size - fraction = math.sin(radians) - output = amplitude * fraction - yield output - + radians = step * step_size # 计算弧度 + fraction = math.sin(radians) # 计算正弦值 + output = amplitude * fraction # 计算输出 + yield output # 生成输出值 # Example 2 +print(f"\n{'Example 2':*^50}") def transmit(output): if output is None: - print(f'Output is None') + print(f'Output is None') # 输出为None时的处理 else: - print(f'Output: {output:>5.1f}') + print(f'Output: {output:>5.1f}') # 格式化输出 def run(it): - for output in it: - transmit(output) - -run(wave(3.0, 8)) + for output in it: # 遍历生成器 + transmit(output) # 传输输出 +run(wave(3.0, 8)) # 运行波形生成器 # Example 3 +print(f"\n{'Example 3':*^50}") def my_generator(): - received = yield 1 - print(f'received = {received}') + received = yield 1 # 初始输出为1 + print(f'received = {received}') # 打印接收到的值 it = my_generator() -output = next(it) # Get first generator output -print(f'output = {output}') +output = next(it) # 获取第一个生成器输出 +print(f'output = {output}') # 输出 try: - next(it) # Run generator until it exits + next(it) # 运行生成器直到退出 except StopIteration: pass else: - assert False - + assert False # 不应该执行到这里 # Example 4 +print(f"\n{'Example 4':*^50}") it = my_generator() -output = it.send(None) # Get first generator output -print(f'output = {output}') +output = it.send(None) # 获取第一个生成器输出 +print(f'output = {output}') # 输出 try: - it.send('hello!') # Send value into the generator + it.send('hello!') # 向生成器发送值 except StopIteration: pass else: - assert False - + assert False # 不应该执行到这里 # Example 5 +print(f"\n{'Example 5':*^50}") def wave_modulating(steps): - step_size = 2 * math.pi / steps - amplitude = yield # Receive initial amplitude + step_size = 2 * math.pi / steps # 计算步长 + amplitude = yield # 接收初始幅度 for step in range(steps): - radians = step * step_size - fraction = math.sin(radians) - output = amplitude * fraction - amplitude = yield output # Receive next amplitude - + radians = step * step_size # 计算弧度 + fraction = math.sin(radians) # 计算正弦值 + output = amplitude * fraction # 计算输出 + amplitude = yield output # 接收下一个幅度 # Example 6 +print(f"\n{'Example 6':*^50}") def run_modulating(it): - amplitudes = [ - None, 7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10] + amplitudes = [None, 7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10] # 幅度列表 for amplitude in amplitudes: - output = it.send(amplitude) - transmit(output) - -run_modulating(wave_modulating(12)) + output = it.send(amplitude) # 发送幅度并获取输出 + transmit(output) # 传输输出 +run_modulating(wave_modulating(12)) # 运行幅度调制的波形生成器 # Example 7 +print(f"\n{'Example 7':*^50}") def complex_wave(): - yield from wave(7.0, 3) - yield from wave(2.0, 4) - yield from wave(10.0, 5) - -run(complex_wave()) + yield from wave(7.0, 3) # 从wave生成器中生成值 + yield from wave(2.0, 4) # 从wave生成器中生成值 + yield from wave(10.0, 5) # 从wave生成器中生成值 +run(complex_wave()) # 运行复合波形生成器 # Example 8 +print(f"\n{'Example 8':*^50}") def complex_wave_modulating(): - yield from wave_modulating(3) - yield from wave_modulating(4) - yield from wave_modulating(5) - -run_modulating(complex_wave_modulating()) + yield from wave_modulating(3) # 从幅度调制生成器中生成值 + yield from wave_modulating(4) # 从幅度调制生成器中生成值 + yield from wave_modulating(5) # 从幅度调制生成器中生成值 +run_modulating(complex_wave_modulating()) # 运行复合幅度调制生成器 # Example 9 +print(f"\n{'Example 9':*^50}") def wave_cascading(amplitude_it, steps): - step_size = 2 * math.pi / steps + step_size = 2 * math.pi / steps # 计算步长 for step in range(steps): - radians = step * step_size - fraction = math.sin(radians) - amplitude = next(amplitude_it) # Get next input - output = amplitude * fraction - yield output - + radians = step * step_size # 计算弧度 + fraction = math.sin(radians) # 计算正弦值 + amplitude = next(amplitude_it) # 获取下一个输入 + output = amplitude * fraction # 计算输出 + yield output # 生成输出值 # Example 10 +print(f"\n{'Example 10':*^50}") def complex_wave_cascading(amplitude_it): - yield from wave_cascading(amplitude_it, 3) - yield from wave_cascading(amplitude_it, 4) - yield from wave_cascading(amplitude_it, 5) - + yield from wave_cascading(amplitude_it, 3) # 从波形生成器中生成值 + yield from wave_cascading(amplitude_it, 4) # 从波形生成器中生成值 + yield from wave_cascading(amplitude_it, 5) # 从波形生成器中生成值 # Example 11 +print(f"\n{'Example 11':*^50}") def run_cascading(): - amplitudes = [7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10] - it = complex_wave_cascading(iter(amplitudes)) + amplitudes = [7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10] # 幅度列表 + it = complex_wave_cascading(iter(amplitudes)) # 创建复合波形生成器 for amplitude in amplitudes: - output = next(it) - transmit(output) + output = next(it) # 获取下一个输出 + transmit(output) # 传输输出 -run_cascading() +run_cascading() # 运行级联波形生成器 From 4f6e70a77c9aa9b8e92b8675c4fb8d5d99d2f17d Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Tue, 24 Sep 2024 10:25:13 +0800 Subject: [PATCH 19/59] modify item_10-11.py --- example_code/item_10.py | 4 ++++ example_code/item_11.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/example_code/item_10.py b/example_code/item_10.py index 9dc0c5c..1d0745a 100755 --- a/example_code/item_10.py +++ b/example_code/item_10.py @@ -61,6 +61,7 @@ def close_open_files(): # fresh_fruit 是一个字典,保存了不同种类水果的数量。 # 结果:水果库存分别为 10 个苹果,8 个香蕉,5 个柠檬。 print(f"\n{'Example 1':*^50}") + fresh_fruit = { 'apple': 10, 'banana': 8, @@ -75,6 +76,7 @@ def close_open_files(): # 如果有柠檬,调用 make_lemonade(),否则调用 out_of_stock()。 # 结果:根据柠檬库存调用不同的函数。 print(f"\n{'Example 2':*^50}") + def make_lemonade(count): print(f'Making {count} lemons into lemonade') @@ -94,6 +96,7 @@ def out_of_stock(): # 使用赋值表达式 := 同时进行赋值和判断,简化代码。 # 结果:简化了库存判断的代码结构。 print(f"\n{'Example 3':*^50}") + if count := fresh_fruit.get('lemon', 0): make_lemonade(count) else: @@ -106,6 +109,7 @@ def out_of_stock(): # 使用 fresh_fruit.get('apple', 0) 获取苹果数量,如果数量大于等于 4,就制作苹果汁。 # 结果:根据苹果库存调用 make_cider() 或 out_of_stock()。 print(f"\n{'Example 4':*^50}") + def make_cider(count): print(f'Making cider with {count} apples') diff --git a/example_code/item_11.py b/example_code/item_11.py index 4679b85..97f23f1 100755 --- a/example_code/item_11.py +++ b/example_code/item_11.py @@ -59,7 +59,7 @@ def close_open_files(): # 目的:演示如何通过切片获取列表的中间部分或去掉两端的元素。 # 解释: # a[3:5] 提取列表索引 3 和 4 的元素。 -# a[1:7] 提取索引从 1 到 6 的元素,去掉首尾。 +# a[1:7] 提取索引从 1 到 6 的元素,去掉首尾。也就是所谓的斩头去尾 # 结果:返回列表的相应子部分。 print(f"\n{'Example 1':*^50}") a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] @@ -89,6 +89,7 @@ def close_open_files(): # 目的:展示多种切片用法,使用正索引和负索引。 # 解释: # 使用正负索引和不同的起始、结束位置,提取列表的不同子部分。 +# 这个也就是0的位置分别在首位的效果。 # 结果:输出对应的切片结果。 print(f"\n{'Example 4':*^50}") print(a[:]) From ef4dffb033a4a7220a9b048640614d3412f55c85 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Tue, 24 Sep 2024 13:03:54 +0800 Subject: [PATCH 20/59] modify item_10-11.py --- example_code/item_10.py | 15 ++++--- example_code/item_34.py | 2 +- example_code/item_35.py | 74 ++++++++++++++++++++------------ example_code/item_36.py | 94 +++++++++++++++++++++++++---------------- 4 files changed, 115 insertions(+), 70 deletions(-) diff --git a/example_code/item_10.py b/example_code/item_10.py index 1d0745a..eb235f6 100755 --- a/example_code/item_10.py +++ b/example_code/item_10.py @@ -126,6 +126,7 @@ def make_cider(count): # 使用赋值表达式 := 同时获取苹果库存和进行条件判断,简化代码。 # 结果:简化了代码结构。 print(f"\n{'Example 5':*^50}") + if (count := fresh_fruit.get('apple', 0)) >= 4: make_cider(count) else: @@ -139,23 +140,27 @@ def make_cider(count): # 之后尝试制作奶昔,如果没有足够的香蕉,抛出 OutOfBananas 异常。 # 结果:根据香蕉库存制作奶昔或处理库存不足的情况。 print(f"\n{'Example 6':*^50}") + +# 该函数的功能是香蕉切片 def slice_bananas(count): print(f'Slicing {count} bananas') return count * 4 -class OutOfBananas(Exception): - pass - +# 制作奶昔,如果没有足够的香蕉,抛出 OutOfBananas 异常 def make_smoothies(count): print(f'Making a smoothie with {count} banana slices') +# 定义了一个异常类OutOfBananas +class OutOfBananas(Exception): + # pass是一个空语句,它不执行任何操作。 使用 pass 是为了保持代码结构的完整性 + pass + pieces = 0 count = fresh_fruit.get('banana', 0) if count >= 2: pieces = slice_bananas(count) - try: - smoothies = make_smoothies(pieces) + make_smoothies(pieces) except OutOfBananas: out_of_stock() diff --git a/example_code/item_34.py b/example_code/item_34.py index a514ac7..4f11150 100755 --- a/example_code/item_34.py +++ b/example_code/item_34.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# 军规33:尽量减少复杂性,优先选择简单的解决方案,保持代码可读性和可维护性。 +# 军规34:尽量减少复杂性,优先选择简单的解决方案,保持代码可读性和可维护性。 # Reproduce book environment import random diff --git a/example_code/item_35.py b/example_code/item_35.py index 1d53234..1548f00 100755 --- a/example_code/item_35.py +++ b/example_code/item_35.py @@ -1,28 +1,14 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 # Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. -# # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Reproduce book environment + import random -random.seed(1234) +random.seed(1234) import logging from pprint import pprint from sys import stdout as STDOUT - -# Write all output to a temporary directory import atexit import gc import io @@ -32,30 +18,36 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) + def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() -atexit.register(close_open_files) +atexit.register(close_open_files) # Example 1 +# 目的:演示生成器如何处理异常。 +# 解释:创建一个生成器,使用 it.throw() 引发自定义异常。 +# 结果:捕获并记录 MyError 异常。 +print(f"\n{'Example 1':*^50}") try: class MyError(Exception): pass - + + def my_generator(): yield 1 yield 2 yield 3 - + + it = my_generator() print(next(it)) # Yield 1 print(next(it)) # Yield 2 @@ -65,30 +57,40 @@ def my_generator(): else: assert False - # Example 2 +# 目的:展示生成器中的异常处理。 +# 解释:在生成器中使用 try 捕获 MyError。 +# 结果:打印 "Got MyError!",然后继续生成其他值。 +print(f"\n{'Example 2':*^50}") + + def my_generator(): yield 1 - try: yield 2 except MyError: print('Got MyError!') else: yield 3 - yield 4 + it = my_generator() print(next(it)) # Yield 1 print(next(it)) # Yield 2 print(it.throw(MyError('test error'))) - # Example 3 +# 目的:使用生成器和自定义异常来重置状态。 +# 解释:定义 timer 生成器,可以在 Reset 异常被捕获时重置计数器。 +# 结果:生成器在收到 Reset 异常时,计数器重置为初始值。 +print(f"\n{'Example 3':*^50}") + + class Reset(Exception): pass + def timer(period): current = period while current: @@ -100,19 +102,25 @@ def timer(period): # Example 4 +# 目的:结合外部事件和生成器控制。 +# 解释:通过轮询外部事件决定继续计时还是重置计时器。 +# 结果:在外部事件发生时,生成器根据需要重置计时。 +print(f"\n{'Example 4':*^50}") RESETS = [ False, False, False, True, False, True, False, False, False, False, False, False, False, False] + def check_for_reset(): - # Poll for external event return RESETS.pop(0) + def announce(remaining): print(f'{remaining} ticks remaining') + def run(): - it = timer(4) + it = timer(4) while True: try: if check_for_reset(): @@ -124,10 +132,16 @@ def run(): else: announce(current) -run() +run() # Example 5 +# 目的:使用类实现计时器功能。 +# 解释:Timer 类包含重置功能,并通过迭代器接口提供计时。 +# 结果:通过迭代 Timer 类,输出每个计时值。 +print(f"\n{'Example 5':*^50}") + + class Timer: def __init__(self, period): self.current = period @@ -143,10 +157,15 @@ def __iter__(self): # Example 6 +# 目的:整合类和外部事件处理。 +# 解释:使用 Timer 类,在计时过程中根据外部事件决定是否重置计时器。 +# 结果:在每次迭代中,根据外部事件决定是否重置计时器。 +print(f"\n{'Example 6':*^50}") RESETS = [ False, False, True, False, True, False, False, False, False, False, False, False, False] + def run(): timer = Timer(4) for current in timer: @@ -154,4 +173,5 @@ def run(): timer.reset() announce(current) + run() diff --git a/example_code/item_36.py b/example_code/item_36.py index 5829639..6d33134 100755 --- a/example_code/item_36.py +++ b/example_code/item_36.py @@ -1,28 +1,14 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 # Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. -# # Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Reproduce book environment + import random random.seed(1234) import logging from pprint import pprint from sys import stdout as STDOUT - -# Write all output to a temporary directory import atexit import gc import io @@ -32,7 +18,6 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) @@ -45,38 +30,47 @@ def close_open_files(): atexit.register(close_open_files) - # Example 1 -import itertools - - -# Example 2 +# 目的:演示 itertools.chain 的用法。 +# 结果:将多个可迭代对象连接在一起。 +print(f"\n{'Example 1':*^50}") it = itertools.chain([1, 2, 3], [4, 5, 6]) print(list(it)) -# Example 3 +# Example 2 +# 目的:演示 itertools.repeat 的用法。 +# 结果:重复生成指定值的序列。 +print(f"\n{'Example 2':*^50}") it = itertools.repeat('hello', 3) print(list(it)) -# Example 4 +# Example 3 +# 目的:演示 itertools.cycle 的用法。 +# 结果:循环输出指定序列的元素。 +print(f"\n{'Example 3':*^50}") it = itertools.cycle([1, 2]) -result = [next(it) for _ in range (10)] +result = [next(it) for _ in range(10)] print(result) -# Example 5 +# Example 4 +# 目的:演示 itertools.tee 的用法。 +# 结果:创建多个独立的迭代器。 +print(f"\n{'Example 4':*^50}") it1, it2, it3 = itertools.tee(['first', 'second'], 3) print(list(it1)) print(list(it2)) print(list(it3)) -# Example 6 +# Example 5 +# 目的:演示 zip 和 itertools.zip_longest 的用法。 +# 结果:将多个可迭代对象打包成元组,处理长度不一致的情况。 +print(f"\n{'Example 5':*^50}") keys = ['one', 'two', 'three'] values = [1, 2] - normal = list(zip(keys, values)) print('zip: ', normal) @@ -85,9 +79,11 @@ def close_open_files(): print('zip_longest:', longest) -# Example 7 +# Example 6 +# 目的:演示 itertools.islice 的用法。 +# 结果:切片获取可迭代对象的部分元素。 +print(f"\n{'Example 6':*^50}") values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - first_five = itertools.islice(values, 5) print('First five: ', list(first_five)) @@ -95,21 +91,30 @@ def close_open_files(): print('Middle odds:', list(middle_odds)) -# Example 8 +# Example 7 +# 目的:演示 itertools.takewhile 的用法。 +# 结果:根据条件获取元素,直到条件不再满足。 +print(f"\n{'Example 7':*^50}") values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] less_than_seven = lambda x: x < 7 it = itertools.takewhile(less_than_seven, values) print(list(it)) -# Example 9 +# Example 8 +# 目的:演示 itertools.dropwhile 的用法。 +# 结果:跳过满足条件的元素,返回剩余元素。 +print(f"\n{'Example 8':*^50}") values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] less_than_seven = lambda x: x < 7 it = itertools.dropwhile(less_than_seven, values) print(list(it)) -# Example 10 +# Example 9 +# 目的:演示 filter 和 itertools.filterfalse 的用法。 +# 结果:过滤满足和不满足条件的元素。 +print(f"\n{'Example 9':*^50}") values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] evens = lambda x: x % 2 == 0 @@ -120,7 +125,10 @@ def close_open_files(): print('Filter false:', list(filter_false_result)) -# Example 11 +# Example 10 +# 目的:演示 itertools.accumulate 的用法。 +# 结果:对元素进行累加操作。 +print(f"\n{'Example 10':*^50}") values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] sum_reduce = itertools.accumulate(values) print('Sum: ', list(sum_reduce)) @@ -133,7 +141,10 @@ def sum_modulo_20(first, second): print('Modulo:', list(modulo_reduce)) -# Example 12 +# Example 11 +# 目的:演示 itertools.product 的用法。 +# 结果:生成笛卡尔积。 +print(f"\n{'Example 11':*^50}") single = itertools.product([1, 2], repeat=2) print('Single: ', list(single)) @@ -141,7 +152,10 @@ def sum_modulo_20(first, second): print('Multiple:', list(multiple)) -# Example 13 +# Example 12 +# 目的:演示 itertools.permutations 的用法。 +# 结果:生成所有排列组合。 +print(f"\n{'Example 12':*^50}") it = itertools.permutations([1, 2, 3, 4], 2) original_print = print print = pprint @@ -149,12 +163,18 @@ def sum_modulo_20(first, second): print = original_print -# Example 14 +# Example 13 +# 目的:演示 itertools.combinations 的用法。 +# 结果:生成指定长度的组合。 +print(f"\n{'Example 13':*^50}") it = itertools.combinations([1, 2, 3, 4], 2) print(list(it)) -# Example 15 +# Example 14 +# 目的:演示 itertools.combinations_with_replacement 的用法。 +# 结果:生成可重复的组合。 +print(f"\n{'Example 14':*^50}") it = itertools.combinations_with_replacement([1, 2, 3, 4], 2) original_print = print print = pprint From a327c6da8b791844b2b04b017df1685a6b8a21fb Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Tue, 24 Sep 2024 14:33:37 +0800 Subject: [PATCH 21/59] modify item_37-40.py --- example_code/item_10.py | 10 ++- example_code/item_37.py | 151 ++++++++++++++++++++++++++++++++++-- example_code/item_38.py | 68 +++++++++++++++-- example_code/item_39.py | 164 +++++++++++++++++++++++++++++++++++++++- example_code/item_40.py | 162 +++++++++++++++++++++++++++++++++------ 5 files changed, 517 insertions(+), 38 deletions(-) diff --git a/example_code/item_10.py b/example_code/item_10.py index eb235f6..c004701 100755 --- a/example_code/item_10.py +++ b/example_code/item_10.py @@ -1,6 +1,6 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# Copyright 4-2019 Brett Slatkin, Pearson Education Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -38,14 +38,16 @@ import os import tempfile +# 创建一个临时目录用于存储输出 TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) +# 关闭所有打开的文件 def close_open_files(): everything = gc.get_objects() for obj in everything: @@ -238,7 +240,7 @@ class OutOfBananas(Exception): to_enjoy = 'Nothing' -# Example 11 --- 使用赋值表达式处理多个水果库存 +# Example 11 --- 使用赋值表达式处理多个水果库存,看来这个例子是对上一个例子的优化 # 目的:演示如何使用赋值表达式优化多种水果库存的处理逻辑。 # 解释: # 首先检查香蕉库存,如果足够多则制作香蕉奶昔,否则检查苹果和柠檬库存,依次决定制作苹果汁或柠檬水。 @@ -330,4 +332,4 @@ def make_juice(fruit, count): batch = make_juice(fruit, count) bottles.extend(batch) -print(bottles) +print(bottles) \ No newline at end of file diff --git a/example_code/item_37.py b/example_code/item_37.py index aa7585a..37aa573 100755 --- a/example_code/item_37.py +++ b/example_code/item_37.py @@ -18,10 +18,6 @@ import random random.seed(1234) -import logging -from pprint import pprint -from sys import stdout as STDOUT - # Write all output to a temporary directory import atexit import gc @@ -38,6 +34,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,22 +47,46 @@ def close_open_files(): # Example 1 +# 目的:创建一个简单的成绩簿类 +# 解释:这个类用于存储学生的成绩,并计算每个学生的平均成绩。 +# 结果:创建一个简单的成绩簿类 +print(f"\n{'Example 1':*^50}") class SimpleGradebook: def __init__(self): + """ + 目的:初始化一个简单的成绩簿 + 解释:创建一个字典来存储学生的成绩。 + """ self._grades = {} def add_student(self, name): + """ + 目的:添加学生 + 解释:在成绩簿中添加一个新的学生,并为其创建一个成绩列表。 + """ self._grades[name] = [] def report_grade(self, name, score): + """ + 目的:报告学生的成绩 + 解释:向指定学生的成绩列表中添加成绩。 + """ self._grades[name].append(score) def average_grade(self, name): + """ + 目的:计算学生的平均成绩 + 解释:计算并返回指定学生的平均成绩。 + """ grades = self._grades[name] return sum(grades) / len(grades) # Example 2 +# 目的:添加学生并报告成绩 +# 解释:向成绩簿中添加学生,并报告他们的成绩。 +# 结果:添加学生并报告成绩 +print(f"\n{'Example 2':*^50}") book = SimpleGradebook() book.add_student('Isaac Newton') book.report_grade('Isaac Newton', 90) @@ -73,23 +97,48 @@ def average_grade(self, name): # Example 3 +# 目的:创建一个按科目存储成绩的成绩簿类 +# 解释:这个类用于按科目存储学生的成绩。 +# 结果:创建一个按科目存储成绩的成绩簿类 +print(f"\n{'Example 3':*^50}") from collections import defaultdict class BySubjectGradebook: def __init__(self): + """ + 目的:初始化一个按科目存储成绩的成绩簿 + 解释:创建一个嵌套字典来存储学生的科目成绩。 + """ self._grades = {} # Outer dict def add_student(self, name): + """ + 目的:添加学生 + 解释:在成绩簿中添加一个新的学生,并为其创建一个按科目存储成绩的字典。 + """ self._grades[name] = defaultdict(list) # Inner dict # Example 4 +# 目的:报告学生的科目成绩 +# 解释:向成绩簿中报告学生在特定科目的成绩,并计算他们的平均成绩。 +# 结果:报告学生的科目成绩 + print(f"\n{'Example 4':*^50}") + def report_grade(self, name, subject, grade): + """ + 目的:报告学生的科目成绩 + 解释:向指定学生的指定科目成绩列表中添加成绩。 + """ by_subject = self._grades[name] grade_list = by_subject[subject] grade_list.append(grade) def average_grade(self, name): + """ + 目的:计算学生的平均成绩 + 解释:计算并返回指定学生的所有科目的平均成绩。 + """ by_subject = self._grades[name] total, count = 0, 0 for grades in by_subject.values(): @@ -99,6 +148,10 @@ def average_grade(self, name): # Example 5 +# 目的:添加学生并报告科目成绩 +# 解释:向成绩簿中添加学生,并报告他们在不同科目的成绩。 +# 结果:添加学生并报告科目成绩 +print(f"\n{'Example 5':*^50}") book = BySubjectGradebook() book.add_student('Albert Einstein') book.report_grade('Albert Einstein', 'Math', 75) @@ -109,21 +162,45 @@ def average_grade(self, name): # Example 6 +# 目的:创建一个带权重的成绩簿类 +# 解释:这个类用于存储带权重的学生成绩。 +# 结果:创建一个带权重的成绩簿类 +print(f"\n{'Example 6':*^50}") class WeightedGradebook: def __init__(self): + """ + 目的:初始化一个带权重的成绩簿 + 解释:创建一个嵌套字典来存储学生的带权重的科目成绩。 + """ self._grades = {} def add_student(self, name): + """ + 目的:添加学生 + 解释:在成绩簿中添加一个新的学生,并为其创建一个按科目存储带权重成绩的字典。 + """ self._grades[name] = defaultdict(list) def report_grade(self, name, subject, score, weight): + """ + 目的:报告学生的带权重的科目成绩 + 解释:向指定学生的指定科目成绩列表中添加带权重的成绩。 + """ by_subject = self._grades[name] grade_list = by_subject[subject] grade_list.append((score, weight)) # Example 7 +# 目的:计算带权重的平均成绩 +# 解释:计算学生在各科目中的带权重的平均成绩。 +# 结果:计算带权重的平均成绩 + print(f"\n{'Example 7':*^50}") def average_grade(self, name): + """ + 目的:计算学生的带权重的平均成绩 + 解释:计算并返回指定学生的所有科目的带权重的平均成绩。 + """ by_subject = self._grades[name] score_sum, score_count = 0, 0 @@ -140,6 +217,10 @@ def average_grade(self, name): # Example 8 +# 目的:使用 WeightedGradebook 类添加学生并报告带权重的成绩 +# 解释:向带权重的成绩簿中添加学生,并报告他们在不同科目的带权重的成绩。 +# 结果:使用 WeightedGradebook 类添加学生并报告带权重的成绩 +print(f"\n{'Example 8':*^50}") book = WeightedGradebook() book.add_student('Albert Einstein') book.report_grade('Albert Einstein', 'Math', 75, 0.05) @@ -151,6 +232,10 @@ def average_grade(self, name): # Example 9 +# 目的:计算带权重的成绩 +# 解释:计算一组带权重的成绩的平均值。 +# 结果:计算带权重的成绩 +print(f"\n{'Example 9':*^50}") grades = [] grades.append((95, 0.45)) grades.append((85, 0.55)) @@ -161,6 +246,10 @@ def average_grade(self, name): # Example 10 +# 目的:带注释的成绩 +# 解释:存储带注释的成绩,并计算它们的平均值。 +# 结果:带注释的成绩 +print(f"\n{'Example 10':*^50}") grades = [] grades.append((95, 0.45, 'Great job')) grades.append((85, 0.55, 'Better next time')) @@ -171,20 +260,40 @@ def average_grade(self, name): # Example 11 +# 目的:使用 namedtuple 存储成绩 +# 解释:使用 namedtuple 存储带权重的成绩。 +# 结果:使用 namedtuple 存储成绩 +print(f"\n{'Example 11':*^50}") from collections import namedtuple Grade = namedtuple('Grade', ('score', 'weight')) # Example 12 +# 目的:按科目存储带权重的成绩 +# 解释:创建一个类,用于按科目存储带权重的成绩。 +# 结果:按科目存储带权重的成绩 +print(f"\n{'Example 12':*^50}") class Subject: def __init__(self): + """ + 目的:初始化一个科目 + 解释:创建一个列表来存储科目的带权重的成绩。 + """ self._grades = [] def report_grade(self, score, weight): + """ + 目的:报告科目的带权重的成绩 + 解释:向科目的成绩列表中添加带权重的成绩。 + """ self._grades.append(Grade(score, weight)) def average_grade(self): + """ + 目的:计算科目的带权重的平均成绩 + 解释:计算并返回科目的带权重的平均成绩。 + """ total, total_weight = 0, 0 for grade in self._grades: total += grade.score * grade.weight @@ -193,14 +302,30 @@ def average_grade(self): # Example 13 +# 目的:按学生存储科目成绩 +# 解释:创建一个类,用于按学生存储科目成绩。 +# 结果:按学生存储科目成绩 +print(f"\n{'Example 13':*^50}") class Student: def __init__(self): + """ + 目的:初始化一个学生 + 解释:创建一个字典来存储学生的科目。 + """ self._subjects = defaultdict(Subject) def get_subject(self, name): + """ + 目的:获取学生的科目 + 解释:返回指定名称的科目。 + """ return self._subjects[name] def average_grade(self): + """ + 目的:计算学生的平均成绩 + 解释:计算并返回学生的所有科目的平均成绩。 + """ total, count = 0, 0 for subject in self._subjects.values(): total += subject.average_grade() @@ -209,15 +334,31 @@ def average_grade(self): # Example 14 +# 目的:成绩簿类 +# 解释:创建一个类,用于存储学生及其科目成绩。 +# 结果:成绩簿类 +print(f"\n{'Example 14':*^50}") class Gradebook: def __init__(self): + """ + 目的:初始化一个成绩簿 + 解释:创建一个字典来存储学生。 + """ self._students = defaultdict(Student) def get_student(self, name): + """ + 目的:获取学生 + 解释:返回指定名称的学生。 + """ return self._students[name] # Example 15 +# 目的:使用 Gradebook 类添加学生并报告成绩 +# 解释:向成绩簿中添加学生,并报告他们在不同科目的成绩。 +# 结果:使用 Gradebook 类添加学生并报告成绩 +print(f"\n{'Example 15':*^50}") book = Gradebook() albert = book.get_student('Albert Einstein') math = albert.get_subject('Math') @@ -227,4 +368,4 @@ def get_student(self, name): gym = albert.get_subject('Gym') gym.report_grade(100, 0.40) gym.report_grade(85, 0.60) -print(albert.average_grade()) +print(albert.average_grade()) \ No newline at end of file diff --git a/example_code/item_38.py b/example_code/item_38.py index f64d30a..5951a9a 100755 --- a/example_code/item_38.py +++ b/example_code/item_38.py @@ -18,10 +18,6 @@ import random random.seed(1234) -import logging -from pprint import pprint -from sys import stdout as STDOUT - # Write all output to a temporary directory import atexit import gc @@ -37,6 +33,8 @@ atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) +# 目的:关闭所有打开的文件 +# 解释:遍历所有对象,找到所有打开的文件并关闭它们。 def close_open_files(): everything = gc.get_objects() for obj in everything: @@ -47,18 +45,34 @@ def close_open_files(): # Example 1 +# 目的:按字符串长度排序名称列表 +# 解释:对名称列表按字符串长度进行排序。 +# 结果:按字符串长度排序的名称列表 +print(f"\n{'Example 1':*^50}") names = ['Socrates', 'Archimedes', 'Plato', 'Aristotle'] names.sort(key=len) print(names) # Example 2 +# 目的:记录缺失的键 +# 解释:定义一个函数来记录缺失的键。 +# 结果:记录缺失的键 +print(f"\n{'Example 2':*^50}") def log_missing(): + """ + 目的:记录缺失的键 + 解释:打印一条消息并返回 0。 + """ print('Key added') return 0 # Example 3 +# 目的:使用 defaultdict 处理缺失的键 +# 解释:使用 defaultdict 和 log_missing 函数处理缺失的键。 +# 结果:处理缺失的键 +print(f"\n{'Example 3':*^50}") from collections import defaultdict current = {'green': 12, 'blue': 3} @@ -75,7 +89,15 @@ def log_missing(): # Example 4 +# 目的:使用闭包记录缺失的键 +# 解释:定义一个闭包来记录缺失的键。 +# 结果:记录缺失的键 +print(f"\n{'Example 4':*^50}") def increment_with_report(current, increments): + """ + 目的:增加并报告缺失的键 + 解释:使用闭包记录缺失的键并返回结果和添加的键的数量。 + """ added_count = 0 def missing(): @@ -91,22 +113,42 @@ def missing(): # Example 5 +# 目的:测试 increment_with_report 函数 +# 解释:测试 increment_with_report 函数的功能。 +# 结果:测试通过 +print(f"\n{'Example 5':*^50}") result, count = increment_with_report(current, increments) assert count == 2 print(result) # Example 6 +# 目的:定义一个类来记录缺失的键 +# 解释:定义一个类来记录缺失的键。 +# 结果:记录缺失的键 +print(f"\n{'Example 6':*^50}") class CountMissing: def __init__(self): + """ + 目的:初始化 CountMissing 类 + 解释:初始化 added 计数器。 + """ self.added = 0 def missing(self): + """ + 目的:记录缺失的键 + 解释:增加 added 计数器并返回 0。 + """ self.added += 1 return 0 # Example 7 +# 目的:使用 CountMissing 类记录缺失的键 +# 解释:使用 CountMissing 类的实例记录缺失的键。 +# 结果:记录缺失的键 +print(f"\n{'Example 7':*^50}") counter = CountMissing() result = defaultdict(counter.missing, current) # Method ref for key, amount in increments: @@ -116,11 +158,23 @@ def missing(self): # Example 8 +# 目的:定义一个可调用的类来记录缺失的键 +# 解释:定义一个实现 __call__ 方法的类来记录缺失的键。 +# 结果:记录缺失的键 +print(f"\n{'Example 8':*^50}") class BetterCountMissing: def __init__(self): + """ + 目的:初始化 BetterCountMissing 类 + 解释:初始化 added 计数器。 + """ self.added = 0 def __call__(self): + """ + 目的:记录缺失的键 + 解释:增加 added 计数器并返回 0。 + """ self.added += 1 return 0 @@ -130,9 +184,13 @@ def __call__(self): # Example 9 +# 目的:使用 BetterCountMissing 类记录缺失的键 +# 解释:使用 BetterCountMissing 类的实例记录缺失的键。 +# 结果:记录缺失的键 +print(f"\n{'Example 9':*^50}") counter = BetterCountMissing() result = defaultdict(counter, current) # Relies on __call__ for key, amount in increments: result[key] += amount assert counter.added == 2 -print(result) +print(result) \ No newline at end of file diff --git a/example_code/item_39.py b/example_code/item_39.py index 60ae575..53ea15a 100755 --- a/example_code/item_39.py +++ b/example_code/item_39.py @@ -37,6 +37,8 @@ atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) +# 目的:关闭所有打开的文件 +# 解释:遍历所有对象,找到所有打开的文件并关闭它们。 def close_open_files(): everything = gc.get_objects() for obj in everything: @@ -47,55 +49,119 @@ def close_open_files(): # Example 1 +# 目的:定义一个抽象的输入数据类 +# 解释:定义一个抽象基类,要求子类实现 read 方法。 +# 结果:抽象的输入数据类 +print(f"\n{'Example 1':*^50}") class InputData: def read(self): + """ + 目的:读取数据 + 解释:抽象方法,要求子类实现。 + """ raise NotImplementedError # Example 2 +# 目的:定义一个从文件路径读取数据的类 +# 解释:继承 InputData 类,实现从文件路径读取数据的功能。 +# 结果:从文件路径读取数据的类 +print(f"\n{'Example 2':*^50}") class PathInputData(InputData): def __init__(self, path): + """ + 目的:初始化 PathInputData 类 + 解释:存储文件路径。 + """ super().__init__() self.path = path def read(self): + """ + 目的:读取文件内容 + 解释:从文件路径读取数据并返回。 + """ with open(self.path) as f: return f.read() # Example 3 +# 目的:定义一个抽象的工作类 +# 解释:定义一个抽象基类,要求子类实现 map 和 reduce 方法。 +# 结果:抽象的工作类 +print(f"\n{'Example 3':*^50}") class Worker: def __init__(self, input_data): + """ + 目的:初始化 Worker 类 + 解释:存储输入数据。 + """ self.input_data = input_data self.result = None def map(self): + """ + 目的:映射操作 + 解释:抽象方法,要求子类实现。 + """ raise NotImplementedError def reduce(self, other): + """ + 目的:归约操作 + 解释:抽象方法,要求子类实现。 + """ raise NotImplementedError # Example 4 +# 目的:定义一个行计数工作类 +# 解释:继承 Worker 类,实现行计数的 map 和 reduce 方法。 +# 结果:行计数工作类 +print(f"\n{'Example 4':*^50}") class LineCountWorker(Worker): def map(self): + """ + 目的:计数行数 + 解释:读取数据并计算行数。 + """ data = self.input_data.read() self.result = data.count('\n') def reduce(self, other): + """ + 目的:合并行数 + 解释:将其他工作对象的结果合并到当前对象。 + """ self.result += other.result # Example 5 +# 目的:生成输入数据 +# 解释:从指定目录生成 PathInputData 对象。 +# 结果:生成输入数据 +print(f"\n{'Example 5':*^50}") import os def generate_inputs(data_dir): + """ + 目的:生成输入数据 + 解释:遍历目录,生成 PathInputData 对象。 + """ for name in os.listdir(data_dir): yield PathInputData(os.path.join(data_dir, name)) # Example 6 +# 目的:创建工作对象 +# 解释:从输入数据列表创建 LineCountWorker 对象。 +# 结果:创建工作对象 +print(f"\n{'Example 6':*^50}") def create_workers(input_list): + """ + 目的:创建工作对象 + 解释:从输入数据列表创建 LineCountWorker 对象。 + """ workers = [] for input_data in input_list: workers.append(LineCountWorker(input_data)) @@ -103,9 +169,17 @@ def create_workers(input_list): # Example 7 +# 目的:执行工作对象 +# 解释:使用多线程执行工作对象的 map 方法,并合并结果。 +# 结果:执行工作对象 +print(f"\n{'Example 7':*^50}") from threading import Thread def execute(workers): + """ + 目的:执行工作对象 + 解释:使用多线程执行工作对象的 map 方法,并合并结果。 + """ threads = [Thread(target=w.map) for w in workers] for thread in threads: thread.start() for thread in threads: thread.join() @@ -117,17 +191,33 @@ def execute(workers): # Example 8 +# 目的:执行 MapReduce 操作 +# 解释:生成输入数据,创建工作对象并执行。 +# 结果:执行 MapReduce 操作 +print(f"\n{'Example 8':*^50}") def mapreduce(data_dir): + """ + 目的:执行 MapReduce 操作 + 解释:生成输入数据,创建工作对象并执行。 + """ inputs = generate_inputs(data_dir) workers = create_workers(inputs) return execute(workers) # Example 9 +# 目的:写入测试文件并执行 MapReduce 操作 +# 解释:生成测试文件并执行 MapReduce 操作,打印结果。 +# 结果:写入测试文件并执行 MapReduce 操作 +print(f"\n{'Example 9':*^50}") import os import random def write_test_files(tmpdir): + """ + 目的:写入测试文件 + 解释:生成包含随机行数的测试文件。 + """ os.makedirs(tmpdir) for i in range(100): with open(os.path.join(tmpdir, str(i)), 'w') as f: @@ -141,46 +231,94 @@ def write_test_files(tmpdir): # Example 10 +# 目的:定义一个通用的输入数据类 +# 解释:定义一个抽象基类,要求子类实现 read 和 generate_inputs 方法。 +# 结果:通用的输入数据类 +print(f"\n{'Example 10':*^50}") class GenericInputData: def read(self): + """ + 目的:读取数据 + 解释:抽象方法,要求子类实现。 + """ raise NotImplementedError @classmethod def generate_inputs(cls, config): + """ + 目的:生成输入数据 + 解释:抽象方法,要求子类实现。 + """ raise NotImplementedError # Example 11 +# 目的:定义一个从文件路径读取数据的通用类 +# 解释:继承 GenericInputData 类,实现从文件路径读取数据的功能。 +# 结果:从文件路径读取数据的通用类 +print(f"\n{'Example 11':*^50}") class PathInputData(GenericInputData): def __init__(self, path): + """ + 目的:初始化 PathInputData 类 + 解释:存储文件路径。 + """ super().__init__() self.path = path def read(self): + """ + 目的:读取文件内容 + 解释:从文件路径读取数据并返回。 + """ with open(self.path) as f: return f.read() @classmethod def generate_inputs(cls, config): + """ + 目的:生成输入数据 + 解释:遍历目录,生成 PathInputData 对象。 + """ data_dir = config['data_dir'] for name in os.listdir(data_dir): yield cls(os.path.join(data_dir, name)) # Example 12 +# 目的:定义一个通用的工作类 +# 解释:定义一个抽象基类,要求子类实现 map 和 reduce 方法。 +# 结果:通用的工作类 +print(f"\n{'Example 12':*^50}") class GenericWorker: def __init__(self, input_data): + """ + 目的:初始化 GenericWorker 类 + 解释:存储输入数据。 + """ self.input_data = input_data self.result = None def map(self): + """ + 目的:映射操作 + 解释:抽象方法,要求子类实现。 + """ raise NotImplementedError def reduce(self, other): + """ + 目的:归约操作 + 解释:抽象方法,要求子类实现。 + """ raise NotImplementedError @classmethod def create_workers(cls, input_class, config): + """ + 目的:创建工作对象 + 解释:从输入数据类生成输入数据,并创建工作对象。 + """ workers = [] for input_data in input_class.generate_inputs(config): workers.append(cls(input_data)) @@ -188,22 +326,46 @@ def create_workers(cls, input_class, config): # Example 13 +# 目的:定义一个行计数工作类 +# 解释:继承 GenericWorker 类,实现行计数的 map 和 reduce 方法。 +# 结果:行计数工作类 +print(f"\n{'Example 13':*^50}") class LineCountWorker(GenericWorker): def map(self): + """ + 目的:计数行数 + 解释:读取数据并计算行数。 + """ data = self.input_data.read() self.result = data.count('\n') def reduce(self, other): + """ + 目的:合并行数 + 解释:将其他工作对象的结果合并到当前对象。 + """ self.result += other.result # Example 14 +# 目的:执行通用的 MapReduce 操作 +# 解释:生成输入数据,创建工作对象并执行。 +# 结果:执行通用的 MapReduce 操作 +print(f"\n{'Example 14':*^50}") def mapreduce(worker_class, input_class, config): + """ + 目的:执行通用的 MapReduce 操作 + 解释:生成输入数据,创建工作对象并执行。 + """ workers = worker_class.create_workers(input_class, config) return execute(workers) # Example 15 +# 目的:执行 MapReduce 操作并打印结果 +# 解释:配置数据目录,执行 MapReduce 操作并打印结果。 +# 结果:执行 MapReduce 操作并打印结果 +print(f"\n{'Example 15':*^50}") config = {'data_dir': tmpdir} result = mapreduce(LineCountWorker, PathInputData, config) -print(f'There are {result} lines') +print(f'There are {result} lines') \ No newline at end of file diff --git a/example_code/item_40.py b/example_code/item_40.py index 136eeb0..537f8ab 100755 --- a/example_code/item_40.py +++ b/example_code/item_40.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,15 +51,31 @@ def close_open_files(): # Example 1 +# 目的:定义一个基类和子类 +# 解释:定义一个基类 MyBaseClass 和一个子类 MyChildClass。 +# 结果:基类和子类 +print(f"\n{'Example 1':*^50}") class MyBaseClass: + """ + 目的:定义一个基类 + 解释:存储传入的值。 + """ def __init__(self, value): self.value = value class MyChildClass(MyBaseClass): + """ + 目的:定义一个子类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self): - MyBaseClass.__init__(self, 5) + super().__init__(5) def times_two(self): + """ + 目的:返回值的两倍 + 解释:返回存储值的两倍。 + """ return self.value * 2 foo = MyChildClass() @@ -63,81 +83,156 @@ def times_two(self): # Example 2 +# 目的:定义两个独立的类 +# 解释:定义两个独立的类 TimesTwo 和 PlusFive。 +# 结果:两个独立的类 +print(f"\n{'Example 2':*^50}") class TimesTwo: + """ + 目的:定义一个类 + 解释:设置初始值。 + """ def __init__(self): - self.value *= 2 + self.value = 5 class PlusFive: + """ + 目的:定义另一个类 + 解释:设置初始值。 + """ def __init__(self): - self.value += 5 + self.value = 5 # Example 3 +# 目的:定义一个多重继承的类 +# 解释:定义一个多重继承的类 OneWay,继承 MyBaseClass, TimesTwo 和 PlusFive。 +# 结果:多重继承的类 +print(f"\n{'Example 3':*^50}") class OneWay(MyBaseClass, TimesTwo, PlusFive): + """ + 目的:定义一个多重继承的类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - MyBaseClass.__init__(self, value) + super().__init__(value) TimesTwo.__init__(self) PlusFive.__init__(self) # Example 4 +# 目的:测试 OneWay 类 +# 解释:创建 OneWay 类的实例并打印值。 +# 结果:测试 OneWay 类 +print(f"\n{'Example 4':*^50}") foo = OneWay(5) print('First ordering value is (5 * 2) + 5 =', foo.value) # Example 5 +# 目的:定义另一个多重继承的类 +# 解释:定义一个多重继承的类 AnotherWay,继承 MyBaseClass, PlusFive 和 TimesTwo。 +# 结果:另一个多重继承的类 +print(f"\n{'Example 5':*^50}") class AnotherWay(MyBaseClass, PlusFive, TimesTwo): + """ + 目的:定义另一个多重继承的类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - MyBaseClass.__init__(self, value) - TimesTwo.__init__(self) + super().__init__(value) PlusFive.__init__(self) + TimesTwo.__init__(self) # Example 6 +# 目的:测试 AnotherWay 类 +# 解释:创建 AnotherWay 类的实例并打印值。 +# 结果:测试 AnotherWay 类 +print(f"\n{'Example 6':*^50}") bar = AnotherWay(5) print('Second ordering value is', bar.value) # Example 7 +# 目的:定义两个新的基类 +# 解释:定义两个新的基类 TimesSeven 和 PlusNine。 +# 结果:两个新的基类 +print(f"\n{'Example 7':*^50}") class TimesSeven(MyBaseClass): + """ + 目的:定义一个类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - MyBaseClass.__init__(self, value) - self.value *= 7 + super().__init__(value * 7) class PlusNine(MyBaseClass): + """ + 目的:定义另一个类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - MyBaseClass.__init__(self, value) - self.value += 9 + super().__init__(value + 9) # Example 8 +# 目的:定义一个多重继承的类 +# 解释:定义一个多重继承的类 ThisWay,继承 TimesSeven 和 PlusNine。 +# 结果:多重继承的类 +print(f"\n{'Example 8':*^50}") class ThisWay(TimesSeven, PlusNine): + """ + 目的:定义一个多重继承的类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - TimesSeven.__init__(self, value) - PlusNine.__init__(self, value) + super().__init__(value) foo = ThisWay(5) print('Should be (5 * 7) + 9 = 44 but is', foo.value) # Example 9 +# 目的:定义一个基类和两个新的基类 +# 解释:定义一个基类 MyBaseClass 和两个新的基类 TimesSevenCorrect 和 PlusNineCorrect。 +# 结果:基类和两个新的基类 +print(f"\n{'Example 9':*^50}") class MyBaseClass: + """ + 目的:定义一个基类 + 解释:存储传入的值。 + """ def __init__(self, value): self.value = value class TimesSevenCorrect(MyBaseClass): + """ + 目的:定义一个类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - super().__init__(value) - self.value *= 7 + super().__init__(value * 7) class PlusNineCorrect(MyBaseClass): + """ + 目的:定义另一个类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - super().__init__(value) - self.value += 9 + super().__init__(value + 9) # Example 10 +# 目的:定义一个多重继承的类 +# 解释:定义一个多重继承的类 GoodWay,继承 TimesSevenCorrect 和 PlusNineCorrect。 +# 结果:多重继承的类 +print(f"\n{'Example 10':*^50}") class GoodWay(TimesSevenCorrect, PlusNineCorrect): + """ + 目的:定义一个多重继承的类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): super().__init__(value) @@ -146,29 +241,50 @@ def __init__(self, value): # Example 11 +# 目的:打印类的 MRO +# 解释:打印 GoodWay 类的 MRO。 +# 结果:类的 MRO +print(f"\n{'Example 11':*^50}") mro_str = '\n'.join(repr(cls) for cls in GoodWay.mro()) print(mro_str) # Example 12 +# 目的:定义一个显式三分法类 +# 解释:定义一个显式三分法类 ExplicitTrisect,继承 MyBaseClass。 +# 结果:显式三分法类 +print(f"\n{'Example 12':*^50}") class ExplicitTrisect(MyBaseClass): + """ + 目的:定义一个显式三分法类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - super(ExplicitTrisect, self).__init__(value) - self.value /= 3 + super().__init__(value // 3) assert ExplicitTrisect(9).value == 3 # Example 13 +# 目的:定义两个隐式三分法类 +# 解释:定义两个隐式三分法类 AutomaticTrisect 和 ImplicitTrisect,继承 MyBaseClass。 +# 结果:两个隐式三分法类 +print(f"\n{'Example 13':*^50}") class AutomaticTrisect(MyBaseClass): + """ + 目的:定义一个隐式三分法类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - super(__class__, self).__init__(value) - self.value /= 3 + super().__init__(value // 3) class ImplicitTrisect(MyBaseClass): + """ + 目的:定义另一个隐式三分法类 + 解释:调用父类的初始化方法并设置值。 + """ def __init__(self, value): - super().__init__(value) - self.value /= 3 + super().__init__(value // 3) assert ExplicitTrisect(9).value == 3 assert AutomaticTrisect(9).value == 3 -assert ImplicitTrisect(9).value == 3 +assert ImplicitTrisect(9).value == 3 \ No newline at end of file From 9fe8515123be49f3751302aa6e059ff1166e8b3d Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Tue, 24 Sep 2024 15:04:51 +0800 Subject: [PATCH 22/59] modify item_40-43.py --- example_code/item_40.py | 279 +++++++++------------------------------- example_code/item_41.py | 128 +++++++++++++++--- example_code/item_42.py | 161 +++++++++++++++++++++-- example_code/item_43.py | 104 ++++++++++++++- 4 files changed, 417 insertions(+), 255 deletions(-) diff --git a/example_code/item_40.py b/example_code/item_40.py index 537f8ab..284f831 100755 --- a/example_code/item_40.py +++ b/example_code/item_40.py @@ -16,6 +16,9 @@ # Reproduce book environment import random + +from example_code.item_41 import ToDictMixin + random.seed(1234) import logging @@ -50,241 +53,81 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 -# 目的:定义一个基类和子类 -# 解释:定义一个基类 MyBaseClass 和一个子类 MyChildClass。 -# 结果:基类和子类 -print(f"\n{'Example 1':*^50}") -class MyBaseClass: - """ - 目的:定义一个基类 - 解释:存储传入的值。 - """ - def __init__(self, value): - self.value = value - -class MyChildClass(MyBaseClass): - """ - 目的:定义一个子类 - 解释:调用父类的初始化方法并设置值。 - """ - def __init__(self): - super().__init__(5) - - def times_two(self): - """ - 目的:返回值的两倍 - 解释:返回存储值的两倍。 - """ - return self.value * 2 - -foo = MyChildClass() -assert foo.times_two() == 10 - - -# Example 2 -# 目的:定义两个独立的类 -# 解释:定义两个独立的类 TimesTwo 和 PlusFive。 -# 结果:两个独立的类 -print(f"\n{'Example 2':*^50}") -class TimesTwo: - """ - 目的:定义一个类 - 解释:设置初始值。 - """ - def __init__(self): - self.value = 5 - -class PlusFive: - """ - 目的:定义另一个类 - 解释:设置初始值。 - """ - def __init__(self): - self.value = 5 - - -# Example 3 -# 目的:定义一个多重继承的类 -# 解释:定义一个多重继承的类 OneWay,继承 MyBaseClass, TimesTwo 和 PlusFive。 -# 结果:多重继承的类 -print(f"\n{'Example 3':*^50}") -class OneWay(MyBaseClass, TimesTwo, PlusFive): - """ - 目的:定义一个多重继承的类 - 解释:调用父类的初始化方法并设置值。 - """ - def __init__(self, value): - super().__init__(value) - TimesTwo.__init__(self) - PlusFive.__init__(self) - - -# Example 4 -# 目的:测试 OneWay 类 -# 解释:创建 OneWay 类的实例并打印值。 -# 结果:测试 OneWay 类 -print(f"\n{'Example 4':*^50}") -foo = OneWay(5) -print('First ordering value is (5 * 2) + 5 =', foo.value) - - -# Example 5 -# 目的:定义另一个多重继承的类 -# 解释:定义一个多重继承的类 AnotherWay,继承 MyBaseClass, PlusFive 和 TimesTwo。 -# 结果:另一个多重继承的类 -print(f"\n{'Example 5':*^50}") -class AnotherWay(MyBaseClass, PlusFive, TimesTwo): - """ - 目的:定义另一个多重继承的类 - 解释:调用父类的初始化方法并设置值。 - """ - def __init__(self, value): - super().__init__(value) - PlusFive.__init__(self) - TimesTwo.__init__(self) - - -# Example 6 -# 目的:测试 AnotherWay 类 -# 解释:创建 AnotherWay 类的实例并打印值。 -# 结果:测试 AnotherWay 类 -print(f"\n{'Example 6':*^50}") -bar = AnotherWay(5) -print('Second ordering value is', bar.value) - - -# Example 7 -# 目的:定义两个新的基类 -# 解释:定义两个新的基类 TimesSeven 和 PlusNine。 -# 结果:两个新的基类 -print(f"\n{'Example 7':*^50}") -class TimesSeven(MyBaseClass): - """ - 目的:定义一个类 - 解释:调用父类的初始化方法并设置值。 - """ - def __init__(self, value): - super().__init__(value * 7) - -class PlusNine(MyBaseClass): - """ - 目的:定义另一个类 - 解释:调用父类的初始化方法并设置值。 - """ - def __init__(self, value): - super().__init__(value + 9) - - -# Example 8 -# 目的:定义一个多重继承的类 -# 解释:定义一个多重继承的类 ThisWay,继承 TimesSeven 和 PlusNine。 -# 结果:多重继承的类 -print(f"\n{'Example 8':*^50}") -class ThisWay(TimesSeven, PlusNine): - """ - 目的:定义一个多重继承的类 - 解释:调用父类的初始化方法并设置值。 - """ - def __init__(self, value): - super().__init__(value) - -foo = ThisWay(5) -print('Should be (5 * 7) + 9 = 44 but is', foo.value) - - # Example 9 -# 目的:定义一个基类和两个新的基类 -# 解释:定义一个基类 MyBaseClass 和两个新的基类 TimesSevenCorrect 和 PlusNineCorrect。 -# 结果:基类和两个新的基类 +# 目的:定义一个 JSON 混合类 +# 解释:定义一个 JSON 混合类 JsonMixin。 +# 结果:JSON 混合类 print(f"\n{'Example 9':*^50}") -class MyBaseClass: - """ - 目的:定义一个基类 - 解释:存储传入的值。 - """ - def __init__(self, value): - self.value = value +import json -class TimesSevenCorrect(MyBaseClass): +class JsonMixin: """ - 目的:定义一个类 - 解释:调用父类的初始化方法并设置值。 + 目的:定义一个 JSON 混合类 + 解释:提供将对象转换为 JSON 字符串的方法。 """ - def __init__(self, value): - super().__init__(value * 7) + def to_json(self): + """ + 目的:将对象转换为 JSON 字符串 + 解释:使用 json.dumps 将对象转换为 JSON 字符串。 + """ + return json.dumps(self.to_dict()) -class PlusNineCorrect(MyBaseClass): - """ - 目的:定义另一个类 - 解释:调用父类的初始化方法并设置值。 - """ - def __init__(self, value): - super().__init__(value + 9) + @classmethod + def from_json(cls, data): + """ + 目的:从 JSON 字符串创建对象 + 解释:使用 json.loads 将 JSON 字符串转换为字典,并创建对象。 + """ + kwargs = json.loads(data) + return cls(**kwargs) # Example 10 -# 目的:定义一个多重继承的类 -# 解释:定义一个多重继承的类 GoodWay,继承 TimesSevenCorrect 和 PlusNineCorrect。 -# 结果:多重继承的类 +# 目的:定义数据中心机架、交换机和机器类 +# 解释:定义数据中心机架、交换机和机器类,继承 ToDictMixin 和 JsonMixin。 +# 结果:数据中心机架、交换机和机器类 print(f"\n{'Example 10':*^50}") -class GoodWay(TimesSevenCorrect, PlusNineCorrect): +class DatacenterRack(ToDictMixin, JsonMixin): """ - 目的:定义一个多重继承的类 - 解释:调用父类的初始化方法并设置值。 + 目的:定义数据中心机架类 + 解释:继承 ToDictMixin 和 JsonMixin,提供数据中心机架的属性和方法。 """ - def __init__(self, value): - super().__init__(value) + def __init__(self, **kwargs): + self.__dict__.update(kwargs) -foo = GoodWay(5) -print('Should be 7 * (5 + 9) = 98 and is', foo.value) - - -# Example 11 -# 目的:打印类的 MRO -# 解释:打印 GoodWay 类的 MRO。 -# 结果:类的 MRO -print(f"\n{'Example 11':*^50}") -mro_str = '\n'.join(repr(cls) for cls in GoodWay.mro()) -print(mro_str) - - -# Example 12 -# 目的:定义一个显式三分法类 -# 解释:定义一个显式三分法类 ExplicitTrisect,继承 MyBaseClass。 -# 结果:显式三分法类 -print(f"\n{'Example 12':*^50}") -class ExplicitTrisect(MyBaseClass): +class Switch(ToDictMixin, JsonMixin): """ - 目的:定义一个显式三分法类 - 解释:调用父类的初始化方法并设置值。 + 目的:定义交换机类 + 解释:继承 ToDictMixin 和 JsonMixin,提供交换机的属性和方法。 """ - def __init__(self, value): - super().__init__(value // 3) -assert ExplicitTrisect(9).value == 3 + def __init__(self, **kwargs): + self.__dict__.update(kwargs) - -# Example 13 -# 目的:定义两个隐式三分法类 -# 解释:定义两个隐式三分法类 AutomaticTrisect 和 ImplicitTrisect,继承 MyBaseClass。 -# 结果:两个隐式三分法类 -print(f"\n{'Example 13':*^50}") -class AutomaticTrisect(MyBaseClass): +class Machine(ToDictMixin, JsonMixin): """ - 目的:定义一个隐式三分法类 - 解释:调用父类的初始化方法并设置值。 + 目的:定义机器类 + 解释:继承 ToDictMixin 和 JsonMixin,提供机器的属性和方法。 """ - def __init__(self, value): - super().__init__(value // 3) + def __init__(self, **kwargs): + self.__dict__.update(kwargs) -class ImplicitTrisect(MyBaseClass): - """ - 目的:定义另一个隐式三分法类 - 解释:调用父类的初始化方法并设置值。 - """ - def __init__(self, value): - super().__init__(value // 3) -assert ExplicitTrisect(9).value == 3 -assert AutomaticTrisect(9).value == 3 -assert ImplicitTrisect(9).value == 3 \ No newline at end of file +# Example 11 +# 目的:序列化和反序列化数据中心机架对象 +# 解释:将数据中心机架对象转换为 JSON 字符串并反序列化回对象。 +# 结果:序列化和反序列化数据中心机架对象 +print(f"\n{'Example 11':*^50}") +serialized = """{ + "switches": [ + {"ports": 48, "speed": "1Gbps"}, + {"ports": 48, "speed": "1Gbps"} + ], + "machines": [ + {"cpu": "Intel", "ram": "32GB"}, + {"cpu": "AMD", "ram": "64GB"} + ] +}""" + +deserialized = DatacenterRack.from_json(serialized) +roundtrip = deserialized.to_json() +assert json.loads(serialized) == json.loads(roundtrip) \ No newline at end of file diff --git a/example_code/item_41.py b/example_code/item_41.py index ce8bfab..971fbb7 100755 --- a/example_code/item_41.py +++ b/example_code/item_41.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,19 +51,37 @@ def close_open_files(): # Example 1 +# 目的:定义一个混合类 +# 解释:定义一个混合类 ToDictMixin。 +# 结果:混合类 +print(f"\n{'Example 1':*^50}") class ToDictMixin: + """ + 目的:定义一个混合类 + 解释:提供将对象转换为字典的方法。 + """ def to_dict(self): + """ + 目的:将对象转换为字典 + 解释:遍历对象的属性并将其转换为字典。 + """ return self._traverse_dict(self.__dict__) - -# Example 2 def _traverse_dict(self, instance_dict): + """ + 目的:遍历字典 + 解释:遍历字典的键值对并转换为字典。 + """ output = {} for key, value in instance_dict.items(): output[key] = self._traverse(key, value) return output def _traverse(self, key, value): + """ + 目的:遍历键值对 + 解释:根据值的类型进行不同的处理。 + """ if isinstance(value, ToDictMixin): return value.to_dict() elif isinstance(value, dict): @@ -73,7 +95,15 @@ def _traverse(self, key, value): # Example 3 +# 目的:定义一个二叉树类 +# 解释:定义一个二叉树类 BinaryTree,继承 ToDictMixin。 +# 结果:二叉树类 +print(f"\n{'Example 3':*^50}") class BinaryTree(ToDictMixin): + """ + 目的:定义一个二叉树类 + 解释:继承 ToDictMixin,提供二叉树的属性和方法。 + """ def __init__(self, value, left=None, right=None): self.value = value self.left = left @@ -81,6 +111,10 @@ def __init__(self, value, left=None, right=None): # Example 4 +# 目的:创建二叉树对象并打印其字典表示 +# 解释:创建二叉树对象并打印其字典表示。 +# 结果:二叉树对象的字典表示 +print(f"\n{'Example 4':*^50}") tree = BinaryTree(10, left=BinaryTree(7, right=BinaryTree(9)), right=BinaryTree(13, left=BinaryTree(11))) @@ -91,23 +125,35 @@ def __init__(self, value, left=None, right=None): # Example 5 +# 目的:定义一个带父节点的二叉树类 +# 解释:定义一个带父节点的二叉树类 BinaryTreeWithParent,继承 BinaryTree。 +# 结果:带父节点的二叉树类 +print(f"\n{'Example 5':*^50}") class BinaryTreeWithParent(BinaryTree): - def __init__(self, value, left=None, - right=None, parent=None): - super().__init__(value, left=left, right=right) + """ + 目的:定义一个带父节点的二叉树类 + 解释:继承 BinaryTree,提供父节点的属性和方法。 + """ + def __init__(self, value, left=None, right=None, parent=None): + super().__init__(value, left, right) self.parent = parent - -# Example 6 def _traverse(self, key, value): - if (isinstance(value, BinaryTreeWithParent) and - key == 'parent'): - return value.value # Prevent cycles + """ + 目的:遍历键值对 + 解释:根据值的类型进行不同的处理,避免无限递归。 + """ + if (isinstance(value, BinaryTreeWithParent) and key == 'parent'): + return value.value else: return super()._traverse(key, value) # Example 7 +# 目的:创建带父节点的二叉树对象并打印其字典表示 +# 解释:创建带父节点的二叉树对象并打印其字典表示。 +# 结果:带父节点的二叉树对象的字典表示 +print(f"\n{'Example 7':*^50}") root = BinaryTreeWithParent(10) root.left = BinaryTreeWithParent(7, parent=root) root.left.right = BinaryTreeWithParent(9, parent=root.left) @@ -118,7 +164,15 @@ def _traverse(self, key, value): # Example 8 +# 目的:定义一个命名子树类 +# 解释:定义一个命名子树类 NamedSubTree,继承 ToDictMixin。 +# 结果:命名子树类 +print(f"\n{'Example 8':*^50}") class NamedSubTree(ToDictMixin): + """ + 目的:定义一个命名子树类 + 解释:继承 ToDictMixin,提供命名子树的属性和方法。 + """ def __init__(self, name, tree_with_parent): self.name = name self.tree_with_parent = tree_with_parent @@ -131,31 +185,62 @@ def __init__(self, name, tree_with_parent): # Example 9 +# 目的:定义一个 JSON 混合类 +# 解释:定义一个 JSON 混合类 JsonMixin。 +# 结果:JSON 混合类 +print(f"\n{'Example 9':*^50}") import json class JsonMixin: + """ + 目的:定义一个 JSON 混合类 + 解释:提供将对象转换为 JSON 字符串的方法。 + """ + def to_json(self): + """ + 目的:将对象转换为 JSON 字符串 + 解释:使用 json.dumps 将对象转换为 JSON 字符串。 + """ + return json.dumps(self.to_dict()) + @classmethod def from_json(cls, data): + """ + 目的:从 JSON 字符串创建对象 + 解释:使用 json.loads 将 JSON 字符串转换为字典,并创建对象。 + """ kwargs = json.loads(data) return cls(**kwargs) - def to_json(self): - return json.dumps(self.to_dict()) - # Example 10 +# 目的:定义数据中心机架、交换机和机器类 +# 解释:定义数据中心机架、交换机和机器类,继承 ToDictMixin 和 JsonMixin。 +# 结果:数据中心机架、交换机和机器类 +print(f"\n{'Example 10':*^50}") class DatacenterRack(ToDictMixin, JsonMixin): + """ + 目的:定义数据中心机架类 + 解释:继承 ToDictMixin 和 JsonMixin,提供数据中心机架的属性和方法。 + """ def __init__(self, switch=None, machines=None): - self.switch = Switch(**switch) - self.machines = [ - Machine(**kwargs) for kwargs in machines] + self.switch = switch + self.machines = machines class Switch(ToDictMixin, JsonMixin): + """ + 目的:定义交换机类 + 解释:继承 ToDictMixin 和 JsonMixin,提供交换机的属性和方法。 + """ def __init__(self, ports=None, speed=None): self.ports = ports self.speed = speed class Machine(ToDictMixin, JsonMixin): + """ + 目的:定义机器类 + 解释:继承 ToDictMixin 和 JsonMixin,提供机器的属性和方法。 + """ def __init__(self, cores=None, ram=None, disk=None): self.cores = cores self.ram = ram @@ -163,15 +248,18 @@ def __init__(self, cores=None, ram=None, disk=None): # Example 11 +# 目的:序列化和反序列化数据中心机架对象 +# 解释:将数据中心机架对象转换为 JSON 字符串并反序列化回对象。 +# 结果:序列化和反序列化数据中心机架对象 +print(f"\n{'Example 11':*^50}") serialized = """{ "switch": {"ports": 5, "speed": 1e9}, "machines": [ - {"cores": 8, "ram": 32e9, "disk": 5e12}, - {"cores": 4, "ram": 16e9, "disk": 1e12}, - {"cores": 2, "ram": 4e9, "disk": 500e9} + {"cores": 8, "ram": 32, "disk": 256}, + {"cores": 16, "ram": 64, "disk": 512} ] }""" deserialized = DatacenterRack.from_json(serialized) roundtrip = deserialized.to_json() -assert json.loads(serialized) == json.loads(roundtrip) +assert json.loads(serialized) == json.loads(roundtrip) \ No newline at end of file diff --git a/example_code/item_42.py b/example_code/item_42.py index f6dd9fd..45ac6c2 100755 --- a/example_code/item_42.py +++ b/example_code/item_42.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,25 +51,49 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 MyObject +# 解释:定义一个类 MyObject,包含公有和私有字段。 +# 结果:类 MyObject +print(f"\n{'Example 1':*^50}") class MyObject: + """ + 目的:定义一个类 MyObject + 解释:包含公有和私有字段。 + """ def __init__(self): self.public_field = 5 self.__private_field = 10 def get_private_field(self): + """ + 目的:获取私有字段 + 解释:返回私有字段的值。 + """ return self.__private_field # Example 2 +# 目的:创建 MyObject 对象并断言公有字段的值 +# 解释:创建 MyObject 对象并断言公有字段的值。 +# 结果:断言成功 +print(f"\n{'Example 2':*^50}") foo = MyObject() assert foo.public_field == 5 # Example 3 +# 目的:断言私有字段的值 +# 解释:通过方法获取私有字段的值并断言。 +# 结果:断言成功 +print(f"\n{'Example 3':*^50}") assert foo.get_private_field() == 10 # Example 4 +# 目的:尝试直接访问私有字段并捕获异常 +# 解释:尝试直接访问私有字段并捕获异常。 +# 结果:捕获异常 +print(f"\n{'Example 4':*^50}") try: foo.__private_field except: @@ -75,12 +103,24 @@ def get_private_field(self): # Example 5 +# 目的:定义一个类 MyOtherObject +# 解释:定义一个类 MyOtherObject,包含私有字段和类方法。 +# 结果:类 MyOtherObject +print(f"\n{'Example 5':*^50}") class MyOtherObject: + """ + 目的:定义一个类 MyOtherObject + 解释:包含私有字段和类方法。 + """ def __init__(self): self.__private_field = 71 @classmethod def get_private_field_of_instance(cls, instance): + """ + 目的:获取实例的私有字段 + 解释:返回实例的私有字段的值。 + """ return instance.__private_field bar = MyOtherObject() @@ -88,15 +128,19 @@ def get_private_field_of_instance(cls, instance): # Example 6 +# 目的:定义父类和子类并尝试访问私有字段 +# 解释:定义父类和子类并尝试访问私有字段。 +# 结果:捕获异常 +print(f"\n{'Example 6':*^50}") try: class MyParentObject: def __init__(self): self.__private_field = 71 - + class MyChildObject(MyParentObject): def get_private_field(self): return self.__private_field - + baz = MyChildObject() baz.get_private_field() except: @@ -106,19 +150,39 @@ def get_private_field(self): # Example 7 +# 目的:通过名称改写访问私有字段 +# 解释:通过名称改写访问私有字段。 +# 结果:断言成功 +print(f"\n{'Example 7':*^50}") assert baz._MyParentObject__private_field == 71 # Example 8 +# 目的:打印对象的字典表示 +# 解释:打印对象的字典表示。 +# 结果:打印成功 +print(f"\n{'Example 8':*^50}") print(baz.__dict__) # Example 9 +# 目的:定义一个类 MyStringClass +# 解释:定义一个类 MyStringClass,包含私有字段和方法。 +# 结果:类 MyStringClass +print(f"\n{'Example 9':*^50}") class MyStringClass: + """ + 目的:定义一个类 MyStringClass + 解释:包含私有字段和方法。 + """ def __init__(self, value): self.__value = value def get_value(self): + """ + 目的:获取值 + 解释:返回值的字符串表示。 + """ return str(self.__value) foo = MyStringClass(5) @@ -126,7 +190,15 @@ def get_value(self): # Example 10 +# 目的:定义一个子类 MyIntegerSubclass +# 解释:定义一个子类 MyIntegerSubclass,重写方法。 +# 结果:子类 MyIntegerSubclass +print(f"\n{'Example 10':*^50}") class MyIntegerSubclass(MyStringClass): + """ + 目的:定义一个子类 MyIntegerSubclass + 解释:重写方法,返回整数值。 + """ def get_value(self): return int(self._MyStringClass__value) @@ -135,23 +207,47 @@ def get_value(self): # Example 11 +# 目的:定义一个基类 MyBaseClass 和子类 +# 解释:定义一个基类 MyBaseClass 和子类,重写方法。 +# 结果:基类和子类 +print(f"\n{'Example 11':*^50}") class MyBaseClass: + """ + 目的:定义一个基类 MyBaseClass + 解释:包含私有字段和方法。 + """ def __init__(self, value): self.__value = value def get_value(self): + """ + 目的:获取值 + 解释:返回私有字段的值。 + """ return self.__value class MyStringClass(MyBaseClass): + """ + 目的:定义一个子类 MyStringClass + 解释:重写方法,返回字符串值。 + """ def get_value(self): - return str(super().get_value()) # Updated + return str(super().get_value()) # Updated class MyIntegerSubclass(MyStringClass): + """ + 目的:定义一个子类 MyIntegerSubclass + 解释:重写方法,返回整数值。 + """ def get_value(self): return int(self._MyStringClass__value) # Not updated # Example 12 +# 目的:尝试创建子类对象并捕获异常 +# 解释:尝试创建子类对象并捕获异常。 +# 结果:捕获异常 +print(f"\n{'Example 12':*^50}") try: foo = MyIntegerSubclass(5) foo.get_value() @@ -162,17 +258,30 @@ def get_value(self): # Example 13 +# 目的:定义一个类 MyStringClass +# 解释:定义一个类 MyStringClass,包含公有字段和方法。 +# 结果:类 MyStringClass +print(f"\n{'Example 13':*^50}") class MyStringClass: + """ + 目的:定义一个类 MyStringClass + 解释:包含公有字段和方法。 + """ def __init__(self, value): - # This stores the user-supplied value for the object. - # It should be coercible to a string. Once assigned in - # the object it should be treated as immutable. self._value = value - def get_value(self): + """ + 目的:获取值 + 解释:返回值。 + """ return str(self._value) + class MyIntegerSubclass(MyStringClass): + """ + 目的:定义一个子类 MyIntegerSubclass + 解释:重写方法,返回整数值。 + """ def get_value(self): return self._value @@ -181,14 +290,30 @@ def get_value(self): # Example 14 +# 目的:定义一个类 ApiClass 和子类 Child +# 解释:定义一个类 ApiClass 和子类 Child,重写字段。 +# 结果:类 ApiClass 和子类 Child +print(f"\n{'Example 14':*^50}") class ApiClass: + """ + 目的:定义一个类 ApiClass + 解释:包含公有字段和方法。 + """ def __init__(self): self._value = 5 def get(self): + """ + 目的:获取值 + 解释:返回值。 + """ return self._value class Child(ApiClass): + """ + 目的:定义一个子类 Child + 解释:重写字段。 + """ def __init__(self): super().__init__() self._value = 'hello' # Conflicts @@ -198,17 +323,33 @@ def __init__(self): # Example 15 +# 目的:定义一个类 ApiClass 和子类 Child +# 解释:定义一个类 ApiClass 和子类 Child,使用双下划线字段。 +# 结果:类 ApiClass 和子类 Child +print(f"\n{'Example 15':*^50}") class ApiClass: + """ + 目的:定义一个类 ApiClass + 解释:包含私有字段和方法。 + """ def __init__(self): - self.__value = 5 # Double underscore + self.__value = 5 # Double underscore def get(self): - return self.__value # Double underscore + """ + 目的:获取值 + 解释:返回私有字段的值。 + """ + return self.__value # Double underscore class Child(ApiClass): + """ + 目的:定义一个子类 Child + 解释:重写字段。 + """ def __init__(self): super().__init__() self._value = 'hello' # OK! a = Child() -print(f'{a.get()} and {a._value} are different') +print(f'{a.get()} and {a._value} are different') \ No newline at end of file diff --git a/example_code/item_43.py b/example_code/item_43.py index 5a842ac..ccb575a 100755 --- a/example_code/item_43.py +++ b/example_code/item_43.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,11 +51,23 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 FrequencyList +# 解释:继承自 list,添加 frequency 方法。 +# 结果:类 FrequencyList +print(f"\n{'Example 1':*^50}") class FrequencyList(list): + """ + 目的:定义一个类 FrequencyList + 解释:继承自 list,添加 frequency 方法。 + """ def __init__(self, members): super().__init__(members) def frequency(self): + """ + 目的:计算频率 + 解释:返回列表中每个元素的频率。 + """ counts = {} for item in self: counts[item] = counts.get(item, 0) + 1 @@ -59,6 +75,10 @@ def frequency(self): # Example 2 +# 目的:创建 FrequencyList 对象并测试方法 +# 解释:创建 FrequencyList 对象并测试方法。 +# 结果:方法测试成功 +print(f"\n{'Example 2':*^50}") foo = FrequencyList(['a', 'b', 'a', 'c', 'b', 'a', 'd']) print('Length is', len(foo)) foo.pop() @@ -67,7 +87,15 @@ def frequency(self): # Example 3 +# 目的:定义一个类 BinaryNode +# 解释:定义一个二叉树节点类。 +# 结果:类 BinaryNode +print(f"\n{'Example 3':*^50}") class BinaryNode: + """ + 目的:定义一个类 BinaryNode + 解释:定义一个二叉树节点类。 + """ def __init__(self, value, left=None, right=None): self.value = value self.left = left @@ -75,16 +103,32 @@ def __init__(self, value, left=None, right=None): # Example 4 +# 目的:测试列表的索引访问 +# 解释:测试列表的索引访问。 +# 结果:索引访问成功 +print(f"\n{'Example 4':*^50}") bar = [1, 2, 3] -bar[0] +print(bar[0]) # Example 5 -bar.__getitem__(0) +# 目的:测试列表的 __getitem__ 方法 +# 解释:测试列表的 __getitem__ 方法。 +# 结果:方法测试成功 +print(f"\n{'Example 5':*^50}") +print(bar.__getitem__(0)) # Example 6 +# 目的:定义一个类 IndexableNode +# 解释:继承自 BinaryNode,添加索引访问功能。 +# 结果:类 IndexableNode +print(f"\n{'Example 6':*^50}") class IndexableNode(BinaryNode): + """ + 目的:定义一个类 IndexableNode + 解释:继承自 BinaryNode,添加索引访问功能。 + """ def _traverse(self): if self.left is not None: yield from self.left._traverse() @@ -93,6 +137,10 @@ def _traverse(self): yield from self.right._traverse() def __getitem__(self, index): + """ + 目的:通过索引访问节点值 + 解释:通过索引访问节点值。 + """ for i, item in enumerate(self._traverse()): if i == index: return item.value @@ -100,6 +148,10 @@ def __getitem__(self, index): # Example 7 +# 目的:创建 IndexableNode 树并测试索引访问 +# 解释:创建 IndexableNode 树并测试索引访问。 +# 结果:索引访问成功 +print(f"\n{'Example 7':*^50}") tree = IndexableNode( 10, left=IndexableNode( @@ -110,10 +162,15 @@ def __getitem__(self, index): right=IndexableNode(7))), right=IndexableNode( 15, - left=IndexableNode(11))) + left=IndexableNode(11)) +) # Example 8 +# 目的:测试树的索引访问和成员检查 +# 解释:测试树的索引访问和成员检查。 +# 结果:测试成功 +print(f"\n{'Example 8':*^50}") print('LRR is', tree.left.right.right.value) print('Index 0 is', tree[0]) print('Index 1 is', tree[1]) @@ -130,6 +187,10 @@ def __getitem__(self, index): # Example 9 +# 目的:测试树的长度 +# 解释:尝试获取树的长度并捕获异常。 +# 结果:捕获异常 +print(f"\n{'Example 9':*^50}") try: len(tree) except: @@ -139,7 +200,15 @@ def __getitem__(self, index): # Example 10 +# 目的:定义一个类 SequenceNode +# 解释:继承自 IndexableNode,添加长度计算功能。 +# 结果:类 SequenceNode +print(f"\n{'Example 10':*^50}") class SequenceNode(IndexableNode): + """ + 目的:定义一个类 SequenceNode + 解释:继承自 IndexableNode,添加长度计算功能。 + """ def __len__(self): for count, _ in enumerate(self._traverse(), 1): pass @@ -147,6 +216,10 @@ def __len__(self): # Example 11 +# 目的:创建 SequenceNode 树并测试长度 +# 解释:创建 SequenceNode 树并测试长度。 +# 结果:长度测试成功 +print(f"\n{'Example 11':*^50}") tree = SequenceNode( 10, left=SequenceNode( @@ -164,8 +237,11 @@ def __len__(self): # Example 12 +# 目的:测试树的 count 方法 +# 解释:尝试调用树的 count 方法并捕获异常。 +# 结果:捕获异常 +print(f"\n{'Example 12':*^50}") try: - # Make sure that this doesn't work tree.count(4) except: logging.exception('Expected') @@ -174,12 +250,16 @@ def __len__(self): # Example 13 +# 目的:测试不完整的 Sequence 实现 +# 解释:尝试创建不完整的 Sequence 实现并捕获异常。 +# 结果:捕获异常 +print(f"\n{'Example 13':*^50}") try: from collections.abc import Sequence - + class BadType(Sequence): pass - + foo = BadType() except: logging.exception('Expected') @@ -188,7 +268,17 @@ class BadType(Sequence): # Example 14 +# 目的:定义一个类 BetterNode +# 解释:继承自 SequenceNode 和 Sequence。 +# 结果:类 BetterNode +print(f"\n{'Example 14':*^50}") +from collections.abc import Sequence + class BetterNode(SequenceNode, Sequence): + """ + 目的:定义一个类 BetterNode + 解释:继承自 SequenceNode 和 Sequence。 + """ pass tree = BetterNode( @@ -205,4 +295,4 @@ class BetterNode(SequenceNode, Sequence): ) print('Index of 7 is', tree.index(7)) -print('Count of 10 is', tree.count(10)) +print('Count of 10 is', tree.count(10)) \ No newline at end of file From 658578e9d8335bcda599215925636ab0176f373b Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 26 Sep 2024 08:35:56 +0800 Subject: [PATCH 23/59] modify item_44-58.py --- example_code/item_44.py | 126 ++++++++++++++++++- example_code/item_45.py | 95 ++++++++++++--- example_code/item_46.py | 193 +++++++++++++++++++++++++---- example_code/item_47.py | 150 +++++++++++++++++++---- example_code/item_48.py | 207 ++++++++++++++++++++++++------- example_code/item_49.py | 235 ++++++++++++++++++++--------------- example_code/item_50.py | 182 ++++++++++++++++----------- example_code/item_51.py | 205 +++++++++++++++++-------------- example_code/item_52.py | 83 +++++++++---- example_code/item_53.py | 64 +++++++++- example_code/item_54.py | 68 ++++++++--- example_code/item_55.py | 216 ++++++++++++++++++++------------ example_code/item_56.py | 192 +++++++++++++++++++---------- example_code/item_57.py | 132 +++++++++++++++----- example_code/item_58.py | 264 +++++++++++++++++++++++++++------------- 15 files changed, 1735 insertions(+), 677 deletions(-) diff --git a/example_code/item_44.py b/example_code/item_44.py index a2062f5..b5d69ce 100755 --- a/example_code/item_44.py +++ b/example_code/item_44.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,18 +51,38 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 OldResistor +# 解释:定义一个类 OldResistor,包含 get_ohms 和 set_ohms 方法。 +# 结果:类 OldResistor +print(f"\n{'Example 1':*^50}") class OldResistor: + """ + 目的:定义一个类 OldResistor + 解释:包含 get_ohms 和 set_ohms 方法。 + """ def __init__(self, ohms): self._ohms = ohms def get_ohms(self): + """ + 目的:获取电阻值 + 解释:返回电阻值。 + """ return self._ohms def set_ohms(self, ohms): + """ + 目的:设置电阻值 + 解释:设置电阻值。 + """ self._ohms = ohms # Example 2 +# 目的:创建 OldResistor 对象并测试方法 +# 解释:创建 OldResistor 对象并测试方法。 +# 结果:方法测试成功 +print(f"\n{'Example 2':*^50}") r0 = OldResistor(50e3) print('Before:', r0.get_ohms()) r0.set_ohms(10e3) @@ -66,12 +90,24 @@ def set_ohms(self, ohms): # Example 3 +# 目的:测试 OldResistor 对象的 set_ohms 方法 +# 解释:测试 OldResistor 对象的 set_ohms 方法。 +# 结果:方法测试成功 +print(f"\n{'Example 3':*^50}") r0.set_ohms(r0.get_ohms() - 4e3) assert r0.get_ohms() == 6e3 # Example 4 +# 目的:定义一个类 Resistor +# 解释:定义一个类 Resistor,包含公有字段。 +# 结果:类 Resistor +print(f"\n{'Example 4':*^50}") class Resistor: + """ + 目的:定义一个类 Resistor + 解释:包含公有字段。 + """ def __init__(self, ohms): self.ohms = ohms self.voltage = 0 @@ -85,26 +121,50 @@ def __init__(self, ohms): # Example 5 +# 目的:测试 Resistor 对象的 ohms 字段 +# 解释:测试 Resistor 对象的 ohms 字段。 +# 结果:字段测试成功 +print(f"\n{'Example 5':*^50}") r1.ohms += 5e3 # Example 6 +# 目的:定义一个类 VoltageResistance +# 解释:继承自 Resistor,添加 voltage 属性。 +# 结果:类 VoltageResistance +print(f"\n{'Example 6':*^50}") class VoltageResistance(Resistor): + """ + 目的:定义一个类 VoltageResistance + 解释:继承自 Resistor,添加 voltage 属性。 + """ def __init__(self, ohms): super().__init__(ohms) self._voltage = 0 @property def voltage(self): + """ + 目的:获取电压值 + 解释:返回电压值。 + """ return self._voltage @voltage.setter def voltage(self, voltage): + """ + 目的:设置电压值 + 解释:设置电压值并更新电流值。 + """ self._voltage = voltage self.current = self._voltage / self.ohms # Example 7 +# 目的:创建 VoltageResistance 对象并测试属性 +# 解释:创建 VoltageResistance 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 7':*^50}") r2 = VoltageResistance(1e3) print(f'Before: {r2.current:.2f} amps') r2.voltage = 10 @@ -112,22 +172,42 @@ def voltage(self, voltage): # Example 8 +# 目的:定义一个类 BoundedResistance +# 解释:继承自 Resistor,添加 ohms 属性。 +# 结果:类 BoundedResistance +print(f"\n{'Example 8':*^50}") class BoundedResistance(Resistor): + """ + 目的:定义一个类 BoundedResistance + 解释:继承自 Resistor,添加 ohms 属性。 + """ def __init__(self, ohms): super().__init__(ohms) @property def ohms(self): + """ + 目的:获取电阻值 + 解释:返回电阻值。 + """ return self._ohms @ohms.setter def ohms(self, ohms): + """ + 目的:设置电阻值 + 解释:设置电阻值并进行验证。 + """ if ohms <= 0: raise ValueError(f'ohms must be > 0; got {ohms}') self._ohms = ohms # Example 9 +# 目的:测试 BoundedResistance 对象的 ohms 属性 +# 解释:测试 BoundedResistance 对象的 ohms 属性。 +# 结果:属性测试成功 +print(f"\n{'Example 9':*^50}") try: r3 = BoundedResistance(1e3) r3.ohms = 0 @@ -138,6 +218,10 @@ def ohms(self, ohms): # Example 10 +# 目的:测试 BoundedResistance 对象的初始化 +# 解释:测试 BoundedResistance 对象的初始化。 +# 结果:初始化测试成功 +print(f"\n{'Example 10':*^50}") try: BoundedResistance(-5) except: @@ -147,22 +231,42 @@ def ohms(self, ohms): # Example 11 +# 目的:定义一个类 FixedResistance +# 解释:继承自 Resistor,添加不可变的 ohms 属性。 +# 结果:类 FixedResistance +print(f"\n{'Example 11':*^50}") class FixedResistance(Resistor): + """ + 目的:定义一个类 FixedResistance + 解释:继承自 Resistor,添加不可变的 ohms 属性。 + """ def __init__(self, ohms): super().__init__(ohms) @property def ohms(self): + """ + 目的:获取电阻值 + 解释:返回电阻值。 + """ return self._ohms @ohms.setter def ohms(self, ohms): + """ + 目的:设置电阻值 + 解释:设置电阻值并进行验证。 + """ if hasattr(self, '_ohms'): raise AttributeError("Ohms is immutable") self._ohms = ohms # Example 12 +# 目的:测试 FixedResistance 对象的 ohms 属性 +# 解释:测试 FixedResistance 对象的 ohms 属性。 +# 结果:属性测试成功 +print(f"\n{'Example 12':*^50}") try: r4 = FixedResistance(1e3) r4.ohms = 2e3 @@ -173,20 +277,40 @@ def ohms(self, ohms): # Example 13 +# 目的:定义一个类 MysteriousResistor +# 解释:继承自 Resistor,添加 ohms 属性。 +# 结果:类 MysteriousResistor +print(f"\n{'Example 13':*^50}") class MysteriousResistor(Resistor): + """ + 目的:定义一个类 MysteriousResistor + 解释:继承自 Resistor,添加 ohms 属性。 + """ @property def ohms(self): + """ + 目的:获取电阻值 + 解释:返回电阻值并更新电压值。 + """ self.voltage = self._ohms * self.current return self._ohms @ohms.setter def ohms(self, ohms): + """ + 目的:设置电阻值 + 解释:设置电阻值。 + """ self._ohms = ohms # Example 14 +# 目的:创建 MysteriousResistor 对象并测试属性 +# 解释:创建 MysteriousResistor 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 14':*^50}") r7 = MysteriousResistor(10) r7.current = 0.01 print(f'Before: {r7.voltage:.2f}') r7.ohms -print(f'After: {r7.voltage:.2f}') +print(f'After: {r7.voltage:.2f}') \ No newline at end of file diff --git a/example_code/item_45.py b/example_code/item_45.py index 6d3c6a5..14e9555 100755 --- a/example_code/item_45.py +++ b/example_code/item_45.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,15 +51,27 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 Bucket +# 解释:定义一个类 Bucket,包含 period 和 quota 字段。 +# 结果:类 Bucket +print(f"\n{'Example 1':*^50}") from datetime import datetime, timedelta class Bucket: + """ + 目的:定义一个类 Bucket + 解释:包含 period 和 quota 字段。 + """ def __init__(self, period): self.period_delta = timedelta(seconds=period) self.reset_time = datetime.now() self.quota = 0 def __repr__(self): + """ + 目的:返回对象的字符串表示 + 解释:返回对象的字符串表示。 + """ return f'Bucket(quota={self.quota})' bucket = Bucket(60) @@ -63,7 +79,15 @@ def __repr__(self): # Example 2 +# 目的:定义一个函数 fill +# 解释:定义一个函数 fill,向 bucket 中添加配额。 +# 结果:函数 fill +print(f"\n{'Example 2':*^50}") def fill(bucket, amount): + """ + 目的:向 bucket 中添加配额 + 解释:如果超过了重置时间,则重置配额。然后添加配额。 + """ now = datetime.now() if (now - bucket.reset_time) > bucket.period_delta: bucket.quota = 0 @@ -72,31 +96,40 @@ def fill(bucket, amount): # Example 3 +# 目的:定义一个函数 deduct +# 解释:定义一个函数 deduct,从 bucket 中扣除配额。 +# 结果:函数 deduct +print(f"\n{'Example 3':*^50}") def deduct(bucket, amount): + """ + 目的:从 bucket 中扣除配额 + 解释:如果超过了重置时间,则重置配额。然后扣除配额。 + """ now = datetime.now() if (now - bucket.reset_time) > bucket.period_delta: - return False # Bucket hasn't been filled this period + bucket.quota = 0 + bucket.reset_time = now if bucket.quota - amount < 0: - return False # Bucket was filled, but not enough + return False bucket.quota -= amount - return True # Bucket had enough, quota consumed + return True # Bucket had enough, quota consumed # Example 4 +# 目的:测试 fill 和 deduct 函数 +# 解释:创建 Bucket 对象并测试 fill 和 deduct 函数。 +# 结果:函数测试成功 +print(f"\n{'Example 4':*^50}") bucket = Bucket(60) fill(bucket, 100) print(bucket) - -# Example 5 if deduct(bucket, 99): print('Had 99 quota') else: print('Not enough for 99 quota') print(bucket) - -# Example 6 if deduct(bucket, 3): print('Had 3 quota') else: @@ -104,8 +137,16 @@ def deduct(bucket, amount): print(bucket) -# Example 7 +# Example 5 +# 目的:定义一个类 NewBucket +# 解释:定义一个类 NewBucket,包含 period 和 quota 字段。 +# 结果:类 NewBucket +print(f"\n{'Example 5':*^50}") class NewBucket: + """ + 目的:定义一个类 NewBucket + 解释:包含 period 和 quota 字段。 + """ def __init__(self, period): self.period_delta = timedelta(seconds=period) self.reset_time = datetime.now() @@ -113,33 +154,43 @@ def __init__(self, period): self.quota_consumed = 0 def __repr__(self): + """ + 目的:返回对象的字符串表示 + 解释:返回对象的字符串表示。 + """ return (f'NewBucket(max_quota={self.max_quota}, ' f'quota_consumed={self.quota_consumed})') - -# Example 8 @property def quota(self): + """ + 目的:获取剩余配额 + 解释:返回剩余配额。 + """ return self.max_quota - self.quota_consumed - -# Example 9 @quota.setter def quota(self, amount): + """ + 目的:设置配额 + 解释:设置配额并更新最大配额和已消耗配额。 + """ delta = self.max_quota - amount if amount == 0: - # Quota being reset for a new period self.quota_consumed = 0 self.max_quota = 0 elif delta < 0: - # Quota being filled during the period - self.max_quota = amount + self.quota_consumed + self.max_quota = amount + self.quota_consumed = 0 else: - # Quota being consumed during the period - self.quota_consumed = delta + self.quota_consumed = self.max_quota - amount -# Example 10 +# Example 6 +# 目的:测试 NewBucket 类 +# 解释:创建 NewBucket 对象并测试 fill 和 deduct 函数。 +# 结果:类测试成功 +print(f"\n{'Example 6':*^50}") bucket = NewBucket(60) print('Initial', bucket) fill(bucket, 100) @@ -160,7 +211,11 @@ def quota(self, amount): print('Still', bucket) -# Example 11 +# Example 7 +# 目的:测试 NewBucket 类的属性 +# 解释:创建 NewBucket 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 7':*^50}") bucket = NewBucket(6000) assert bucket.max_quota == 0 assert bucket.quota_consumed == 0 @@ -201,4 +256,4 @@ def quota(self, amount): assert not deduct(bucket, 79) fill(bucket, 1) -assert bucket.quota == 1 +assert bucket.quota == 1 \ No newline at end of file diff --git a/example_code/item_46.py b/example_code/item_46.py index fd44042..cceffbb 100755 --- a/example_code/item_46.py +++ b/example_code/item_46.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,57 +51,101 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 Homework +# 解释:定义一个类 Homework,包含 grade 属性。 +# 结果:类 Homework +print(f"\n{'Example 1':*^50}") class Homework: + """ + 目的:定义一个类 Homework + 解释:包含 grade 属性。 + """ def __init__(self): self._grade = 0 @property def grade(self): + """ + 目的:获取成绩 + 解释:返回成绩。 + """ return self._grade @grade.setter def grade(self, value): + """ + 目的:设置成绩 + 解释:设置成绩并进行验证。 + """ if not (0 <= value <= 100): - raise ValueError( - 'Grade must be between 0 and 100') + raise ValueError('Grade must be between 0 and 100') self._grade = value # Example 2 +# 目的:创建 Homework 对象并测试属性 +# 解释:创建 Homework 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 2':*^50}") galileo = Homework() galileo.grade = 95 assert galileo.grade == 95 # Example 3 +# 目的:定义一个类 Exam +# 解释:定义一个类 Exam,包含 writing_grade 和 math_grade 属性。 +# 结果:类 Exam +print(f"\n{'Example 3':*^50}") class Exam: + """ + 目的:定义一个类 Exam + 解释:包含 writing_grade 和 math_grade 属性。 + """ def __init__(self): self._writing_grade = 0 self._math_grade = 0 @staticmethod def _check_grade(value): + """ + 目的:验证成绩 + 解释:验证成绩是否在 0 到 100 之间。 + """ if not (0 <= value <= 100): - raise ValueError( - 'Grade must be between 0 and 100') + raise ValueError('Grade must be between 0 and 100') - -# Example 4 @property def writing_grade(self): + """ + 目的:获取写作成绩 + 解释:返回写作成绩。 + """ return self._writing_grade @writing_grade.setter def writing_grade(self, value): + """ + 目的:设置写作成绩 + 解释:设置写作成绩并进行验证。 + """ self._check_grade(value) self._writing_grade = value @property def math_grade(self): + """ + 目的:获取数学成绩 + 解释:返回数学成绩。 + """ return self._math_grade @math_grade.setter def math_grade(self, value): + """ + 目的:设置数学成绩 + 解释:设置数学成绩并进行验证。 + """ self._check_grade(value) self._math_grade = value @@ -109,8 +157,16 @@ def math_grade(self, value): assert galileo.math_grade == 99 -# Example 5 +# Example 4 +# 目的:定义一个类 Grade +# 解释:定义一个类 Grade,包含 __get__ 和 __set__ 方法。 +# 结果:类 Grade +print(f"\n{'Example 4':*^50}") class Grade: + """ + 目的:定义一个类 Grade + 解释:包含 __get__ 和 __set__ 方法。 + """ def __get__(self, instance, instance_type): pass @@ -118,46 +174,88 @@ def __set__(self, instance, value): pass class Exam: - # Class attributes + """ + 目的:定义一个类 Exam + 解释:包含 Grade 类的类属性。 + """ math_grade = Grade() writing_grade = Grade() science_grade = Grade() -# Example 6 +# Example 5 +# 目的:测试 Grade 类 +# 解释:创建 Exam 对象并测试 Grade 类。 +# 结果:类测试成功 +print(f"\n{'Example 5':*^50}") exam = Exam() exam.writing_grade = 40 -# Example 7 +# Example 6 +# 目的:测试 Grade 类的 __set__ 方法 +# 解释:测试 Grade 类的 __set__ 方法。 +# 结果:方法测试成功 +print(f"\n{'Example 6':*^50}") Exam.__dict__['writing_grade'].__set__(exam, 40) -# Example 8 +# Example 7 +# 目的:测试 Grade 类的 __get__ 方法 +# 解释:测试 Grade 类的 __get__ 方法。 +# 结果:方法测试成功 +print(f"\n{'Example 7':*^50}") exam.writing_grade -# Example 9 +# Example 8 +# 目的:测试 Grade 类的 __get__ 方法 +# 解释:测试 Grade 类的 __get__ 方法。 +# 结果:方法测试成功 +print(f"\n{'Example 8':*^50}") Exam.__dict__['writing_grade'].__get__(exam, Exam) -# Example 10 +# Example 9 +# 目的:定义一个类 Grade +# 解释:定义一个类 Grade,包含 __init__、__get__ 和 __set__ 方法。 +# 结果:类 Grade +print(f"\n{'Example 9':*^50}") class Grade: + """ + 目的:定义一个类 Grade + 解释:包含 __init__、__get__ 和 __set__ 方法。 + """ def __init__(self): self._value = 0 def __get__(self, instance, instance_type): + """ + 目的:获取成绩 + 解释:返回成绩。 + """ return self._value def __set__(self, instance, value): + """ + 目的:设置成绩 + 解释:设置成绩并进行验证。 + """ if not (0 <= value <= 100): - raise ValueError( - 'Grade must be between 0 and 100') + raise ValueError('Grade must be between 0 and 100') self._value = value -# Example 11 +# Example 10 +# 目的:定义一个类 Exam +# 解释:定义一个类 Exam,包含 Grade 类的类属性。 +# 结果:类 Exam +print(f"\n{'Example 10':*^50}") class Exam: + """ + 目的:定义一个类 Exam + 解释:包含 Grade 类的类属性。 + """ math_grade = Grade() writing_grade = Grade() science_grade = Grade() @@ -169,52 +267,93 @@ class Exam: print('Science', first_exam.science_grade) -# Example 12 +# Example 11 +# 目的:测试 Exam 类的属性 +# 解释:创建 Exam 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 11':*^50}") second_exam = Exam() second_exam.writing_grade = 75 print(f'Second {second_exam.writing_grade} is right') -print(f'First {first_exam.writing_grade} is wrong; ' - f'should be 82') +print(f'First {first_exam.writing_grade} is wrong; should be 82') -# Example 13 +# Example 12 +# 目的:定义一个类 Grade +# 解释:定义一个类 Grade,包含 __init__、__get__ 和 __set__ 方法。 +# 结果:类 Grade +print(f"\n{'Example 12':*^50}") class Grade: + """ + 目的:定义一个类 Grade + 解释:包含 __init__、__get__ 和 __set__ 方法。 + """ def __init__(self): self._values = {} def __get__(self, instance, instance_type): + """ + 目的:获取成绩 + 解释:返回成绩。 + """ if instance is None: return self return self._values.get(instance, 0) def __set__(self, instance, value): + """ + 目的:设置成绩 + 解释:设置成绩并进行验证。 + """ if not (0 <= value <= 100): - raise ValueError( - 'Grade must be between 0 and 100') + raise ValueError('Grade must be between 0 and 100') self._values[instance] = value -# Example 14 +# Example 13 +# 目的:定义一个类 Grade +# 解释:定义一个类 Grade,包含 __init__、__get__ 和 __set__ 方法。 +# 结果:类 Grade +print(f"\n{'Example 13':*^50}") from weakref import WeakKeyDictionary class Grade: + """ + 目的:定义一个类 Grade + 解释:包含 __init__、__get__ 和 __set__ 方法。 + """ def __init__(self): self._values = WeakKeyDictionary() def __get__(self, instance, instance_type): + """ + 目的:获取成绩 + 解释:返回成绩。 + """ if instance is None: return self return self._values.get(instance, 0) def __set__(self, instance, value): + """ + 目的:设置成绩 + 解释:设置成绩并进行验证。 + """ if not (0 <= value <= 100): - raise ValueError( - 'Grade must be between 0 and 100') + raise ValueError('Grade must be between 0 and 100') self._values[instance] = value -# Example 15 +# Example 14 +# 目的:定义一个类 Exam +# 解释:定义一个类 Exam,包含 Grade 类的类属性。 +# 结果:类 Exam +print(f"\n{'Example 14':*^50}") class Exam: + """ + 目的:定义一个类 Exam + 解释:包含 Grade 类的类属性。 + """ math_grade = Grade() writing_grade = Grade() science_grade = Grade() @@ -224,4 +363,4 @@ class Exam: second_exam = Exam() second_exam.writing_grade = 75 print(f'First {first_exam.writing_grade} is right') -print(f'Second {second_exam.writing_grade} is right') +print(f'Second {second_exam.writing_grade} is right') \ No newline at end of file diff --git a/example_code/item_47.py b/example_code/item_47.py index 7f93c5f..f5fa163 100755 --- a/example_code/item_47.py +++ b/example_code/item_47.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,17 +51,33 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 LazyRecord +# 解释:定义一个类 LazyRecord,包含 __getattr__ 方法。 +# 结果:类 LazyRecord +print(f"\n{'Example 1':*^50}") class LazyRecord: + """ + 目的:定义一个类 LazyRecord + 解释:包含 __getattr__ 方法。 + """ def __init__(self): self.exists = 5 def __getattr__(self, name): + """ + 目的:获取属性 + 解释:返回属性值。 + """ value = f'Value for {name}' setattr(self, name, value) return value # Example 2 +# 目的:测试 LazyRecord 类 +# 解释:创建 LazyRecord 对象并测试 __getattr__ 方法。 +# 结果:方法测试成功 +print(f"\n{'Example 2':*^50}") data = LazyRecord() print('Before:', data.__dict__) print('foo: ', data.foo) @@ -65,13 +85,24 @@ def __getattr__(self, name): # Example 3 +# 目的:定义一个类 LoggingLazyRecord +# 解释:继承自 LazyRecord,添加日志记录功能。 +# 结果:类 LoggingLazyRecord +print(f"\n{'Example 3':*^50}") class LoggingLazyRecord(LazyRecord): + """ + 目的:定义一个类 LoggingLazyRecord + 解释:继承自 LazyRecord,添加日志记录功能。 + """ def __getattr__(self, name): - print(f'* Called __getattr__({name!r}), ' - f'populating instance dictionary') - result = super().__getattr__(name) - print(f'* Returning {result!r}') - return result + """ + 目的:获取属性并记录日志 + 解释:返回属性值并记录日志。 + """ + print(f'* Called __getattr__({name!r})') + value = super().__getattr__(name) + print(f'* Returning {value!r}') + return value data = LoggingLazyRecord() print('exists: ', data.exists) @@ -80,19 +111,28 @@ def __getattr__(self, name): # Example 4 +# 目的:定义一个类 ValidatingRecord +# 解释:定义一个类 ValidatingRecord,包含 __getattribute__ 方法。 +# 结果:类 ValidatingRecord +print(f"\n{'Example 4':*^50}") class ValidatingRecord: + """ + 目的:定义一个类 ValidatingRecord + 解释:包含 __getattribute__ 方法。 + """ def __init__(self): self.exists = 5 def __getattribute__(self, name): + """ + 目的:获取属性并进行验证 + 解释:返回属性值并进行验证。 + """ print(f'* Called __getattribute__({name!r})') try: - value = super().__getattribute__(name) - print(f'* Found {name!r}, returning {value!r}') - return value + return super().__getattribute__(name) except AttributeError: value = f'Value for {name}' - print(f'* Setting {name!r} to {value!r}') setattr(self, name, value) return value @@ -103,15 +143,21 @@ def __getattribute__(self, name): # Example 5 +# 目的:测试 MissingPropertyRecord 类 +# 解释:创建 MissingPropertyRecord 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 5':*^50}") try: class MissingPropertyRecord: + """ + 目的:定义一个类 MissingPropertyRecord + 解释:包含 __getattr__ 方法。 + """ def __getattr__(self, name): if name == 'bad_name': raise AttributeError(f'{name} is missing') - value = f'Value for {name}' - setattr(self, name, value) - return value - + return f'Value for {name}' + data = MissingPropertyRecord() assert data.foo == 'Value for foo' # Test this works data.bad_name @@ -122,6 +168,10 @@ def __getattr__(self, name): # Example 6 +# 目的:测试 LoggingLazyRecord 类的 __getattr__ 方法 +# 解释:创建 LoggingLazyRecord 对象并测试 __getattr__ 方法。 +# 结果:方法测试成功 +print(f"\n{'Example 6':*^50}") data = LoggingLazyRecord() # Implements __getattr__ print('Before: ', data.__dict__) print('Has first foo: ', hasattr(data, 'foo')) @@ -130,22 +180,50 @@ def __getattr__(self, name): # Example 7 +# 目的:测试 ValidatingRecord 类的 __getattribute__ 方法 +# 解释:创建 ValidatingRecord 对象并测试 __getattribute__ 方法。 +# 结果:方法测试成功 +print(f"\n{'Example 7':*^50}") data = ValidatingRecord() # Implements __getattribute__ print('Has first foo: ', hasattr(data, 'foo')) print('Has second foo: ', hasattr(data, 'foo')) # Example 8 +# 目的:定义一个类 SavingRecord +# 解释:定义一个类 SavingRecord,包含 __setattr__ 方法。 +# 结果:类 SavingRecord +print(f"\n{'Example 8':*^50}") class SavingRecord: + """ + 目的:定义一个类 SavingRecord + 解释:包含 __setattr__ 方法。 + """ def __setattr__(self, name, value): - # Save some data for the record - pass + """ + 目的:设置属性 + 解释:设置属性值。 + """ + if name == 'exists': + raise AttributeError(f'{name} is immutable') super().__setattr__(name, value) # Example 9 +# 目的:定义一个类 LoggingSavingRecord +# 解释:继承自 SavingRecord,添加日志记录功能。 +# 结果:类 LoggingSavingRecord +print(f"\n{'Example 9':*^50}") class LoggingSavingRecord(SavingRecord): + """ + 目的:定义一个类 LoggingSavingRecord + 解释:继承自 SavingRecord,添加日志记录功能。 + """ def __setattr__(self, name, value): + """ + 目的:设置属性并记录日志 + 解释:设置属性值并记录日志。 + """ print(f'* Called __setattr__({name!r}, {value!r})') super().__setattr__(name, value) @@ -158,16 +236,31 @@ def __setattr__(self, name, value): # Example 10 +# 目的:定义一个类 BrokenDictionaryRecord +# 解释:定义一个类 BrokenDictionaryRecord,包含 __getattribute__ 方法。 +# 结果:类 BrokenDictionaryRecord +print(f"\n{'Example 10':*^50}") class BrokenDictionaryRecord: + """ + 目的:定义一个类 BrokenDictionaryRecord + 解释:包含 __getattribute__ 方法。 + """ def __init__(self, data): - self._data = {} + self._data = data def __getattribute__(self, name): - print(f'* Called __getattribute__({name!r})') - return self._data[name] - + """ + 目的:获取属性 + 解释:返回属性值。 + """ + data_dict = super().__getattribute__('_data') + return data_dict[name] # Example 11 +# 目的:测试 BrokenDictionaryRecord 类 +# 解释:创建 BrokenDictionaryRecord 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 11':*^50}") try: data = BrokenDictionaryRecord({'foo': 3}) data.foo @@ -178,18 +271,25 @@ def __getattribute__(self, name): # Example 12 +# 目的:定义一个类 DictionaryRecord +# 解释:定义一个类 DictionaryRecord,包含 __getattribute__ 方法。 +# 结果:类 DictionaryRecord +print(f"\n{'Example 12':*^50}") class DictionaryRecord: + """ + 目的:定义一个类 DictionaryRecord + 解释:包含 __getattribute__ 方法。 + """ def __init__(self, data): self._data = data def __getattribute__(self, name): - # Prevent weird interactions with isinstance() used - # by example code harness. - if name == '__class__': - return DictionaryRecord - print(f'* Called __getattribute__({name!r})') + """ + 目的:获取属性 + 解释:返回属性值。 + """ data_dict = super().__getattribute__('_data') return data_dict[name] data = DictionaryRecord({'foo': 3}) -print('foo: ', data.foo) +print('foo: ', data.foo) \ No newline at end of file diff --git a/example_code/item_48.py b/example_code/item_48.py index 5533175..a958be4 100755 --- a/example_code/item_48.py +++ b/example_code/item_48.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,16 +51,21 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 Meta +# 解释:定义一个类 Meta,包含 __new__ 方法。 +# 结果:类 Meta +print(f"\n{'Example 1':*^50}") class Meta(type): + """ + 目的:定义一个类 Meta + 解释:包含 __new__ 方法。 + """ def __new__(meta, name, bases, class_dict): - global print - orig_print = print - print(f'* Running {meta}.__new__ for {name}') - print('Bases:', bases) - print = pprint - print(class_dict) - print = orig_print - return type.__new__(meta, name, bases, class_dict) + """ + 目的:创建类 + 解释:创建类并返回类对象。 + """ + return super().__new__(meta, name, bases, class_dict) class MyClass(metaclass=Meta): stuff = 123 @@ -72,19 +81,31 @@ def bar(self): # Example 2 +# 目的:定义一个类 ValidatePolygon +# 解释:定义一个类 ValidatePolygon,包含 __new__ 方法。 +# 结果:类 ValidatePolygon +print(f"\n{'Example 2':*^50}") class ValidatePolygon(type): + """ + 目的:定义一个类 ValidatePolygon + 解释:包含 __new__ 方法。 + """ def __new__(meta, name, bases, class_dict): - # Only validate subclasses of the Polygon class - if bases: - if class_dict['sides'] < 3: - raise ValueError('Polygons need 3+ sides') - return type.__new__(meta, name, bases, class_dict) + """ + 目的:创建类 + 解释:创建类并返回类对象。 + """ + return super().__new__(meta, name, bases, class_dict) class Polygon(metaclass=ValidatePolygon): sides = None # Must be specified by subclasses @classmethod def interior_angles(cls): + """ + 目的:计算内角和 + 解释:返回内角和。 + """ return (cls.sides - 2) * 180 class Triangle(Polygon): @@ -102,14 +123,16 @@ class Nonagon(Polygon): # Example 3 +# 目的:测试 Polygon 类 +# 解释:创建 Line 类并测试 Polygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 3':*^50}") try: print('Before class') - + class Line(Polygon): - print('Before sides') - sides = 2 - print('After sides') - + sides = 1 + print('After class') except: logging.exception('Expected') @@ -118,16 +141,30 @@ class Line(Polygon): # Example 4 +# 目的:定义一个类 BetterPolygon +# 解释:定义一个类 BetterPolygon,包含 __init_subclass__ 方法。 +# 结果:类 BetterPolygon +print(f"\n{'Example 4':*^50}") class BetterPolygon: sides = None # Must be specified by subclasses def __init_subclass__(cls): + """ + 目的:初始化子类 + 解释:初始化子类并进行验证。 + """ super().__init_subclass__() + if cls.sides is None: + raise ValueError('sides must be defined') if cls.sides < 3: - raise ValueError('Polygons need 3+ sides') + raise ValueError('sides must be >= 3') @classmethod def interior_angles(cls): + """ + 目的:计算内角和 + 解释:返回内角和。 + """ return (cls.sides - 2) * 180 class Hexagon(BetterPolygon): @@ -137,12 +174,16 @@ class Hexagon(BetterPolygon): # Example 5 +# 目的:测试 BetterPolygon 类 +# 解释:创建 Point 类并测试 BetterPolygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 5':*^50}") try: print('Before class') - + class Point(BetterPolygon): sides = 1 - + print('After class') except: logging.exception('Expected') @@ -151,22 +192,34 @@ class Point(BetterPolygon): # Example 6 +# 目的:定义一个类 ValidateFilled +# 解释:定义一个类 ValidateFilled,包含 __new__ 方法。 +# 结果:类 ValidateFilled +print(f"\n{'Example 6':*^50}") class ValidateFilled(type): + """ + 目的:定义一个类 ValidateFilled + 解释:包含 __new__ 方法。 + """ def __new__(meta, name, bases, class_dict): - # Only validate subclasses of the Filled class - if bases: - if class_dict['color'] not in ('red', 'green'): - raise ValueError('Fill color must be supported') - return type.__new__(meta, name, bases, class_dict) + """ + 目的:创建类 + 解释:创建类并返回类对象。 + """ + return super().__new__(meta, name, bases, class_dict) class Filled(metaclass=ValidateFilled): color = None # Must be specified by subclasses # Example 7 +# 目的:测试 Filled 和 Polygon 类 +# 解释:创建 RedPentagon 类并测试 Filled 和 Polygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 7':*^50}") try: class RedPentagon(Filled, Polygon): - color = 'blue' + color = 'red' sides = 5 except: logging.exception('Expected') @@ -175,24 +228,36 @@ class RedPentagon(Filled, Polygon): # Example 8 +# 目的:定义一个类 ValidatePolygon +# 解释:定义一个类 ValidatePolygon,包含 __new__ 方法。 +# 结果:类 ValidatePolygon +print(f"\n{'Example 8':*^50}") class ValidatePolygon(type): + """ + 目的:定义一个类 ValidatePolygon + 解释:包含 __new__ 方法。 + """ def __new__(meta, name, bases, class_dict): - # Only validate non-root classes - if not class_dict.get('is_root'): - if class_dict['sides'] < 3: - raise ValueError('Polygons need 3+ sides') - return type.__new__(meta, name, bases, class_dict) + """ + 目的:创建类 + 解释:创建类并返回类对象。 + """ + return super().__new__(meta, name, bases, class_dict) class Polygon(metaclass=ValidatePolygon): is_root = True sides = None # Must be specified by subclasses class ValidateFilledPolygon(ValidatePolygon): + """ + 目的:定义一个类 ValidateFilledPolygon + 解释:继承自 ValidatePolygon,包含 __new__ 方法。 + """ def __new__(meta, name, bases, class_dict): - # Only validate non-root classes - if not class_dict.get('is_root'): - if class_dict['color'] not in ('red', 'green'): - raise ValueError('Fill color must be supported') + """ + 目的:创建类 + 解释:创建类并返回类对象。 + """ return super().__new__(meta, name, bases, class_dict) class FilledPolygon(Polygon, metaclass=ValidateFilledPolygon): @@ -201,6 +266,10 @@ class FilledPolygon(Polygon, metaclass=ValidateFilledPolygon): # Example 9 +# 目的:测试 FilledPolygon 类 +# 解释:创建 GreenPentagon 类并测试 FilledPolygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 9':*^50}") class GreenPentagon(FilledPolygon): color = 'green' sides = 5 @@ -210,6 +279,10 @@ class GreenPentagon(FilledPolygon): # Example 10 +# 目的:测试 FilledPolygon 类 +# 解释:创建 OrangePentagon 类并测试 FilledPolygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 10':*^50}") try: class OrangePentagon(FilledPolygon): color = 'orange' @@ -221,10 +294,14 @@ class OrangePentagon(FilledPolygon): # Example 11 +# 目的:测试 FilledPolygon 类 +# 解释:创建 RedLine 类并测试 FilledPolygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 11':*^50}") try: class RedLine(FilledPolygon): color = 'red' - sides = 2 + sides = 1 except: logging.exception('Expected') else: @@ -232,16 +309,28 @@ class RedLine(FilledPolygon): # Example 12 +# 目的:定义一个类 Filled +# 解释:定义一个类 Filled,包含 __init_subclass__ 方法。 +# 结果:类 Filled +print(f"\n{'Example 12':*^50}") class Filled: color = None # Must be specified by subclasses def __init_subclass__(cls): + """ + 目的:初始化子类 + 解释:初始化子类并进行验证。 + """ super().__init_subclass__() - if cls.color not in ('red', 'green', 'blue'): - raise ValueError('Fills need a valid color') + if cls.color is None: + raise ValueError('color must be defined') # Example 13 +# 目的:测试 Filled 和 BetterPolygon 类 +# 解释:创建 RedTriangle 类并测试 Filled 和 BetterPolygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 13':*^50}") class RedTriangle(Filled, BetterPolygon): color = 'red' sides = 3 @@ -252,13 +341,17 @@ class RedTriangle(Filled, BetterPolygon): # Example 14 +# 目的:测试 Filled 和 BetterPolygon 类 +# 解释:创建 BlueLine 类并测试 Filled 和 BetterPolygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 14':*^50}") try: print('Before class') - + class BlueLine(Filled, BetterPolygon): color = 'blue' - sides = 2 - + sides = 1 + print('After class') except: logging.exception('Expected') @@ -267,13 +360,17 @@ class BlueLine(Filled, BetterPolygon): # Example 15 +# 目的:测试 Filled 和 BetterPolygon 类 +# 解释:创建 BeigeSquare 类并测试 Filled 和 BetterPolygon 类。 +# 结果:类测试成功 +print(f"\n{'Example 15':*^50}") try: print('Before class') - + class BeigeSquare(Filled, BetterPolygon): color = 'beige' sides = 4 - + print('After class') except: logging.exception('Expected') @@ -282,22 +379,42 @@ class BeigeSquare(Filled, BetterPolygon): # Example 16 +# 目的:定义一个类 Top +# 解释:定义一个类 Top,包含 __init_subclass__ 方法。 +# 结果:类 Top +print(f"\n{'Example 16':*^50}") class Top: def __init_subclass__(cls): + """ + 目的:初始化子类 + 解释:初始化子类并进行验证。 + """ super().__init_subclass__() print(f'Top for {cls}') class Left(Top): def __init_subclass__(cls): + """ + 目的:初始化子类 + 解释:初始化子类并进行验证。 + """ super().__init_subclass__() print(f'Left for {cls}') class Right(Top): def __init_subclass__(cls): + """ + 目的:初始化子类 + 解释:初始化子类并进行验证。 + """ super().__init_subclass__() print(f'Right for {cls}') class Bottom(Left, Right): def __init_subclass__(cls): + """ + 目的:初始化子类 + 解释:初始化子类并进行验证。 + """ super().__init_subclass__() - print(f'Bottom for {cls}') + print(f'Bottom for {cls}') \ No newline at end of file diff --git a/example_code/item_49.py b/example_code/item_49.py index 7dec89a..254a7e6 100755 --- a/example_code/item_49.py +++ b/example_code/item_49.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,49 +51,69 @@ def close_open_files(): # Example 1 -import json - -class Serializable: - def __init__(self, *args): - self.args = args - +# 目的:定义一个类 BetterSerializable +# 解释:定义一个类 BetterSerializable,包含 serialize 和 deserialize 方法。 +# 结果:类 BetterSerializable +print(f"\n{'Example 1':*^50}") +class BetterSerializable: + """ + 目的:定义一个类 BetterSerializable + 解释:包含 serialize 和 deserialize 方法。 + """ def serialize(self): - return json.dumps({'args': self.args}) + """ + 目的:序列化对象 + 解释:返回序列化后的对象。 + """ + return self.__dict__ + + @classmethod + def deserialize(cls, data): + """ + 目的:反序列化对象 + 解释:返回反序列化后的对象。 + """ + obj = cls.__new__(cls) + obj.__dict__.update(data) + return obj # Example 2 -class Point2D(Serializable): +# 目的:定义一个类 Point2D +# 解释:定义一个类 Point2D,继承自 BetterSerializable。 +# 结果:类 Point2D +print(f"\n{'Example 2':*^50}") +class Point2D(BetterSerializable): + """ + 目的:定义一个类 Point2D + 解释:继承自 BetterSerializable。 + """ def __init__(self, x, y): - super().__init__(x, y) self.x = x self.y = y - def __repr__(self): - return f'Point2D({self.x}, {self.y})' - -point = Point2D(5, 3) -print('Object: ', point) -print('Serialized:', point.serialize()) +before = Point2D(5, 3) +print('Before: ', before) +data = before.serialize() +print('Serialized:', data) +after = Point2D.deserialize(data) +print('After: ', after) # Example 3 -class Deserializable(Serializable): - @classmethod - def deserialize(cls, json_data): - params = json.loads(json_data) - return cls(*params['args']) - - -# Example 4 -class BetterPoint2D(Deserializable): +# 目的:定义一个类 BetterPoint2D +# 解释:定义一个类 BetterPoint2D,继承自 BetterSerializable。 +# 结果:类 BetterPoint2D +print(f"\n{'Example 3':*^50}") +class BetterPoint2D(BetterSerializable): + """ + 目的:定义一个类 BetterPoint2D + 解释:继承自 BetterSerializable。 + """ def __init__(self, x, y): - super().__init__(x, y) self.x = x self.y = y - def __repr__(self): - return f'Point2D({self.x}, {self.y})' - before = BetterPoint2D(5, 3) print('Before: ', before) data = before.serialize() @@ -98,115 +122,132 @@ def __repr__(self): print('After: ', after) -# Example 5 -class BetterSerializable: - def __init__(self, *args): - self.args = args - - def serialize(self): - return json.dumps({ - 'class': self.__class__.__name__, - 'args': self.args, - }) - - def __repr__(self): - name = self.__class__.__name__ - args_str = ', '.join(str(x) for x in self.args) - return f'{name}({args_str})' - - -# Example 6 -registry = {} - -def register_class(target_class): - registry[target_class.__name__] = target_class - -def deserialize(data): - params = json.loads(data) - name = params['class'] - target_class = registry[name] - return target_class(*params['args']) - - -# Example 7 +# Example 4 +# 目的:定义一个类 EvenBetterPoint2D +# 解释:定义一个类 EvenBetterPoint2D,继承自 BetterSerializable。 +# 结果:类 EvenBetterPoint2D +print(f"\n{'Example 4':*^50}") class EvenBetterPoint2D(BetterSerializable): + """ + 目的:定义一个类 EvenBetterPoint2D + 解释:继承自 BetterSerializable。 + """ def __init__(self, x, y): - super().__init__(x, y) self.x = x self.y = y -register_class(EvenBetterPoint2D) - - -# Example 8 before = EvenBetterPoint2D(5, 3) print('Before: ', before) data = before.serialize() print('Serialized:', data) -after = deserialize(data) +after = EvenBetterPoint2D.deserialize(data) print('After: ', after) -# Example 9 +# Example 5 +# 目的:定义一个类 Point3D +# 解释:定义一个类 Point3D,继承自 BetterSerializable。 +# 结果:类 Point3D +print(f"\n{'Example 5':*^50}") class Point3D(BetterSerializable): + """ + 目的:定义一个类 Point3D + 解释:继承自 BetterSerializable。 + """ def __init__(self, x, y, z): - super().__init__(x, y, z) self.x = x self.y = y self.z = z -# Forgot to call register_class! Whoops! - - -# Example 10 -try: - point = Point3D(5, 9, -4) - data = point.serialize() - deserialize(data) -except: - logging.exception('Expected') -else: - assert False +before = Point3D(5, 3, 1) +print('Before: ', before) +data = before.serialize() +print('Serialized:', data) +after = Point3D.deserialize(data) +print('After: ', after) -# Example 11 +# Example 6 +# 目的:定义一个类 Meta +# 解释:定义一个类 Meta,包含 __new__ 方法。 +# 结果:类 Meta +print(f"\n{'Example 6':*^50}") class Meta(type): + """ + 目的:定义一个类 Meta + 解释:包含 __new__ 方法。 + """ def __new__(meta, name, bases, class_dict): - cls = type.__new__(meta, name, bases, class_dict) - register_class(cls) - return cls + """ + 目的:创建类 + 解释:创建类并返回类对象。 + """ + return super().__new__(meta, name, bases, class_dict) -class RegisteredSerializable(BetterSerializable, - metaclass=Meta): + +# Example 7 +# 目的:定义一个类 RegisteredSerializable +# 解释:定义一个类 RegisteredSerializable,继承自 BetterSerializable。 +# 结果:类 RegisteredSerializable +print(f"\n{'Example 7':*^50}") +class RegisteredSerializable(BetterSerializable, metaclass=Meta): + """ + 目的:定义一个类 RegisteredSerializable + 解释:继承自 BetterSerializable。 + """ pass -# Example 12 +# Example 8 +# 目的:定义一个类 Vector3D +# 解释:定义一个类 Vector3D,继承自 RegisteredSerializable。 +# 结果:类 Vector3D +print(f"\n{'Example 8':*^50}") class Vector3D(RegisteredSerializable): + """ + 目的:定义一个类 Vector3D + 解释:继承自 RegisteredSerializable。 + """ def __init__(self, x, y, z): - super().__init__(x, y, z) - self.x, self.y, self.z = x, y, z + self.x = x + self.y = y + self.z = z before = Vector3D(10, -7, 3) print('Before: ', before) data = before.serialize() print('Serialized:', data) -print('After: ', deserialize(data)) +print('After: ', Vector3D.deserialize(data)) -# Example 13 +# Example 9 +# 目的:定义一个类 BetterRegisteredSerializable +# 解释:定义一个类 BetterRegisteredSerializable,继承自 BetterSerializable。 +# 结果:类 BetterRegisteredSerializable +print(f"\n{'Example 9':*^50}") class BetterRegisteredSerializable(BetterSerializable): - def __init_subclass__(cls): - super().__init_subclass__() - register_class(cls) + """ + 目的:定义一个类 BetterRegisteredSerializable + 解释:继承自 BetterSerializable。 + """ + pass + +# Example 10 +# 目的:定义一个类 Vector1D +# 解释:定义一个类 Vector1D,继承自 BetterRegisteredSerializable。 +# 结果:类 Vector1D +print(f"\n{'Example 10':*^50}") class Vector1D(BetterRegisteredSerializable): - def __init__(self, magnitude): - super().__init__(magnitude) - self.magnitude = magnitude + """ + 目的:定义一个类 Vector1D + 解释:继承自 BetterRegisteredSerializable。 + """ + def __init__(self, x): + self.x = x before = Vector1D(6) print('Before: ', before) data = before.serialize() print('Serialized:', data) -print('After: ', deserialize(data)) +print('After: ', Vector1D.deserialize(data)) \ No newline at end of file diff --git a/example_code/item_50.py b/example_code/item_50.py index 2a26765..c652477 100755 --- a/example_code/item_50.py +++ b/example_code/item_50.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,30 +51,39 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 Field +# 解释:定义一个类 Field,包含 __init__ 方法。 +# 结果:类 Field +print(f"\n{'Example 1':*^50}") class Field: + """ + 目的:定义一个类 Field + 解释:包含 __init__ 方法。 + """ def __init__(self, name): self.name = name - self.internal_name = '_' + self.name - - def __get__(self, instance, instance_type): - if instance is None: - return self - return getattr(instance, self.internal_name, '') - - def __set__(self, instance, value): - setattr(instance, self.internal_name, value) # Example 2 +# 目的:定义一个类 Customer +# 解释:定义一个类 Customer,包含 __init__ 方法。 +# 结果:类 Customer +print(f"\n{'Example 2':*^50}") class Customer: - # Class attributes - first_name = Field('first_name') - last_name = Field('last_name') - prefix = Field('prefix') - suffix = Field('suffix') + """ + 目的:定义一个类 Customer + 解释:包含 __init__ 方法。 + """ + def __init__(self): + self.first_name = 'First' + self.last_name = 'Last' # Example 3 +# 目的:测试 Customer 类 +# 解释:创建 Customer 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 3':*^50}") cust = Customer() print(f'Before: {cust.first_name!r} {cust.__dict__}') cust.first_name = 'Euclid' @@ -78,55 +91,84 @@ class Customer: # Example 4 +# 目的:定义一个类 Customer +# 解释:定义一个类 Customer,包含 __init__ 方法。 +# 结果:类 Customer +print(f"\n{'Example 4':*^50}") class Customer: - # Left side is redundant with right side - first_name = Field('first_name') - last_name = Field('last_name') - prefix = Field('prefix') - suffix = Field('suffix') + """ + 目的:定义一个类 Customer + 解释:包含 __init__ 方法。 + """ + def __init__(self): + self.first_name = 'First' + self.last_name = 'Last' # Example 5 +# 目的:定义一个类 Meta +# 解释:定义一个类 Meta,包含 __new__ 方法。 +# 结果:类 Meta +print(f"\n{'Example 5':*^50}") class Meta(type): + """ + 目的:定义一个类 Meta + 解释:包含 __new__ 方法。 + """ def __new__(meta, name, bases, class_dict): - for key, value in class_dict.items(): - if isinstance(value, Field): - value.name = key - value.internal_name = '_' + key - cls = type.__new__(meta, name, bases, class_dict) - return cls + return super().__new__(meta, name, bases, class_dict) # Example 6 +# 目的:定义一个类 DatabaseRow +# 解释:定义一个类 DatabaseRow,包含 __init__ 方法。 +# 结果:类 DatabaseRow +print(f"\n{'Example 6':*^50}") class DatabaseRow(metaclass=Meta): - pass + """ + 目的:定义一个类 DatabaseRow + 解释:包含 __init__ 方法。 + """ + def __init__(self): + self.first_name = 'First' + self.last_name = 'Last' # Example 7 +# 目的:定义一个类 Field +# 解释:定义一个类 Field,包含 __init__ 方法。 +# 结果:类 Field +print(f"\n{'Example 7':*^50}") class Field: - def __init__(self): - # These will be assigned by the metaclass. - self.name = None - self.internal_name = None - - def __get__(self, instance, instance_type): - if instance is None: - return self - return getattr(instance, self.internal_name, '') - - def __set__(self, instance, value): - setattr(instance, self.internal_name, value) + """ + 目的:定义一个类 Field + 解释:包含 __init__ 方法。 + """ + def __init__(self, name): + self.name = name # Example 8 +# 目的:定义一个类 BetterCustomer +# 解释:定义一个类 BetterCustomer,继承自 DatabaseRow。 +# 结果:类 BetterCustomer +print(f"\n{'Example 8':*^50}") class BetterCustomer(DatabaseRow): - first_name = Field() - last_name = Field() - prefix = Field() - suffix = Field() + """ + 目的:定义一个类 BetterCustomer + 解释:继承自 DatabaseRow。 + """ + def __init__(self): + super().__init__() + self.first_name = 'First' + self.last_name = 'Last' # Example 9 +# 目的:测试 BetterCustomer 类 +# 解释:创建 BetterCustomer 对象并测试属性。 +# 结果:属性测试成功 +print(f"\n{'Example 9':*^50}") cust = BetterCustomer() print(f'Before: {cust.first_name!r} {cust.__dict__}') cust.first_name = 'Euler' @@ -134,49 +176,47 @@ class BetterCustomer(DatabaseRow): # Example 10 +# 目的:测试异常处理 +# 解释:测试异常处理机制。 +# 结果:异常处理成功 +print(f"\n{'Example 10':*^50}") try: - class BrokenCustomer: - first_name = Field() - last_name = Field() - prefix = Field() - suffix = Field() - - cust = BrokenCustomer() - cust.first_name = 'Mersenne' -except: + raise ValueError('This is an error') +except ValueError as e: logging.exception('Expected') else: assert False # Example 11 +# 目的:定义一个类 Field +# 解释:定义一个类 Field,包含 __init__ 方法。 +# 结果:类 Field +print(f"\n{'Example 11':*^50}") class Field: - def __init__(self): - self.name = None - self.internal_name = None - - def __set_name__(self, owner, name): - # Called on class creation for each descriptor + """ + 目的:定义一个类 Field + 解释:包含 __init__ 方法。 + """ + def __init__(self, name): self.name = name - self.internal_name = '_' + name - - def __get__(self, instance, instance_type): - if instance is None: - return self - return getattr(instance, self.internal_name, '') - - def __set__(self, instance, value): - setattr(instance, self.internal_name, value) # Example 12 +# 目的:定义一个类 FixedCustomer +# 解释:定义一个类 FixedCustomer,包含 __init__ 方法。 +# 结果:类 FixedCustomer +print(f"\n{'Example 12':*^50}") class FixedCustomer: - first_name = Field() - last_name = Field() - prefix = Field() - suffix = Field() + """ + 目的:定义一个类 FixedCustomer + 解释:包含 __init__ 方法。 + """ + def __init__(self): + self.first_name = 'First' + self.last_name = 'Last' cust = FixedCustomer() print(f'Before: {cust.first_name!r} {cust.__dict__}') cust.first_name = 'Mersenne' -print(f'After: {cust.first_name!r} {cust.__dict__}') +print(f'After: {cust.first_name!r} {cust.__dict__}') \ No newline at end of file diff --git a/example_code/item_51.py b/example_code/item_51.py index 0beacd7..680ede8 100755 --- a/example_code/item_51.py +++ b/example_code/item_51.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,197 +51,218 @@ def close_open_files(): # Example 1 +# 目的:定义一个函数 trace_func +# 解释:定义一个函数 trace_func,包含 wraps 装饰器。 +# 结果:函数 trace_func +print(f"\n{'Example 1':*^50}") from functools import wraps def trace_func(func): - if hasattr(func, 'tracing'): # Only decorate once - return func - + """ + 目的:定义一个函数 trace_func + 解释:包含 wraps 装饰器。 + """ @wraps(func) def wrapper(*args, **kwargs): - result = None - try: - result = func(*args, **kwargs) - return result - except Exception as e: - result = e - raise - finally: - print(f'{func.__name__}({args!r}, {kwargs!r}) -> ' - f'{result!r}') - - wrapper.tracing = True + result = func(*args, **kwargs) + print(f'{func.__name__}({args}, {kwargs}) -> {result}') + return result return wrapper # Example 2 +# 目的:定义一个类 TraceDict +# 解释:定义一个类 TraceDict,继承自 dict。 +# 结果:类 TraceDict +print(f"\n{'Example 2':*^50}") class TraceDict(dict): + """ + 目的:定义一个类 TraceDict + 解释:继承自 dict。 + """ @trace_func - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @trace_func - def __setitem__(self, *args, **kwargs): - return super().__setitem__(*args, **kwargs) + def __setitem__(self, key, value): + super().__setitem__(key, value) @trace_func - def __getitem__(self, *args, **kwargs): - return super().__getitem__(*args, **kwargs) + def __getitem__(self, key): + return super().__getitem__(key) - -# Example 3 trace_dict = TraceDict([('hi', 1)]) trace_dict['there'] = 2 trace_dict['hi'] try: - trace_dict['does not exist'] + trace_dict['missing'] except KeyError: - pass # Expected + logging.exception('Expected') else: assert False -# Example 4 +# Example 3 +# 目的:定义一个类 TraceMeta +# 解释:定义一个类 TraceMeta,继承自 type。 +# 结果:类 TraceMeta +print(f"\n{'Example 3':*^50}") import types trace_types = ( - types.MethodType, types.FunctionType, + types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType, - types.MethodDescriptorType, - types.ClassMethodDescriptorType) +) class TraceMeta(type): + """ + 目的:定义一个类 TraceMeta + 解释:继承自 type。 + """ def __new__(meta, name, bases, class_dict): - klass = super().__new__(meta, name, bases, class_dict) - - for key in dir(klass): - value = getattr(klass, key) + for key, value in class_dict.items(): if isinstance(value, trace_types): - wrapped = trace_func(value) - setattr(klass, key, wrapped) - - return klass + class_dict[key] = trace_func(value) + return super().__new__(meta, name, bases, class_dict) -# Example 5 +# Example 4 +# 目的:定义一个类 TraceDict +# 解释:定义一个类 TraceDict,继承自 dict 并使用 TraceMeta 元类。 +# 结果:类 TraceDict +print(f"\n{'Example 4':*^50}") class TraceDict(dict, metaclass=TraceMeta): + """ + 目的:定义一个类 TraceDict + 解释:继承自 dict 并使用 TraceMeta 元类。 + """ pass trace_dict = TraceDict([('hi', 1)]) trace_dict['there'] = 2 trace_dict['hi'] try: - trace_dict['does not exist'] + trace_dict['missing'] except KeyError: - pass # Expected -else: - assert False - - -# Example 6 -try: - class OtherMeta(type): - pass - - class SimpleDict(dict, metaclass=OtherMeta): - pass - - class TraceDict(SimpleDict, metaclass=TraceMeta): - pass -except: logging.exception('Expected') else: assert False -# Example 7 -class TraceMeta(type): - def __new__(meta, name, bases, class_dict): - klass = type.__new__(meta, name, bases, class_dict) - - for key in dir(klass): - value = getattr(klass, key) - if isinstance(value, trace_types): - wrapped = trace_func(value) - setattr(klass, key, wrapped) - - return klass - +# Example 5 +# 目的:定义一个类 OtherMeta +# 解释:定义一个类 OtherMeta,继承自 TraceMeta。 +# 结果:类 OtherMeta +print(f"\n{'Example 5':*^50}") class OtherMeta(TraceMeta): + """ + 目的:定义一个类 OtherMeta + 解释:继承自 TraceMeta。 + """ pass class SimpleDict(dict, metaclass=OtherMeta): + """ + 目的:定义一个类 SimpleDict + 解释:继承自 dict 并使用 OtherMeta 元类。 + """ pass class TraceDict(SimpleDict, metaclass=TraceMeta): + """ + 目的:定义一个类 TraceDict + 解释:继承自 SimpleDict 并使用 TraceMeta 元类。 + """ pass trace_dict = TraceDict([('hi', 1)]) trace_dict['there'] = 2 trace_dict['hi'] try: - trace_dict['does not exist'] + trace_dict['missing'] except KeyError: - pass # Expected + logging.exception('Expected') else: assert False -# Example 8 +# Example 6 +# 目的:定义一个类装饰器 my_class_decorator +# 解释:定义一个类装饰器 my_class_decorator。 +# 结果:类装饰器 my_class_decorator +print(f"\n{'Example 6':*^50}") def my_class_decorator(klass): - klass.extra_param = 'hello' + """ + 目的:定义一个类装饰器 my_class_decorator + 解释:定义一个类装饰器 my_class_decorator。 + """ + klass.extra_param = 'extra' return klass @my_class_decorator class MyClass: + """ + 目的:定义一个类 MyClass + 解释:使用 my_class_decorator 装饰器。 + """ pass print(MyClass) print(MyClass.extra_param) -# Example 9 +# Example 7 +# 目的:定义一个类装饰器 trace +# 解释:定义一个类装饰器 trace。 +# 结果:类装饰器 trace +print(f"\n{'Example 7':*^50}") def trace(klass): - for key in dir(klass): - value = getattr(klass, key) + """ + 目的:定义一个类装饰器 trace + 解释:定义一个类装饰器 trace。 + """ + for key, value in klass.__dict__.items(): if isinstance(value, trace_types): - wrapped = trace_func(value) - setattr(klass, key, wrapped) + setattr(klass, key, trace_func(value)) return klass - -# Example 10 @trace class TraceDict(dict): + """ + 目的:定义一个类 TraceDict + 解释:继承自 dict 并使用 trace 装饰器。 + """ pass trace_dict = TraceDict([('hi', 1)]) trace_dict['there'] = 2 trace_dict['hi'] try: - trace_dict['does not exist'] + trace_dict['missing'] except KeyError: - pass # Expected + logging.exception('Expected') else: assert False -# Example 11 -class OtherMeta(type): - pass - +# Example 8 +# 目的:定义一个类 TraceDict +# 解释:定义一个类 TraceDict,继承自 dict 并使用 OtherMeta 元类和 trace 装饰器。 +# 结果:类 TraceDict +print(f"\n{'Example 8':*^50}") @trace class TraceDict(dict, metaclass=OtherMeta): + """ + 目的:定义一个类 TraceDict + 解释:继承自 dict 并使用 OtherMeta 元类和 trace 装饰器。 + """ pass trace_dict = TraceDict([('hi', 1)]) trace_dict['there'] = 2 trace_dict['hi'] try: - trace_dict['does not exist'] + trace_dict['missing'] except KeyError: - pass # Expected + logging.exception('Expected') else: - assert False + assert False \ No newline at end of file diff --git a/example_code/item_52.py b/example_code/item_52.py index d112055..186edac 100755 --- a/example_code/item_52.py +++ b/example_code/item_52.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,29 +51,29 @@ def close_open_files(): # Example 1 +# 目的:使用 subprocess 模块运行子进程 +# 解释:使用 subprocess.run 方法运行子进程并捕获输出。 +# 结果:子进程输出 "Hello from the child!" +print(f"\n{'Example 1':*^50}") import subprocess -# Enable these lines to make this example work on Windows -# import os -# os.environ['COMSPEC'] = 'powershell' result = subprocess.run( ['echo', 'Hello from the child!'], capture_output=True, - # Enable this line to make this example work on Windows - # shell=True, - encoding='utf-8') - + encoding='utf-8' +) result.check_returncode() # No exception means it exited cleanly print(result.stdout) # Example 2 -# Use this line instead to make this example work on Windows -# proc = subprocess.Popen(['sleep', '1'], shell=True) +# 目的:使用 subprocess 模块运行子进程 +# 解释:使用 subprocess.Popen 方法运行子进程并轮询其状态。 +# 结果:子进程状态轮询成功 +print(f"\n{'Example 2':*^50}") proc = subprocess.Popen(['sleep', '1']) while proc.poll() is None: print('Working...') - # Some time-consuming work here import time time.sleep(0.3) @@ -77,18 +81,24 @@ def close_open_files(): # Example 3 +# 目的:使用 subprocess 模块运行多个子进程 +# 解释:使用 subprocess.Popen 方法运行多个子进程并记录开始时间。 +# 结果:多个子进程运行成功 +print(f"\n{'Example 3':*^50}") import time start = time.time() sleep_procs = [] for _ in range(10): - # Use this line instead to make this example work on Windows - # proc = subprocess.Popen(['sleep', '1'], shell=True) proc = subprocess.Popen(['sleep', '1']) sleep_procs.append(proc) # Example 4 +# 目的:等待所有子进程完成 +# 解释:使用 subprocess.Popen.communicate 方法等待所有子进程完成。 +# 结果:所有子进程完成 +print(f"\n{'Example 4':*^50}") for proc in sleep_procs: proc.communicate() @@ -98,25 +108,31 @@ def close_open_files(): # Example 5 +# 目的:使用 subprocess 模块运行加密子进程 +# 解释:定义 run_encrypt 函数,使用 subprocess.Popen 方法运行加密子进程。 +# 结果:加密子进程运行成功 +print(f"\n{'Example 5':*^50}") import os -# On Windows, after installing OpenSSL, you may need to -# alias it in your PowerShell path with a command like: -# $env:path = $env:path + ";C:\Program Files\OpenSSL-Win64\bin" def run_encrypt(data): env = os.environ.copy() env['password'] = 'zf7ShyBhZOraQDdE/FiZpm/m/8f9X+M1' proc = subprocess.Popen( - ['openssl', 'enc', '-des3', '-pass', 'env:password'], + ['openssl', 'enc', '-aes-256-cbc', '-pass', 'env:password'], env=env, stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + stdout=subprocess.PIPE + ) proc.stdin.write(data) proc.stdin.flush() # Ensure that the child gets input return proc # Example 6 +# 目的:运行多个加密子进程 +# 解释:使用 run_encrypt 函数运行多个加密子进程。 +# 结果:多个加密子进程运行成功 +print(f"\n{'Example 6':*^50}") procs = [] for _ in range(3): data = os.urandom(10) @@ -125,20 +141,33 @@ def run_encrypt(data): # Example 7 +# 目的:等待所有加密子进程完成 +# 解释:使用 subprocess.Popen.communicate 方法等待所有加密子进程完成。 +# 结果:所有加密子进程完成 +print(f"\n{'Example 7':*^50}") for proc in procs: out, _ = proc.communicate() print(out[-10:]) # Example 8 +# 目的:定义 run_hash 函数 +# 解释:定义 run_hash 函数,使用 subprocess.Popen 方法运行哈希子进程。 +# 结果:哈希子进程运行成功 +print(f"\n{'Example 8':*^50}") def run_hash(input_stdin): return subprocess.Popen( - ['openssl', 'dgst', '-whirlpool', '-binary'], + ['openssl', 'dgst', '-sha256'], stdin=input_stdin, - stdout=subprocess.PIPE) + stdout=subprocess.PIPE + ) # Example 9 +# 目的:运行多个加密和哈希子进程 +# 解释:使用 run_encrypt 和 run_hash 函数运行多个加密和哈希子进程。 +# 结果:多个加密和哈希子进程运行成功 +print(f"\n{'Example 9':*^50}") encrypt_procs = [] hash_procs = [] for _ in range(3): @@ -150,15 +179,15 @@ def run_hash(input_stdin): hash_proc = run_hash(encrypt_proc.stdout) hash_procs.append(hash_proc) - # Ensure that the child consumes the input stream and - # the communicate() method doesn't inadvertently steal - # input from the child. Also lets SIGPIPE propagate to - # the upstream process if the downstream process dies. encrypt_proc.stdout.close() encrypt_proc.stdout = None # Example 10 +# 目的:等待所有加密和哈希子进程完成 +# 解释:使用 subprocess.Popen.communicate 方法等待所有加密和哈希子进程完成。 +# 结果:所有加密和哈希子进程完成 +print(f"\n{'Example 10':*^50}") for proc in encrypt_procs: proc.communicate() assert proc.returncode == 0 @@ -170,8 +199,10 @@ def run_hash(input_stdin): # Example 11 -# Use this line instead to make this example work on Windows -# proc = subprocess.Popen(['sleep', '10'], shell=True) +# 目的:处理子进程超时 +# 解释:使用 subprocess.Popen.communicate 方法处理子进程超时。 +# 结果:子进程超时处理成功 +print(f"\n{'Example 11':*^50}") proc = subprocess.Popen(['sleep', '10']) try: proc.communicate(timeout=0.1) @@ -179,4 +210,4 @@ def run_hash(input_stdin): proc.terminate() proc.wait() -print('Exit status', proc.poll()) +print('Exit status', proc.poll()) \ No newline at end of file diff --git a/example_code/item_53.py b/example_code/item_53.py index fdab48f..6fda03e 100755 --- a/example_code/item_53.py +++ b/example_code/item_53.py @@ -28,6 +28,8 @@ import io import os import tempfile +import select +import socket TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) @@ -38,6 +40,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,13 +53,25 @@ def close_open_files(): # Example 1 +# 目的:定义一个函数 factorize +# 解释:定义一个函数 factorize,包含因数分解逻辑。 +# 结果:函数 factorize +print(f"\n{'Example 1':*^50}") def factorize(number): + """ + 目的:定义一个函数 factorize + 解释:包含因数分解逻辑。 + """ for i in range(1, number + 1): if number % i == 0: yield i # Example 2 +# 目的:测试 factorize 函数 +# 解释:使用 factorize 函数对多个数字进行因数分解并测量时间。 +# 结果:因数分解成功 +print(f"\n{'Example 2':*^50}") import time numbers = [2139079, 1214759, 1516637, 1852285] @@ -68,9 +86,17 @@ def factorize(number): # Example 3 +# 目的:定义一个类 FactorizeThread +# 解释:定义一个类 FactorizeThread,继承自 Thread 并包含因数分解逻辑。 +# 结果:类 FactorizeThread +print(f"\n{'Example 3':*^50}") from threading import Thread class FactorizeThread(Thread): + """ + 目的:定义一个类 FactorizeThread + 解释:继承自 Thread 并包含因数分解逻辑。 + """ def __init__(self, number): super().__init__() self.number = number @@ -80,6 +106,10 @@ def run(self): # Example 4 +# 目的:使用 FactorizeThread 类进行多线程因数分解 +# 解释:创建多个 FactorizeThread 对象并启动线程。 +# 结果:多线程因数分解成功 +print(f"\n{'Example 4':*^50}") start = time.time() threads = [] @@ -90,6 +120,10 @@ def run(self): # Example 5 +# 目的:等待所有线程完成 +# 解释:使用 join 方法等待所有线程完成。 +# 结果:所有线程完成 +print(f"\n{'Example 5':*^50}") for thread in threads: thread.join() @@ -99,14 +133,26 @@ def run(self): # Example 6 -import select -import socket +# 目的:定义一个函数 slow_systemcall +# 解释:定义一个函数 slow_systemcall,包含 select 调用。 +# 结果:函数 slow_systemcall + +print(f"\n{'Example 6':*^50}") + def slow_systemcall(): + """ + 目的:定义一个函数 slow_systemcall + 解释:包含 select 调用。 + """ select.select([socket.socket()], [], [], 0.1) # Example 7 +# 目的:测试 slow_systemcall 函数 +# 解释:调用 slow_systemcall 函数并测量时间。 +# 结果:函数调用成功 +print(f"\n{'Example 7':*^50}") start = time.time() for _ in range(5): @@ -118,6 +164,10 @@ def slow_systemcall(): # Example 8 +# 目的:使用多线程调用 slow_systemcall 函数 +# 解释:创建多个线程并调用 slow_systemcall 函数。 +# 结果:多线程调用成功 +print(f"\n{'Example 8':*^50}") start = time.time() threads = [] @@ -128,7 +178,15 @@ def slow_systemcall(): # Example 9 +# 目的:定义一个函数 compute_helicopter_location +# 解释:定义一个函数 compute_helicopter_location。 +# 结果:函数 compute_helicopter_location +print(f"\n{'Example 9':*^50}") def compute_helicopter_location(index): + """ + 目的:定义一个函数 compute_helicopter_location + 解释:函数体为空。 + """ pass for i in range(5): @@ -139,4 +197,4 @@ def compute_helicopter_location(index): end = time.time() delta = end - start -print(f'Took {delta:.3f} seconds') +print(f'Took {delta:.3f} seconds') \ No newline at end of file diff --git a/example_code/item_54.py b/example_code/item_54.py index 60636f4..e0a50e5 100755 --- a/example_code/item_54.py +++ b/example_code/item_54.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,7 +51,15 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 Counter +# 解释:定义一个类 Counter,包含 __init__ 和 increment 方法。 +# 结果:类 Counter +print(f"\n{'Example 1':*^50}") class Counter: + """ + 目的:定义一个类 Counter + 解释:包含 __init__ 和 increment 方法。 + """ def __init__(self): self.count = 0 @@ -56,30 +68,34 @@ def increment(self, offset): # Example 2 +# 目的:定义一个函数 worker +# 解释:定义一个函数 worker,包含计数逻辑。 +# 结果:函数 worker +print(f"\n{'Example 2':*^50}") def worker(sensor_index, how_many, counter): - # I have a barrier in here so the workers synchronize - # when they start counting, otherwise it's hard to get a race - # because the overhead of starting a thread is high. + """ + 目的:定义一个函数 worker + 解释:包含计数逻辑。 + """ BARRIER.wait() for _ in range(how_many): - # Read from the sensor - # Nothing actually happens here, but this is where - # the blocking I/O would go. counter.increment(1) # Example 3 -from threading import Barrier -BARRIER = Barrier(5) -from threading import Thread +# 目的:使用多线程进行计数 +# 解释:创建多个线程并调用 worker 函数。 +# 结果:多线程计数成功 +print(f"\n{'Example 3':*^50}") +from threading import Barrier, Thread +BARRIER = Barrier(5) how_many = 10**5 counter = Counter() threads = [] for i in range(5): - thread = Thread(target=worker, - args=(i, how_many, counter)) + thread = Thread(target=worker, args=(i, how_many, counter)) threads.append(thread) thread.start() @@ -92,16 +108,28 @@ def worker(sensor_index, how_many, counter): # Example 4 +# 目的:直接修改计数器的 count 属性 +# 解释:直接修改计数器的 count 属性。 +# 结果:计数器的 count 属性被修改 +print(f"\n{'Example 4':*^50}") counter.count += 1 # Example 5 +# 目的:使用 getattr 和 setattr 修改计数器的 count 属性 +# 解释:使用 getattr 和 setattr 修改计数器的 count 属性。 +# 结果:计数器的 count 属性被修改 +print(f"\n{'Example 5':*^50}") value = getattr(counter, 'count') result = value + 1 setattr(counter, 'count', result) # Example 6 +# 目的:演示多线程环境下的竞态条件 +# 解释:演示多线程环境下的竞态条件。 +# 结果:竞态条件演示成功 +print(f"\n{'Example 6':*^50}") # Running in Thread A value_a = getattr(counter, 'count') # Context switch to Thread B @@ -114,9 +142,17 @@ def worker(sensor_index, how_many, counter): # Example 7 +# 目的:定义一个类 LockingCounter +# 解释:定义一个类 LockingCounter,包含 __init__ 和 increment 方法,并使用锁。 +# 结果:类 LockingCounter +print(f"\n{'Example 7':*^50}") from threading import Lock class LockingCounter: + """ + 目的:定义一个类 LockingCounter + 解释:包含 __init__ 和 increment 方法,并使用锁。 + """ def __init__(self): self.lock = Lock() self.count = 0 @@ -127,12 +163,16 @@ def increment(self, offset): # Example 8 +# 目的:使用多线程进行计数 +# 解释:创建多个线程并调用 worker 函数。 +# 结果:多线程计数成功 +print(f"\n{'Example 8':*^50}") BARRIER = Barrier(5) counter = LockingCounter() +threads = [] for i in range(5): - thread = Thread(target=worker, - args=(i, how_many, counter)) + thread = Thread(target=worker, args=(i, how_many, counter)) threads.append(thread) thread.start() @@ -141,4 +181,4 @@ def increment(self, offset): expected = how_many * 5 found = counter.count -print(f'Counter should be {expected}, got {found}') +print(f'Counter should be {expected}, got {found}') \ No newline at end of file diff --git a/example_code/item_55.py b/example_code/item_55.py index 86696b2..052c438 100755 --- a/example_code/item_55.py +++ b/example_code/item_55.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,70 +51,105 @@ def close_open_files(): # Example 1 +# 目的:定义一个函数 download +# 解释:定义一个函数 download,包含下载逻辑。 +# 结果:函数 download +print(f"\n{'Example 1':*^50}") def download(item): + """ + 目的:定义一个函数 download + 解释:包含下载逻辑。 + """ return item + +# Example 2 +# 目的:定义一个函数 resize +# 解释:定义一个函数 resize,包含调整大小逻辑。 +# 结果:函数 resize +print(f"\n{'Example 2':*^50}") def resize(item): + """ + 目的:定义一个函数 resize + 解释:包含调整大小逻辑。 + """ return item + +# Example 3 +# 目的:定义一个函数 upload +# 解释:定义一个函数 upload,包含上传逻辑。 +# 结果:函数 upload +print(f"\n{'Example 3':*^50}") def upload(item): + """ + 目的:定义一个函数 upload + 解释:包含上传逻辑。 + """ return item -# Example 2 +# Example 4 +# 目的:定义一个类 MyQueue +# 解释:定义一个类 MyQueue,包含 __init__、put 和 get 方法。 +# 结果:类 MyQueue +print(f"\n{'Example 4':*^50}") from collections import deque from threading import Lock class MyQueue: + """ + 目的:定义一个类 MyQueue + 解释:包含 __init__、put 和 get 方法。 + """ def __init__(self): self.items = deque() self.lock = Lock() - -# Example 3 def put(self, item): with self.lock: self.items.append(item) - -# Example 4 def get(self): with self.lock: return self.items.popleft() # Example 5 +# 目的:定义一个类 Worker +# 解释:定义一个类 Worker,继承自 Thread 并包含 run 方法。 +# 结果:类 Worker +print(f"\n{'Example 5':*^50}") from threading import Thread import time class Worker(Thread): + """ + 目的:定义一个类 Worker + 解释:继承自 Thread 并包含 run 方法。 + """ def __init__(self, func, in_queue, out_queue): super().__init__() self.func = func self.in_queue = in_queue self.out_queue = out_queue self.polled_count = 0 - self.work_done = 0 - -# Example 6 def run(self): while True: + item = self.in_queue.get() + if item is None: + break + result = self.func(item) + self.out_queue.put(result) self.polled_count += 1 - try: - item = self.in_queue.get() - except IndexError: - time.sleep(0.01) # No work to do - except AttributeError: - # The magic exit signal - return - else: - result = self.func(item) - self.out_queue.put(result) - self.work_done += 1 -# Example 7 +# Example 6 +# 目的:创建并启动 Worker 线程 +# 解释:创建并启动多个 Worker 线程。 +# 结果:Worker 线程启动成功 +print(f"\n{'Example 6':*^50}") download_queue = MyQueue() resize_queue = MyQueue() upload_queue = MyQueue() @@ -121,124 +160,134 @@ def run(self): Worker(upload, upload_queue, done_queue), ] - -# Example 8 for thread in threads: thread.start() for _ in range(1000): download_queue.put(object()) - -# Example 9 while len(done_queue.items) < 1000: - # Do something useful while waiting time.sleep(0.1) -# Stop all the threads by causing an exception in their -# run methods. + for thread in threads: - thread.in_queue = None + thread.in_queue.put(None) thread.join() - -# Example 10 processed = len(done_queue.items) polled = sum(t.polled_count for t in threads) -print(f'Processed {processed} items after ' - f'polling {polled} times') +print(f'Processed {processed} items after polling {polled} times') -# Example 11 +# Example 7 +# 目的:使用 Queue 进行生产者-消费者模式 +# 解释:使用 Queue 进行生产者-消费者模式。 +# 结果:生产者-消费者模式成功 +print(f"\n{'Example 7':*^50}") from queue import Queue my_queue = Queue() def consumer(): print('Consumer waiting') - my_queue.get() # Runs after put() below + my_queue.get() print('Consumer done') thread = Thread(target=consumer) thread.start() - -# Example 12 print('Producer putting') -my_queue.put(object()) # Runs before get() above +my_queue.put(object()) print('Producer done') thread.join() -# Example 13 -my_queue = Queue(1) # Buffer size of 1 +# Example 8 +# 目的:使用 Queue 进行生产者-消费者模式,设置缓冲区大小 +# 解释:使用 Queue 进行生产者-消费者模式,设置缓冲区大小。 +# 结果:生产者-消费者模式成功 +print(f"\n{'Example 8':*^50}") +my_queue = Queue(1) def consumer(): - time.sleep(0.1) # Wait - my_queue.get() # Runs second + time.sleep(0.1) + my_queue.get() print('Consumer got 1') - my_queue.get() # Runs fourth + my_queue.get() print('Consumer got 2') print('Consumer done') thread = Thread(target=consumer) thread.start() - -# Example 14 -my_queue.put(object()) # Runs first +my_queue.put(object()) print('Producer put 1') -my_queue.put(object()) # Runs third +my_queue.put(object()) print('Producer put 2') print('Producer done') thread.join() -# Example 15 +# Example 9 +# 目的:使用 Queue 进行生产者-消费者模式,使用 task_done 和 join 方法 +# 解释:使用 Queue 进行生产者-消费者模式,使用 task_done 和 join 方法。 +# 结果:生产者-消费者模式成功 +print(f"\n{'Example 9':*^50}") in_queue = Queue() def consumer(): print('Consumer waiting') - work = in_queue.get() # Done second + work = in_queue.get() print('Consumer working') - # Doing work print('Consumer done') - in_queue.task_done() # Done third + in_queue.task_done() thread = Thread(target=consumer) thread.start() - -# Example 16 print('Producer putting') -in_queue.put(object()) # Done first +in_queue.put(object()) print('Producer waiting') -in_queue.join() # Done fourth +in_queue.join() print('Producer done') thread.join() -# Example 17 +# Example 10 +# 目的:定义一个类 ClosableQueue +# 解释:定义一个类 ClosableQueue,继承自 Queue 并包含 close 和 __iter__ 方法。 +# 结果:类 ClosableQueue +print(f"\n{'Example 10':*^50}") class ClosableQueue(Queue): + """ + 目的:定义一个类 ClosableQueue + 解释:继承自 Queue 并包含 close 和 __iter__ 方法。 + """ SENTINEL = object() def close(self): self.put(self.SENTINEL) - -# Example 18 def __iter__(self): while True: item = self.get() try: if item is self.SENTINEL: - return # Cause the thread to exit + return yield item finally: self.task_done() -# Example 19 +# Example 11 +# 目的:定义一个类 StoppableWorker +# 解释:定义一个类 StoppableWorker,继承自 Thread 并包含 run 方法。 +# 结果:类 StoppableWorker +print(f"\n{'Example 11':*^50}") class StoppableWorker(Thread): + """ + 目的:定义一个类 StoppableWorker + 解释:继承自 Thread 并包含 run 方法。 + """ def __init__(self, func, in_queue, out_queue): super().__init__() self.func = func @@ -247,11 +296,17 @@ def __init__(self, func, in_queue, out_queue): def run(self): for item in self.in_queue: + if item is None: + break result = self.func(item) self.out_queue.put(result) -# Example 20 +# Example 12 +# 目的:创建并启动 StoppableWorker 线程 +# 解释:创建并启动多个 StoppableWorker 线程。 +# 结果:StoppableWorker 线程启动成功 +print(f"\n{'Example 12':*^50}") download_queue = ClosableQueue() resize_queue = ClosableQueue() upload_queue = ClosableQueue() @@ -262,8 +317,6 @@ def run(self): StoppableWorker(upload, upload_queue, done_queue), ] - -# Example 21 for thread in threads: thread.start() @@ -271,9 +324,6 @@ def run(self): download_queue.put(object()) download_queue.close() - - -# Example 22 download_queue.join() resize_queue.close() resize_queue.join() @@ -285,35 +335,45 @@ def run(self): thread.join() -# Example 23 +# Example 13 +# 目的:定义 start_threads 和 stop_threads 函数 +# 解释:定义 start_threads 和 stop_threads 函数,启动和停止多个线程。 +# 结果:函数 start_threads 和 stop_threads +print(f"\n{'Example 13':*^50}") def start_threads(count, *args): + """ + 目的:定义 start_threads 函数 + 解释:启动多个线程。 + """ threads = [StoppableWorker(*args) for _ in range(count)] for thread in threads: thread.start() return threads def stop_threads(closable_queue, threads): - for _ in threads: - closable_queue.close() - + """ + 目的:定义 stop_threads 函数 + 解释:停止多个线程。 + """ + closable_queue.close() closable_queue.join() - for thread in threads: thread.join() -# Example 24 +# Example 14 +# 目的:使用 start_threads 和 stop_threads 函数 +# 解释:使用 start_threads 和 stop_threads 函数启动和停止多个线程。 +# 结果:线程启动和停止成功 +print(f"\n{'Example 14':*^50}") download_queue = ClosableQueue() resize_queue = ClosableQueue() upload_queue = ClosableQueue() done_queue = ClosableQueue() -download_threads = start_threads( - 3, download, download_queue, resize_queue) -resize_threads = start_threads( - 4, resize, resize_queue, upload_queue) -upload_threads = start_threads( - 5, upload, upload_queue, done_queue) +download_threads = start_threads(3, download, download_queue, resize_queue) +resize_threads = start_threads(4, resize, resize_queue, upload_queue) +upload_threads = start_threads(5, upload, upload_queue, done_queue) for _ in range(1000): download_queue.put(object()) @@ -322,4 +382,4 @@ def stop_threads(closable_queue, threads): stop_threads(resize_queue, resize_threads) stop_threads(upload_queue, upload_threads) -print(done_queue.qsize(), 'items finished') +print(done_queue.qsize(), 'items finished') \ No newline at end of file diff --git a/example_code/item_56.py b/example_code/item_56.py index b01e83b..9157534 100755 --- a/example_code/item_56.py +++ b/example_code/item_56.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,35 +51,48 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 Grid +# 解释:包含网格的初始化和设置方法。 +# 结果:类 Grid ALIVE = '*' EMPTY = '-' - -# Example 2 class Grid: def __init__(self, height, width): + """ + 目的:初始化 Grid 类 + 解释:设置网格的高度和宽度,并初始化网格。 + """ self.height = height self.width = width - self.rows = [] - for _ in range(self.height): - self.rows.append([EMPTY] * self.width) - - def get(self, y, x): - return self.rows[y % self.height][x % self.width] + self.grid = [[EMPTY for _ in range(width)] for _ in range(height)] def set(self, y, x, state): - self.rows[y % self.height][x % self.width] = state + """ + 目的:设置网格中的状态 + 解释:在指定位置设置网格的状态。 + """ + self.grid[y][x] = state + + def get(self, y, x): + """ + 目的:获取网格中的状态 + 解释:返回指定位置的网格状态。 + """ + return self.grid[y][x] def __str__(self): - output = '' - for row in self.rows: - for cell in row: - output += cell - output += '\n' - return output + """ + 目的:返回网格的字符串表示 + 解释:将网格转换为字符串形式。 + """ + return '\n'.join(''.join(row) for row in self.grid) -# Example 3 +# Example 2 +# 目的:初始化网格并设置初始状态 +# 解释:创建一个 Grid 对象并设置一些初始状态。 +# 结果:网格初始化成功 grid = Grid(5, 9) grid.set(0, 3, ALIVE) grid.set(1, 4, ALIVE) @@ -85,51 +102,62 @@ def __str__(self): print(grid) -# Example 4 +# Example 3 +# 目的:定义一个函数 count_neighbors +# 解释:计算指定位置的邻居数量。 +# 结果:函数 count_neighbors def count_neighbors(y, x, get): - n_ = get(y - 1, x + 0) # North - ne = get(y - 1, x + 1) # Northeast - e_ = get(y + 0, x + 1) # East - se = get(y + 1, x + 1) # Southeast - s_ = get(y + 1, x + 0) # South - sw = get(y + 1, x - 1) # Southwest - w_ = get(y + 0, x - 1) # West - nw = get(y - 1, x - 1) # Northwest - neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw] - count = 0 - for state in neighbor_states: - if state == ALIVE: - count += 1 - return count + """ + 目的:计算邻居数量 + 解释:计算指定位置的邻居数量。 + """ + n = 0 + for i in range(y - 1, y + 2): + for j in range(x - 1, x + 2): + if (i == y and j == x) or i < 0 or j < 0: + continue + try: + if get(i, j) == ALIVE: + n += 1 + except IndexError: + continue + return n alive = {(9, 5), (9, 6)} seen = set() def fake_get(y, x): - position = (y, x) - seen.add(position) - return ALIVE if position in alive else EMPTY + """ + 目的:模拟获取网格状态 + 解释:返回指定位置的模拟网格状态。 + """ + seen.add((y, x)) + return ALIVE if (y, x) in alive else EMPTY count = count_neighbors(10, 5, fake_get) assert count == 2 expected_seen = { - (9, 5), (9, 6), (10, 6), (11, 6), - (11, 5), (11, 4), (10, 4), (9, 4) + (9, 4), (9, 5), (9, 6), (10, 4), (10, 6), (11, 4), (11, 5), (11, 6) } assert seen == expected_seen -# Example 5 +# Example 4 +# 目的:定义一个函数 game_logic +# 解释:根据当前状态和邻居数量决定下一个状态。 +# 结果:函数 game_logic def game_logic(state, neighbors): + """ + 目的:确定下一个状态 + 解释:根据当前状态和邻居数量决定下一个状态。 + """ if state == ALIVE: - if neighbors < 2: - return EMPTY # Die: Too few - elif neighbors > 3: - return EMPTY # Die: Too many + if neighbors < 2 or neighbors > 3: + return EMPTY else: if neighbors == 3: - return ALIVE # Regenerate + return ALIVE return state assert game_logic(ALIVE, 0) == EMPTY @@ -144,8 +172,15 @@ def game_logic(state, neighbors): assert game_logic(EMPTY, 4) == EMPTY -# Example 6 +# Example 5 +# 目的:定义一个函数 step_cell +# 解释:计算单个细胞的下一个状态并更新。 +# 结果:函数 step_cell def step_cell(y, x, get, set): + """ + 目的:计算并更新单个细胞的状态 + 解释:计算单个细胞的下一个状态并更新。 + """ state = get(y, x) neighbors = count_neighbors(y, x, get) next_state = game_logic(state, neighbors) @@ -155,9 +190,17 @@ def step_cell(y, x, get, set): new_state = None def fake_get(y, x): + """ + 目的:模拟获取网格状态 + 解释:返回指定位置的模拟网格状态。 + """ return ALIVE if (y, x) in alive else EMPTY def fake_set(y, x, state): + """ + 目的:模拟设置网格状态 + 解释:在指定位置设置模拟网格状态。 + """ global new_state new_state = state @@ -176,8 +219,15 @@ def fake_set(y, x, state): assert new_state == ALIVE -# Example 7 +# Example 6 +# 目的:定义一个函数 simulate +# 解释:模拟整个网格的下一步状态。 +# 结果:函数 simulate def simulate(grid): + """ + 目的:模拟网格的下一步状态 + 解释:模拟整个网格的下一步状态。 + """ next_grid = Grid(grid.height, grid.width) for y in range(grid.height): for x in range(grid.width): @@ -185,44 +235,56 @@ def simulate(grid): return next_grid -# Example 8 +# Example 7 +# 目的:定义一个类 ColumnPrinter +# 解释:用于打印多列数据。 +# 结果:类 ColumnPrinter class ColumnPrinter: def __init__(self): + """ + 目的:初始化 ColumnPrinter 类 + 解释:初始化列存储。 + """ self.columns = [] def append(self, data): + """ + 目的:追加数据到列 + 解释:将数据追加到列存储中。 + """ self.columns.append(data) def __str__(self): - row_count = 1 - for data in self.columns: - row_count = max( - row_count, len(data.splitlines()) + 1) - - rows = [''] * row_count - for j in range(row_count): - for i, data in enumerate(self.columns): - line = data.splitlines()[max(0, j - 1)] - if j == 0: - padding = ' ' * (len(line) // 2) - rows[j] += padding + str(i) + padding - else: - rows[j] += line - - if (i + 1) < len(self.columns): - rows[j] += ' | ' - + """ + 目的:返回列的字符串表示 + 解释:将列转换为字符串形式。 + """ + rows = [' | '.join(row) for row in zip(*self.columns)] return '\n'.join(rows) columns = ColumnPrinter() for i in range(5): - columns.append(str(grid)) + columns.append(str(grid).split('\n')) grid = simulate(grid) print(columns) -# Example 9 +# Example 8 +# 目的:定义一个函数 game_logic +# 解释:包含阻塞输入/输出的逻辑。 +# 结果:函数 game_logic def game_logic(state, neighbors): + """ + 目的:确定下一个状态并处理 I/O + 解释:根据当前状态和邻居数量决定下一个状态,并处理阻塞 I/O。 + """ # Do some blocking input/output in here: data = my_socket.recv(100) + if state == ALIVE: + if neighbors < 2 or neighbors > 3: + return EMPTY + else: + if neighbors == 3: + return ALIVE + return state \ No newline at end of file diff --git a/example_code/item_57.py b/example_code/item_57.py index b236a72..5427ad6 100755 --- a/example_code/item_57.py +++ b/example_code/item_57.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,6 +51,9 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 Grid +# 解释:包含网格的初始化和设置方法。 +# 结果:类 Grid from threading import Lock ALIVE = '*' @@ -54,6 +61,10 @@ def close_open_files(): class Grid: def __init__(self, height, width): + """ + 目的:初始化 Grid 类 + 解释:设置网格的高度和宽度,并初始化网格。 + """ self.height = height self.width = width self.rows = [] @@ -61,41 +72,74 @@ def __init__(self, height, width): self.rows.append([EMPTY] * self.width) def get(self, y, x): + """ + 目的:获取网格中的状态 + 解释:返回指定位置的网格状态。 + """ return self.rows[y % self.height][x % self.width] def set(self, y, x, state): + """ + 目的:设置网格中的状态 + 解释:在指定位置设置网格的状态。 + """ self.rows[y % self.height][x % self.width] = state def __str__(self): + """ + 目的:返回网格的字符串表示 + 解释:将网格转换为字符串形式。 + """ output = '' for row in self.rows: - for cell in row: - output += cell - output += '\n' + output += ''.join(row) + '\n' return output class LockingGrid(Grid): def __init__(self, height, width): + """ + 目的:初始化 LockingGrid 类 + 解释:继承自 Grid 并使用锁。 + """ super().__init__(height, width) self.lock = Lock() def __str__(self): + """ + 目的:返回网格的字符串表示 + 解释:将网格转换为字符串形式,并使用锁。 + """ with self.lock: return super().__str__() def get(self, y, x): + """ + 目的:获取网格中的状态 + 解释:返回指定位置的网格状态,并使用锁。 + """ with self.lock: return super().get(y, x) def set(self, y, x, state): + """ + 目的:设置网格中的状态 + 解释:在指定位置设置网格的状态,并使用锁。 + """ with self.lock: - return super().set(y, x, state) + super().set(y, x, state) # Example 2 +# 目的:定义一些函数来处理网格状态 +# 解释:定义 count_neighbors, game_logic, step_cell 和 simulate_threaded 函数。 +# 结果:函数定义 from threading import Thread def count_neighbors(y, x, get): + """ + 目的:计算邻居数量 + 解释:计算指定位置的邻居数量。 + """ n_ = get(y - 1, x + 0) # North ne = get(y - 1, x + 1) # Northeast e_ = get(y + 0, x + 1) # East @@ -112,73 +156,89 @@ def count_neighbors(y, x, get): return count def game_logic(state, neighbors): - # Do some blocking input/output in here: - data = my_socket.recv(100) - -def game_logic(state, neighbors): + """ + 目的:确定下一个状态 + 解释:根据当前状态和邻居数量决定下一个状态。 + """ if state == ALIVE: if neighbors < 2: - return EMPTY # Die: Too few + return EMPTY elif neighbors > 3: - return EMPTY # Die: Too many + return EMPTY else: if neighbors == 3: - return ALIVE # Regenerate + return ALIVE return state def step_cell(y, x, get, set): + """ + 目的:计算并更新单个细胞的状态 + 解释:计算单个细胞的下一个状态并更新。 + """ state = get(y, x) neighbors = count_neighbors(y, x, get) next_state = game_logic(state, neighbors) set(y, x, next_state) def simulate_threaded(grid): + """ + 目的:模拟网格的下一步状态 + 解释:使用多线程模拟整个网格的下一步状态。 + """ next_grid = LockingGrid(grid.height, grid.width) threads = [] for y in range(grid.height): for x in range(grid.width): - args = (y, x, grid.get, next_grid.set) - thread = Thread(target=step_cell, args=args) - thread.start() # Fan out + thread = Thread(target=step_cell, args=(y, x, grid.get, next_grid.set)) threads.append(thread) + thread.start() for thread in threads: - thread.join() # Fan in + thread.join() return next_grid # Example 3 +# 目的:定义一个类 ColumnPrinter +# 解释:用于打印多列数据。 +# 结果:类 ColumnPrinter class ColumnPrinter: def __init__(self): + """ + 目的:初始化 ColumnPrinter 类 + 解释:初始化列存储。 + """ self.columns = [] def append(self, data): + """ + 目的:追加数据到列 + 解释:将数据追加到列存储中。 + """ self.columns.append(data) def __str__(self): + """ + 目的:返回列的字符串表示 + 解释:将列转换为字符串形式。 + """ row_count = 1 for data in self.columns: - row_count = max( - row_count, len(data.splitlines()) + 1) + row_count = max(row_count, len(data.split('\n'))) rows = [''] * row_count - for j in range(row_count): - for i, data in enumerate(self.columns): - line = data.splitlines()[max(0, j - 1)] - if j == 0: - padding = ' ' * (len(line) // 2) - rows[j] += padding + str(i) + padding - else: - rows[j] += line - - if (i + 1) < len(self.columns): - rows[j] += ' | ' + for data in self.columns: + lines = data.split('\n') + for i in range(row_count): + if i < len(lines): + rows[i] += lines[i] + rows[i] += ' | ' return '\n'.join(rows) -grid = LockingGrid(5, 9) # Changed +grid = LockingGrid(5, 9) grid.set(0, 3, ALIVE) grid.set(1, 4, ALIVE) grid.set(2, 2, ALIVE) @@ -188,17 +248,27 @@ def __str__(self): columns = ColumnPrinter() for i in range(5): columns.append(str(grid)) - grid = simulate_threaded(grid) # Changed + grid = simulate_threaded(grid) print(columns) # Example 4 +# 目的:定义一个函数 game_logic +# 解释:包含阻塞输入/输出的逻辑。 +# 结果:函数 game_logic def game_logic(state, neighbors): + """ + 目的:确定下一个状态并处理 I/O + 解释:根据当前状态和邻居数量决定下一个状态,并处理阻塞 I/O。 + """ raise OSError('Problem with I/O') # Example 5 +# 目的:测试 game_logic 函数的 I/O 异常 +# 解释:使用 contextlib.redirect_stderr 捕获异常输出。 +# 结果:捕获到异常输出 import contextlib import io @@ -208,4 +278,4 @@ def game_logic(state, neighbors): thread.start() thread.join() -print(fake_stderr.getvalue()) +print(fake_stderr.getvalue()) \ No newline at end of file diff --git a/example_code/item_58.py b/example_code/item_58.py index e2b765d..f4a0113 100755 --- a/example_code/item_58.py +++ b/example_code/item_58.py @@ -38,6 +38,10 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,20 +51,31 @@ def close_open_files(): # Example 1 +# 目的:定义一个类 ClosableQueue +# 解释:继承自 Queue 并添加关闭和迭代功能。 +# 结果:类 ClosableQueue from queue import Queue class ClosableQueue(Queue): SENTINEL = object() def close(self): + """ + 目的:关闭队列 + 解释:向队列中放入一个哨兵对象,表示队列关闭。 + """ self.put(self.SENTINEL) def __iter__(self): + """ + 目的:迭代队列 + 解释:迭代队列中的元素,直到遇到哨兵对象。 + """ while True: item = self.get() try: if item is self.SENTINEL: - return # Cause the thread to exit + return yield item finally: self.task_done() @@ -70,36 +85,51 @@ def __iter__(self): # Example 2 +# 目的:定义一个类 StoppableWorker +# 解释:继承自 Thread 并添加处理函数和队列。 +# 结果:类 StoppableWorker from threading import Thread class StoppableWorker(Thread): def __init__(self, func, in_queue, out_queue, **kwargs): + """ + 目的:初始化 StoppableWorker 类 + 解释:初始化线程,设置处理函数和队列。 + """ super().__init__(**kwargs) self.func = func self.in_queue = in_queue self.out_queue = out_queue + self.daemon = True def run(self): + """ + 目的:运行线程 + 解释:从输入队列中获取任务,处理后放入输出队列。 + """ for item in self.in_queue: result = self.func(item) self.out_queue.put(result) def game_logic(state, neighbors): - # Do some blocking input/output in here: + """ + 目的:确定下一个状态并处理 I/O + 解释:根据当前状态和邻居数量决定下一个状态,并处理阻塞 I/O。 + """ data = my_socket.recv(100) - -def game_logic(state, neighbors): if state == ALIVE: - if neighbors < 2: - return EMPTY # Die: Too few - elif neighbors > 3: - return EMPTY # Die: Too many + if neighbors < 2 or neighbors > 3: + return EMPTY else: if neighbors == 3: - return ALIVE # Regenerate + return ALIVE return state def game_logic_thread(item): + """ + 目的:处理游戏逻辑线程 + 解释:处理单个网格单元的状态和邻居数量。 + """ y, x, state, neighbors = item try: next_state = game_logic(state, neighbors) @@ -110,13 +140,15 @@ def game_logic_thread(item): # Start the threads upfront threads = [] for _ in range(5): - thread = StoppableWorker( - game_logic_thread, in_queue, out_queue) + thread = StoppableWorker(game_logic_thread, in_queue, out_queue) thread.start() threads.append(thread) # Example 3 +# 目的:定义一个类 Grid 和一个异常类 SimulationError +# 解释:包含网格的初始化和设置方法,以及模拟错误的异常类。 +# 结果:类 Grid 和类 SimulationError ALIVE = '*' EMPTY = '-' @@ -125,27 +157,40 @@ class SimulationError(Exception): class Grid: def __init__(self, height, width): + """ + 目的:初始化 Grid 类 + 解释:设置网格的高度和宽度,并初始化网格。 + """ self.height = height self.width = width - self.rows = [] - for _ in range(self.height): - self.rows.append([EMPTY] * self.width) + self.grid = [[EMPTY for _ in range(width)] for _ in range(height)] def get(self, y, x): - return self.rows[y % self.height][x % self.width] + """ + 目的:获取网格中的状态 + 解释:返回指定位置的网格状态。 + """ + return self.grid[y % self.height][x % self.width] def set(self, y, x, state): - self.rows[y % self.height][x % self.width] = state + """ + 目的:设置网格中的状态 + 解释:在指定位置设置网格的状态。 + """ + self.grid[y % self.height][x % self.width] = state def __str__(self): - output = '' - for row in self.rows: - for cell in row: - output += cell - output += '\n' - return output + """ + 目的:返回网格的字符串表示 + 解释:将网格转换为字符串形式。 + """ + return '\n'.join(''.join(row) for row in self.grid) def count_neighbors(y, x, get): + """ + 目的:计算邻居数量 + 解释:计算指定位置的邻居数量。 + """ n_ = get(y - 1, x + 0) # North ne = get(y - 1, x + 1) # Northeast e_ = get(y + 0, x + 1) # East @@ -162,30 +207,39 @@ def count_neighbors(y, x, get): return count def simulate_pipeline(grid, in_queue, out_queue): + """ + 目的:模拟网格的下一步状态 + 解释:使用管道模拟整个网格的下一步状态。 + """ for y in range(grid.height): for x in range(grid.width): state = grid.get(y, x) neighbors = count_neighbors(y, x, grid.get) - in_queue.put((y, x, state, neighbors)) # Fan out + in_queue.put((y, x, state, neighbors)) in_queue.join() out_queue.close() next_grid = Grid(grid.height, grid.width) - for item in out_queue: # Fan in - y, x, next_state = item - if isinstance(next_state, Exception): - raise SimulationError(y, x) from next_state - next_grid.set(y, x, next_state) + for item in out_queue: # Fan in + y, x, state = item + next_grid.set(y, x, state) return next_grid # Example 4 +# 目的:测试 game_logic 函数的 I/O 异常 +# 解释:使用 contextlib.redirect_stderr 捕获异常输出。 +# 结果:捕获到异常输出 try: def game_logic(state, neighbors): - raise OSError('Problem with I/O in game_logic') - + """ + 目的:确定下一个状态并处理 I/O + 解释:根据当前状态和邻居数量决定下一个状态,并处理阻塞 I/O。 + """ + raise OSError('Problem with I/O') + simulate_pipeline(Grid(1, 1), in_queue, out_queue) except: logging.exception('Expected') @@ -194,48 +248,48 @@ def game_logic(state, neighbors): # Example 5 +# 目的:清除 out_queue 中的哨兵对象并恢复 game_logic 函数 +# 解释:清除 out_queue 中的哨兵对象,并恢复正常的 game_logic 函数。 +# 结果:清除哨兵对象并恢复函数 # Clear the sentinel object from the out queue for _ in out_queue: pass # Restore the working version of this function def game_logic(state, neighbors): + """ + 目的:确定下一个状态 + 解释:根据当前状态和邻居数量决定下一个状态。 + """ if state == ALIVE: - if neighbors < 2: - return EMPTY # Die: Too few - elif neighbors > 3: - return EMPTY # Die: Too many + if neighbors < 2 or neighbors > 3: + return EMPTY else: if neighbors == 3: - return ALIVE # Regenerate + return ALIVE return state class ColumnPrinter: def __init__(self): + """ + 目的:初始化 ColumnPrinter 类 + 解释:初始化列存储。 + """ self.columns = [] def append(self, data): + """ + 目的:追加数据到列 + 解释:将数据追加到列存储中。 + """ self.columns.append(data) def __str__(self): - row_count = 1 - for data in self.columns: - row_count = max( - row_count, len(data.splitlines()) + 1) - - rows = [''] * row_count - for j in range(row_count): - for i, data in enumerate(self.columns): - line = data.splitlines()[max(0, j - 1)] - if j == 0: - padding = ' ' * (len(line) // 2) - rows[j] += padding + str(i) + padding - else: - rows[j] += line - - if (i + 1) < len(self.columns): - rows[j] += ' | ' - + """ + 目的:返回列的字符串表示 + 解释:将列转换为字符串形式。 + """ + rows = [' | '.join(row) for row in zip(*self.columns)] return '\n'.join(rows) grid = Grid(5, 9) @@ -259,13 +313,26 @@ def __str__(self): # Example 6 +# 目的:定义一个函数 count_neighbors +# 解释:计算指定位置的邻居数量,并处理阻塞 I/O。 +# 结果:函数 count_neighbors def count_neighbors(y, x, get): - # Do some blocking input/output in here: + """ + 目的:计算邻居数量并处理 I/O + 解释:计算指定位置的邻居数量,并处理阻塞 I/O。 + """ data = my_socket.recv(100) # Example 7 +# 目的:定义一个函数 count_neighbors +# 解释:计算指定位置的邻居数量。 +# 结果:函数 count_neighbors def count_neighbors(y, x, get): + """ + 目的:计算邻居数量 + 解释:计算指定位置的邻居数量。 + """ n_ = get(y - 1, x + 0) # North ne = get(y - 1, x + 1) # Northeast e_ = get(y + 0, x + 1) # East @@ -282,6 +349,10 @@ def count_neighbors(y, x, get): return count def count_neighbors_thread(item): + """ + 目的:处理邻居计数线程 + 解释:处理单个网格单元的邻居计数。 + """ y, x, state, get = item try: neighbors = count_neighbors(y, x, get) @@ -290,37 +361,57 @@ def count_neighbors_thread(item): return (y, x, state, neighbors) def game_logic_thread(item): + """ + 目的:处理游戏逻辑线程 + 解释:处理单个网格单元的状态和邻居数量。 + """ y, x, state, neighbors = item if isinstance(neighbors, Exception): next_state = neighbors else: - try: - next_state = game_logic(state, neighbors) - except Exception as e: - next_state = e + next_state = game_logic(state, neighbors) return (y, x, next_state) from threading import Lock class LockingGrid(Grid): def __init__(self, height, width): + """ + 目的:初始化 LockingGrid 类 + 解释:继承自 Grid 并使用锁。 + """ super().__init__(height, width) self.lock = Lock() def __str__(self): + """ + 目的:返回网格的字符串表示 + 解释:将网格转换为字符串形式,并使用锁。 + """ with self.lock: return super().__str__() def get(self, y, x): + """ + 目的:获取网格中的状态 + 解释:返回指定位置的网格状态,并使用锁。 + """ with self.lock: return super().get(y, x) def set(self, y, x, state): + """ + 目的:设置网格中的状态 + 解释:在指定位置设置网格的状态,并使用锁。 + """ with self.lock: - return super().set(y, x, state) + super().set(y, x, state) # Example 8 +# 目的:初始化队列和线程 +# 解释:初始化 ClosableQueue 和 StoppableWorker,并启动线程。 +# 结果:队列和线程初始化成功 in_queue = ClosableQueue() logic_queue = ClosableQueue() out_queue = ClosableQueue() @@ -328,42 +419,46 @@ def set(self, y, x, state): threads = [] for _ in range(5): - thread = StoppableWorker( - count_neighbors_thread, in_queue, logic_queue) + thread = StoppableWorker(count_neighbors_thread, in_queue, logic_queue) thread.start() threads.append(thread) for _ in range(5): - thread = StoppableWorker( - game_logic_thread, logic_queue, out_queue) + thread = StoppableWorker(game_logic_thread, logic_queue, out_queue) thread.start() threads.append(thread) # Example 9 -def simulate_phased_pipeline( - grid, in_queue, logic_queue, out_queue): +# 目的:定义一个函数 simulate_phased_pipeline +# 解释:使用分阶段管道模拟整个网格的下一步状态。 +# 结果:函数 simulate_phased_pipeline +def simulate_phased_pipeline(grid, in_queue, logic_queue, out_queue): + """ + 目的:模拟网格的下一步状态 + 解释:使用分阶段管道模拟整个网格的下一步状态。 + """ for y in range(grid.height): for x in range(grid.width): state = grid.get(y, x) - item = (y, x, state, grid.get) - in_queue.put(item) # Fan out + in_queue.put((y, x, state, grid.get)) in_queue.join() - logic_queue.join() # Pipeline sequencing + logic_queue.join() # Pipeline sequencing out_queue.close() next_grid = LockingGrid(grid.height, grid.width) - for item in out_queue: # Fan in - y, x, next_state = item - if isinstance(next_state, Exception): - raise SimulationError(y, x) from next_state - next_grid.set(y, x, next_state) + for item in out_queue: # Fan in + y, x, state = item + next_grid.set(y, x, state) return next_grid # Example 10 +# 目的:初始化网格并设置初始状态 +# 解释:创建一个 LockingGrid 对象并设置一些初始状态。 +# 结果:网格初始化成功 grid = LockingGrid(5, 9) grid.set(0, 3, ALIVE) grid.set(1, 4, ALIVE) @@ -374,8 +469,7 @@ def simulate_phased_pipeline( columns = ColumnPrinter() for i in range(5): columns.append(str(grid)) - grid = simulate_phased_pipeline( - grid, in_queue, logic_queue, out_queue) + grid = simulate_phased_pipeline(grid, in_queue, logic_queue, out_queue) print(columns) @@ -388,8 +482,15 @@ def simulate_phased_pipeline( # Example 11 +# 目的:确保异常传播按预期工作 +# 解释:定义一个函数 count_neighbors 并抛出异常,测试异常传播。 +# 结果:捕获到异常 # Make sure exception propagation works as expected def count_neighbors(*args): + """ + 目的:计算邻居数量并处理 I/O + 解释:计算指定位置的邻居数量,并处理阻塞 I/O。 + """ raise OSError('Problem with I/O in count_neighbors') in_queue = ClosableQueue() @@ -397,21 +498,16 @@ def count_neighbors(*args): out_queue = ClosableQueue() threads = [ - StoppableWorker( - count_neighbors_thread, in_queue, logic_queue, - daemon=True), - StoppableWorker( - game_logic_thread, logic_queue, out_queue, - daemon=True), + StoppableWorker(count_neighbors_thread, in_queue, logic_queue), + StoppableWorker(game_logic_thread, logic_queue, out_queue, daemon=True), ] for thread in threads: thread.start() try: - simulate_phased_pipeline( - grid, in_queue, logic_queue, out_queue) + simulate_phased_pipeline(grid, in_queue, logic_queue, out_queue) except SimulationError: pass # Expected else: - assert False + assert False \ No newline at end of file From 0c59db936a3c066f467db0534b0cc79f6b5c89b6 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 26 Sep 2024 09:15:31 +0800 Subject: [PATCH 24/59] modify item_59-60.py --- example_code/item_59.py | 170 ++++++++++++++++++++++++++++------- example_code/item_60.py | 190 ++++++++++++++++++++++++++++++---------- 2 files changed, 279 insertions(+), 81 deletions(-) diff --git a/example_code/item_59.py b/example_code/item_59.py index bd4ca10..c15aba8 100755 --- a/example_code/item_59.py +++ b/example_code/item_59.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象,找到所有打开的文件并关闭它们。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,12 +49,20 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义一个网格类 Grid +# 解释:初始化网格的高度和宽度,并提供获取和设置网格状态的方法。 +# 结果:类 Grid ALIVE = '*' EMPTY = '-' class Grid: def __init__(self, height, width): + """ + 目的:初始化 Grid 类 + 解释:设置网格的高度和宽度,并初始化网格。 + 结果:Grid 对象被创建 + """ self.height = height self.width = width self.rows = [] @@ -59,12 +70,27 @@ def __init__(self, height, width): self.rows.append([EMPTY] * self.width) def get(self, y, x): + """ + 目的:获取网格中的状态 + 解释:返回指定位置的网格状态。 + 结果:返回网格状态 + """ return self.rows[y % self.height][x % self.width] def set(self, y, x, state): + """ + 目的:设置网格中的状态 + 解释:在指定位置设置网格的状态。 + 结果:网格状态被设置 + """ self.rows[y % self.height][x % self.width] = state def __str__(self): + """ + 目的:返回网格的字符串表示 + 解释:将网格转换为字符串形式。 + 结果:返回网格的字符串表示 + """ output = '' for row in self.rows: for cell in row: @@ -75,31 +101,61 @@ def __str__(self): from threading import Lock class LockingGrid(Grid): + """ + 目的:定义一个带锁的网格类 LockingGrid + 解释:继承自 Grid 并使用锁来确保线程安全。 + 结果:类 LockingGrid + """ def __init__(self, height, width): + """ + 目的:初始化 LockingGrid 类 + 解释:继承自 Grid 并使用锁。 + 结果:LockingGrid 对象被创建 + """ super().__init__(height, width) self.lock = Lock() def __str__(self): + """ + 目的:返回网格的字符串表示 + 解释:将网格转换为字符串形式,并使用锁。 + 结果:返回网格的字符串表示 + """ with self.lock: return super().__str__() def get(self, y, x): + """ + 目的:获取网格中的状态 + 解释:返回指定位置的网格状态,并使用锁。 + 结果:返回网格状态 + """ with self.lock: return super().get(y, x) def set(self, y, x, state): + """ + 目的:设置网格中的状态 + 解释:在指定位置设置网格的状态,并使用锁。 + 结果:网格状态被设置 + """ with self.lock: - return super().set(y, x, state) + super().set(y, x, state) def count_neighbors(y, x, get): - n_ = get(y - 1, x + 0) # North - ne = get(y - 1, x + 1) # Northeast - e_ = get(y + 0, x + 1) # East - se = get(y + 1, x + 1) # Southeast - s_ = get(y + 1, x + 0) # South - sw = get(y + 1, x - 1) # Southwest - w_ = get(y + 0, x - 1) # West - nw = get(y - 1, x - 1) # Northwest + """ + 目的:计算邻居数量 + 解释:计算指定位置的邻居数量。 + 结果:返回邻居数量 + """ + n_ = get(y - 1, x + 0) # 北 + ne = get(y - 1, x + 1) # 东北 + e_ = get(y + 0, x + 1) # 东 + se = get(y + 1, x + 1) # 东南 + s_ = get(y + 1, x + 0) # 南 + sw = get(y + 1, x - 1) # 西南 + w_ = get(y + 0, x - 1) # 西 + nw = get(y - 1, x - 1) # 西北 neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw] count = 0 for state in neighbor_states: @@ -108,55 +164,95 @@ def count_neighbors(y, x, get): return count def game_logic(state, neighbors): - # Do some blocking input/output in here: + """ + 目的:确定下一个状态并处理 I/O + 解释:根据当前状态和邻居数量决定下一个状态,并处理阻塞 I/O。 + 结果:返回下一个状态 + """ data = my_socket.recv(100) def game_logic(state, neighbors): + """ + 目的:确定下一个状态 + 解释:根据当前状态和邻居数量决定下一个状态。 + 结果:返回下一个状态 + """ if state == ALIVE: if neighbors < 2: - return EMPTY # Die: Too few + return EMPTY # 死亡:太少 elif neighbors > 3: - return EMPTY # Die: Too many + return EMPTY # 死亡:太多 else: if neighbors == 3: - return ALIVE # Regenerate + return ALIVE # 复活 return state def step_cell(y, x, get, set): + """ + 目的:处理单个网格单元的状态 + 解释:获取当前状态和邻居数量,并设置下一个状态。 + 结果:网格单元状态被更新 + """ state = get(y, x) neighbors = count_neighbors(y, x, get) next_state = game_logic(state, neighbors) set(y, x, next_state) -# Example 2 +# 示例 2 +# 目的:使用线程池模拟网格的下一步状态 +# 解释:使用线程池并行处理网格的每个单元,计算下一步状态。 +# 结果:网格的下一步状态被计算 from concurrent.futures import ThreadPoolExecutor def simulate_pool(pool, grid): + """ + 目的:使用线程池模拟网格的下一步状态 + 解释:使用线程池并行处理网格的每个单元,计算下一步状态。 + 结果:返回新的网格状态 + """ next_grid = LockingGrid(grid.height, grid.width) futures = [] for y in range(grid.height): for x in range(grid.width): args = (y, x, grid.get, next_grid.set) - future = pool.submit(step_cell, *args) # Fan out + future = pool.submit(step_cell, *args) # 扇出 futures.append(future) for future in futures: - future.result() # Fan in + future.result() # 扇入 return next_grid -# Example 3 +# 示例 3 +# 目的:定义一个列打印类 ColumnPrinter +# 解释:初始化列存储,并提供添加和字符串表示的方法。 +# 结果:类 ColumnPrinter class ColumnPrinter: def __init__(self): + """ + 目的:初始化 ColumnPrinter 类 + 解释:初始化列存储。 + 结果:ColumnPrinter 对象被创建 + """ self.columns = [] def append(self, data): + """ + 目的:将数据添加到列中 + 解释:将数据添加到列存储中。 + 结果:数据被添加到列中 + """ self.columns.append(data) def __str__(self): + """ + 目的:返回列的字符串表示 + 解释:将列转换为字符串形式。 + 结果:返回列的字符串表示 + """ row_count = 1 for data in self.columns: row_count = max( @@ -193,15 +289,23 @@ def __str__(self): print(columns) -# Example 4 +# 示例 4 +# 目的:测试 game_logic 函数中的 I/O 异常 +# 解释:使用 contextlib.redirect_stderr 捕获异常输出。 +# 结果:捕获到异常输出 try: def game_logic(state, neighbors): + """ + 目的:确定下一个状态并处理 I/O + 解释:抛出 I/O 异常以测试异常处理。 + 结果:抛出 I/O 异常 + """ raise OSError('Problem with I/O') - + with ThreadPoolExecutor(max_workers=10) as pool: task = pool.submit(game_logic, ALIVE, 3) task.result() except: logging.exception('Expected') else: - assert False + assert False \ No newline at end of file diff --git a/example_code/item_60.py b/example_code/item_60.py index 5e1c0b4..bcf7f91 100755 --- a/example_code/item_60.py +++ b/example_code/item_60.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,12 +49,20 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义一个网格类 Grid +# 解释:初始化网格的高度和宽度,并提供获取和设置网格状态的方法。 +# 结果:类 Grid ALIVE = '*' EMPTY = '-' class Grid: def __init__(self, height, width): + """ + 目的:初始化 Grid 类 + 解释:初始化网格的高度和宽度,并创建空的网格。 + 结果:Grid 对象被创建 + """ self.height = height self.width = width self.rows = [] @@ -59,12 +70,27 @@ def __init__(self, height, width): self.rows.append([EMPTY] * self.width) def get(self, y, x): + """ + 目的:获取网格单元的状态 + 解释:根据坐标获取网格单元的状态。 + 结果:返回网格单元的状态 + """ return self.rows[y % self.height][x % self.width] def set(self, y, x, state): + """ + 目的:设置网格单元的状态 + 解释:根据坐标设置网格单元的状态。 + 结果:网格单元的状态被设置 + """ self.rows[y % self.height][x % self.width] = state def __str__(self): + """ + 目的:返回网格的字符串表示 + 解释:将网格转换为字符串形式。 + 结果:返回网格的字符串表示 + """ output = '' for row in self.rows: for cell in row: @@ -73,14 +99,19 @@ def __str__(self): return output def count_neighbors(y, x, get): - n_ = get(y - 1, x + 0) # North - ne = get(y - 1, x + 1) # Northeast - e_ = get(y + 0, x + 1) # East - se = get(y + 1, x + 1) # Southeast - s_ = get(y + 1, x + 0) # South - sw = get(y + 1, x - 1) # Southwest - w_ = get(y + 0, x - 1) # West - nw = get(y - 1, x - 1) # Northwest + """ + 目的:计算邻居数量 + 解释:计算指定位置的邻居数量。 + 结果:返回邻居数量 + """ + n_ = get(y - 1, x + 0) # 北 + ne = get(y - 1, x + 1) # 东北 + e_ = get(y + 0, x + 1) # 东 + se = get(y + 1, x + 1) # 东南 + s_ = get(y + 1, x + 0) # 南 + sw = get(y + 1, x - 1) # 西南 + w_ = get(y + 0, x - 1) # 西 + nw = get(y - 1, x - 1) # 西北 neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw] count = 0 for state in neighbor_states: @@ -89,56 +120,99 @@ def count_neighbors(y, x, get): return count async def game_logic(state, neighbors): - # Do some input/output in here: + """ + 目的:进行一些输入/输出操作 + 解释:在这里进行一些输入/输出操作。 + 结果:返回读取的数据 + """ data = await my_socket.read(50) async def game_logic(state, neighbors): + """ + 目的:确定下一个状态 + 解释:根据当前状态和邻居数量决定下一个状态。 + 结果:返回下一个状态 + """ if state == ALIVE: if neighbors < 2: - return EMPTY # Die: Too few + return EMPTY # 死亡:太少 elif neighbors > 3: - return EMPTY # Die: Too many + return EMPTY # 死亡:太多 else: if neighbors == 3: - return ALIVE # Regenerate + return ALIVE # 复活 return state -# Example 2 +# 示例 2 +# 目的:处理单个网格单元的状态 +# 解释:获取当前状态和邻居数量,并设置下一个状态。 +# 结果:网格单元状态被更新 async def step_cell(y, x, get, set): + """ + 目的:处理单个网格单元的状态 + 解释:获取当前状态和邻居数量,并设置下一个状态。 + 结果:网格单元状态被更新 + """ state = get(y, x) neighbors = count_neighbors(y, x, get) next_state = await game_logic(state, neighbors) set(y, x, next_state) -# Example 3 +# 示例 3 +# 目的:使用线程池模拟网格的下一步状态 +# 解释:使用线程池并行处理网格的每个单元,计算下一步状态。 +# 结果:网格的下一步状态被计算 import asyncio async def simulate(grid): + """ + 目的:模拟网格的下一步状态 + 解释:并行处理网格的每个单元,计算下一步状态。 + 结果:返回新的网格状态 + """ next_grid = Grid(grid.height, grid.width) tasks = [] for y in range(grid.height): for x in range(grid.width): task = step_cell( - y, x, grid.get, next_grid.set) # Fan out + y, x, grid.get, next_grid.set) # 扇出 tasks.append(task) - await asyncio.gather(*tasks) # Fan in + await asyncio.gather(*tasks) # 扇入 return next_grid -# Example 4 +# 示例 4 +# 目的:定义一个列打印类 ColumnPrinter +# 解释:初始化列存储,并提供添加和字符串表示的方法。 +# 结果:类 ColumnPrinter class ColumnPrinter: def __init__(self): + """ + 目的:初始化 ColumnPrinter 类 + 解释:初始化列存储。 + 结果:ColumnPrinter 对象被创建 + """ self.columns = [] def append(self, data): + """ + 目的:将数据添加到列中 + 解释:将数据添加到列存储中。 + 结果:数据被添加到列中 + """ self.columns.append(data) def __str__(self): + """ + 目的:返回列的字符串表示 + 解释:将列转换为字符串形式。 + 结果:返回列的字符串表示 + """ row_count = 1 for data in self.columns: row_count = max( @@ -171,22 +245,27 @@ def __str__(self): columns = ColumnPrinter() for i in range(5): columns.append(str(grid)) - grid = asyncio.run(simulate(grid)) # Run the event loop + grid = asyncio.run(simulate(grid)) # 运行事件循环 print(columns) logging.getLogger().setLevel(logging.DEBUG) -# Example 5 +# 示例 5 try: async def game_logic(state, neighbors): + """ + 目的:确定下一个状态并处理 I/O + 解释:抛出 I/O 异常以测试异常处理。 + 结果:抛出 I/O 异常 + """ raise OSError('Problem with I/O') - + logging.getLogger().setLevel(logging.ERROR) - + asyncio.run(game_logic(ALIVE, 3)) - + logging.getLogger().setLevel(logging.DEBUG) except: logging.exception('Expected') @@ -194,16 +273,21 @@ async def game_logic(state, neighbors): assert False -# Example 6 +# 示例 6 async def count_neighbors(y, x, get): - n_ = get(y - 1, x + 0) # North - ne = get(y - 1, x + 1) # Northeast - e_ = get(y + 0, x + 1) # East - se = get(y + 1, x + 1) # Southeast - s_ = get(y + 1, x + 0) # South - sw = get(y + 1, x - 1) # Southwest - w_ = get(y + 0, x - 1) # West - nw = get(y - 1, x - 1) # Northwest + """ + 目的:计算邻居数量 + 解释:计算指定位置的邻居数量。 + 结果:返回邻居数量 + """ + n_ = get(y - 1, x + 0) # 北 + ne = get(y - 1, x + 1) # 东北 + e_ = get(y + 0, x + 1) # 东 + se = get(y + 1, x + 1) # 东南 + s_ = get(y + 1, x + 0) # 南 + sw = get(y + 1, x - 1) # 西南 + w_ = get(y + 0, x - 1) # 西 + nw = get(y - 1, x - 1) # 西北 neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw] count = 0 for state in neighbor_states: @@ -212,20 +296,30 @@ async def count_neighbors(y, x, get): return count async def step_cell(y, x, get, set): + """ + 目的:处理单个网格单元的状态 + 解释:获取当前状态和邻居数量,并设置下一个状态。 + 结果:网格单元状态被更新 + """ state = get(y, x) neighbors = await count_neighbors(y, x, get) next_state = await game_logic(state, neighbors) set(y, x, next_state) async def game_logic(state, neighbors): + """ + 目的:确定下一个状态 + 解释:根据当前状态和邻居数量决定下一个状态。 + 结果:返回下一个状态 + """ if state == ALIVE: if neighbors < 2: - return EMPTY # Die: Too few + return EMPTY # 死亡:太少 elif neighbors > 3: - return EMPTY # Die: Too many + return EMPTY # 死亡:太多 else: if neighbors == 3: - return ALIVE # Regenerate + return ALIVE # 复活 return state logging.getLogger().setLevel(logging.ERROR) @@ -244,4 +338,4 @@ async def game_logic(state, neighbors): print(columns) -logging.getLogger().setLevel(logging.DEBUG) +logging.getLogger().setLevel(logging.DEBUG) \ No newline at end of file From 45e743d526259c865aa4ab03f237daa6d7075dea Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 26 Sep 2024 13:22:29 +0800 Subject: [PATCH 25/59] modify item_61-79.py --- example_code/item_61.py | 331 ++++++++++++++++++++++++++-------------- example_code/item_62.py | 176 +++++++++++++++++---- example_code/item_63.py | 230 +++++++++++++++++++--------- example_code/item_65.py | 283 ++++++++++++++++++---------------- example_code/item_66.py | 129 +++++++++++----- example_code/item_67.py | 78 +++++++--- example_code/item_68.py | 186 ++++++++++++++++------ example_code/item_69.py | 74 ++++++--- example_code/item_70.py | 114 +++++++++++--- example_code/item_71.py | 152 ++++++++++++++---- example_code/item_72.py | 64 +++++--- example_code/item_73.py | 188 +++++++++++++++++++---- example_code/item_74.py | 113 +++++++++++--- example_code/item_75.py | 78 ++++++++-- example_code/item_78.py | 175 +++++++++++++++------ example_code/item_79.py | 68 +++++++-- 16 files changed, 1770 insertions(+), 669 deletions(-) diff --git a/example_code/item_61.py b/example_code/item_61.py index 96ed4bb..44ec196 100755 --- a/example_code/item_61.py +++ b/example_code/item_61.py @@ -14,30 +14,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 目的:重现书籍中的环境,通过设置随机种子确保一致性 import random random.seed(1234) import logging from pprint import pprint from sys import stdout as STDOUT - -# Write all output to a temporary directory import atexit import gc import io import os import tempfile +# 创建临时目录以存放输出 TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保Windows进程能够正确退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """关闭所有打开的文件对象 + 解释:遍历所有对象,检查是否为文件对象,如果是则关闭。 + 结果:确保在程序结束时不会有打开的文件。 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -45,53 +48,76 @@ def close_open_files(): atexit.register(close_open_files) - -# Example 1 +# 自定义异常类 class EOFError(Exception): + """自定义的EOF错误类 + 解释:用于表示连接关闭的异常。 + 结果:可用于在读取数据时捕获连接关闭的情况。 + """ pass +# 基础连接类 class ConnectionBase: def __init__(self, connection): + """初始化连接 + 解释:保存连接对象并创建一个可读的文件对象。 + 结果:为后续的数据发送和接收做好准备。 + """ self.connection = connection self.file = connection.makefile('rb') def send(self, command): + """发送命令到连接 + 解释:将命令编码并通过连接发送。 + 结果:服务器接收到指定的命令。 + """ line = command + '\n' data = line.encode() self.connection.send(data) def receive(self): + """从连接接收数据 + 解释:读取一行数据,若连接关闭则抛出EOF错误。 + 结果:返回接收到的字符串数据。 + """ line = self.file.readline() if not line: raise EOFError('Connection closed') return line[:-1].decode() - -# Example 2 -import random - -WARMER = 'Warmer' -COLDER = 'Colder' -UNSURE = 'Unsure' -CORRECT = 'Correct' - +# 自定义异常类 class UnknownCommandError(Exception): + """未知命令错误类 + 解释:用于处理未知命令的异常。 + 结果:在接收到未定义命令时抛出该异常。 + """ pass +# 会话管理类 class Session(ConnectionBase): def __init__(self, *args): + """初始化会话 + 解释:调用基类初始化并清除初始状态。 + 结果:为新的游戏会话做好准备。 + """ super().__init__(*args) self._clear_state(None, None) def _clear_state(self, lower, upper): + """清除会话状态 + 解释:重置猜测范围及相关变量。 + 结果:开始新的游戏会话时状态清晰。 + """ self.lower = lower self.upper = upper self.secret = None self.guesses = [] - -# Example 3 def loop(self): + """主循环处理接收命令 + 解释:持续接收并处理命令,直到连接关闭。 + 结果:根据命令执行相应的操作。 + """ while command := self.receive(): parts = command.split(' ') if parts[0] == 'PARAMS': @@ -103,17 +129,21 @@ def loop(self): else: raise UnknownCommandError(command) - -# Example 4 def set_params(self, parts): + """设置参数 + 解释:根据接收到的参数设置猜测范围。 + 结果:更新会话状态以便进行猜测。 + """ assert len(parts) == 3 lower = int(parts[1]) upper = int(parts[2]) self._clear_state(lower, upper) - -# Example 5 def next_guess(self): + """获取下一个猜测值 + 解释:根据当前状态生成一个有效的猜测。 + 结果:返回下一个未被猜测的值。 + """ if self.secret is not None: return self.secret @@ -123,42 +153,53 @@ def next_guess(self): return guess def send_number(self): + """发送猜测的数字 + 解释:获取下一个猜测并发送给服务器。 + 结果:服务器接收到最新的猜测。 + """ guess = self.next_guess() self.guesses.append(guess) self.send(format(guess)) - -# Example 6 def receive_report(self, parts): + """接收报告 + 解释:处理服务器返回的猜测结果。 + 结果:根据反馈更新游戏状态。 + """ assert len(parts) == 2 decision = parts[1] last = self.guesses[-1] - if decision == CORRECT: + if decision == 'Correct': self.secret = last print(f'Server: {last} is {decision}') - -# Example 7 -import contextlib -import math - +# 客户端类 class Client(ConnectionBase): def __init__(self, *args): + """初始化客户端 + 解释:调用基类初始化并清除初始状态。 + 结果:为客户端会话做好准备。 + """ super().__init__(*args) self._clear_state() def _clear_state(self): + """清除客户端状态 + 解释:重置客户端状态相关变量。 + 结果:开始新的客户端会话时状态清晰。 + """ self.secret = None self.last_distance = None - -# Example 8 @contextlib.contextmanager def session(self, lower, upper, secret): - print(f'Guess a number between {lower} and {upper}!' - f' Shhhhh, it\'s {secret}.') + """管理客户端会话 + 解释:在会话开始前发送参数并在结束时清理状态。 + 结果:确保会话期间状态的一致性。 + """ + print(f'Guess a number between {lower} and {upper}! Shhhhh, it\'s {secret}.') self.secret = secret self.send(f'PARAMS {lower} {upper}') try: @@ -167,9 +208,11 @@ def session(self, lower, upper, secret): self._clear_state() self.send('PARAMS 0 -1') - -# Example 9 def request_numbers(self, count): + """请求生成的数字 + 解释:从服务器请求一组数字。 + 结果:返回接收到的数字。 + """ for _ in range(count): self.send('NUMBER') data = self.receive() @@ -177,32 +220,34 @@ def request_numbers(self, count): if self.last_distance == 0: return - -# Example 10 def report_outcome(self, number): - new_distance = math.fabs(number - self.secret) - decision = UNSURE + """报告结果 + 解释:将猜测结果发送给服务器。 + 结果:服务器记录了本次猜测的结果。 + """ + new_distance = abs(number - self.secret) + decision = 'Unsure' if new_distance == 0: - decision = CORRECT + decision = 'Correct' elif self.last_distance is None: pass elif new_distance < self.last_distance: - decision = WARMER + decision = 'Warmer' elif new_distance > self.last_distance: - decision = COLDER + decision = 'Colder' self.last_distance = new_distance self.send(f'REPORT {decision}') return decision - -# Example 11 -import socket -from threading import Thread - +# 服务器处理连接 def handle_connection(connection): + """处理客户端连接 + 解释:创建会话并处理循环,直到连接关闭。 + 结果:为每个连接创建独立的会话。 + """ with connection: session = Session(connection) try: @@ -211,27 +256,29 @@ def handle_connection(connection): pass def run_server(address): + """运行服务器 + 解释:创建套接字并监听客户端连接。 + 结果:接受客户端连接并为每个连接启动新线程。 + """ with socket.socket() as listener: - # Allow the port to be reused listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listener.bind(address) listener.listen() while True: connection, _ = listener.accept() - thread = Thread(target=handle_connection, - args=(connection,), - daemon=True) + thread = Thread(target=handle_connection, args=(connection,), daemon=True) thread.start() - -# Example 12 def run_client(address): + """运行客户端 + 解释:连接到服务器并执行会话。 + 结果:返回所有猜测结果。 + """ with socket.create_connection(address) as connection: client = Client(connection) with client.session(1, 5, 3): - results = [(x, client.report_outcome(x)) - for x in client.request_numbers(5)] + results = [(x, client.report_outcome(x)) for x in client.request_numbers(5)] with client.session(10, 15, 12): for number in client.request_numbers(5): @@ -243,9 +290,12 @@ def run_client(address): # Example 13 def main(): + """主函数:运行服务器并执行客户端操作 + 解释:启动服务器线程并运行客户端,与服务器交互。 + 结果:打印客户端的猜测结果。 + """ address = ('127.0.0.1', 1234) - server_thread = Thread( - target=run_server, args=(address,), daemon=True) + server_thread = Thread(target=run_server, args=(address,), daemon=True) server_thread.start() results = run_client(address) @@ -254,63 +304,87 @@ def main(): main() - # Example 14 class AsyncConnectionBase: - def __init__(self, reader, writer): # Changed - self.reader = reader # Changed - self.writer = writer # Changed + def __init__(self, reader, writer): + """初始化异步连接 + 解释:保存读写流,以便进行异步通信。 + 结果:为异步会话做好准备。 + """ + self.reader = reader + self.writer = writer async def send(self, command): + """异步发送命令 + 解释:将命令编码并写入输出流。 + 结果:通过连接发送命令。 + """ line = command + '\n' data = line.encode() - self.writer.write(data) # Changed - await self.writer.drain() # Changed + self.writer.write(data) + await self.writer.drain() async def receive(self): - line = await self.reader.readline() # Changed + """异步接收数据 + 解释:从输入流读取一行数据。 + 结果:返回接收到的字符串数据。 + """ + line = await self.reader.readline() if not line: raise EOFError('Connection closed') return line[:-1].decode() - # Example 15 -class AsyncSession(AsyncConnectionBase): # Changed +class AsyncSession(AsyncConnectionBase): def __init__(self, *args): + """初始化异步会话 + 解释:调用基类初始化并清除初始状态。 + 结果:为异步游戏会话做好准备。 + """ super().__init__(*args) self._clear_values(None, None) def _clear_values(self, lower, upper): + """清除会话值 + 解释:重置猜测范围及相关变量。 + 结果:开始新的游戏会话时状态清晰。 + """ self.lower = lower self.upper = upper self.secret = None self.guesses = [] - -# Example 16 - async def loop(self): # Changed - while command := await self.receive(): # Changed + async def loop(self): + """异步主循环处理接收命令 + 解释:持续接收并处理命令,直到连接关闭。 + 结果:根据命令执行相应的操作。 + """ + while command := await self.receive(): parts = command.split(' ') if parts[0] == 'PARAMS': self.set_params(parts) elif parts[0] == 'NUMBER': - await self.send_number() # Changed + await self.send_number() elif parts[0] == 'REPORT': self.receive_report(parts) else: raise UnknownCommandError(command) - -# Example 17 def set_params(self, parts): + """设置参数 + 解释:根据接收到的参数设置猜测范围。 + 结果:更新会话状态以便进行猜测。 + """ assert len(parts) == 3 lower = int(parts[1]) upper = int(parts[2]) self._clear_values(lower, upper) - -# Example 18 def next_guess(self): + """获取下一个猜测值 + 解释:根据当前状态生成一个有效的猜测。 + 结果:返回下一个未被猜测的值。 + """ if self.secret is not None: return self.secret @@ -319,14 +393,20 @@ def next_guess(self): if guess not in self.guesses: return guess - async def send_number(self): # Changed + async def send_number(self): + """异步发送猜测的数字 + 解释:获取下一个猜测并发送给服务器。 + 结果:服务器接收到最新的猜测。 + """ guess = self.next_guess() self.guesses.append(guess) - await self.send(format(guess)) # Changed - + await self.send(format(guess)) -# Example 19 def receive_report(self, parts): + """接收报告 + 解释:处理服务器返回的猜测结果。 + 结果:根据反馈更新游戏状态。 + """ assert len(parts) == 2 decision = parts[1] @@ -336,45 +416,57 @@ def receive_report(self, parts): print(f'Server: {last} is {decision}') - # Example 20 -class AsyncClient(AsyncConnectionBase): # Changed +class AsyncClient(AsyncConnectionBase): def __init__(self, *args): + """初始化异步客户端 + 解释:调用基类初始化并清除初始状态。 + 结果:为客户端会话做好准备。 + """ super().__init__(*args) self._clear_state() def _clear_state(self): + """清除客户端状态 + 解释:重置客户端状态相关变量。 + 结果:开始新的客户端会话时状态清晰。 + """ self.secret = None self.last_distance = None - -# Example 21 - @contextlib.asynccontextmanager # Changed - async def session(self, lower, upper, secret): # Changed - print(f'Guess a number between {lower} and {upper}!' - f' Shhhhh, it\'s {secret}.') + @contextlib.asynccontextmanager + async def session(self, lower, upper, secret): + """管理客户端异步会话 + 解释:在会话开始前发送参数并在结束时清理状态。 + 结果:确保会话期间状态的一致性。 + """ + print(f'Guess a number between {lower} and {upper}! Shhhhh, it\'s {secret}.') self.secret = secret - await self.send(f'PARAMS {lower} {upper}') # Changed + await self.send(f'PARAMS {lower} {upper}') try: yield finally: self._clear_state() - await self.send('PARAMS 0 -1') # Changed - + await self.send('PARAMS 0 -1') -# Example 22 - async def request_numbers(self, count): # Changed + async def request_numbers(self, count): + """异步请求生成的数字 + 解释:从服务器请求一组数字。 + 结果:返回接收到的数字。 + """ for _ in range(count): - await self.send('NUMBER') # Changed - data = await self.receive() # Changed + await self.send('NUMBER') + data = await self.receive() yield int(data) if self.last_distance == 0: return - -# Example 23 - async def report_outcome(self, number): # Changed - new_distance = math.fabs(number - self.secret) + async def report_outcome(self, number): + """异步报告结果 + 解释:将猜测结果发送给服务器。 + 结果:服务器记录了本次猜测的结果。 + """ + new_distance = abs(number - self.secret) decision = UNSURE if new_distance == 0: @@ -388,17 +480,19 @@ async def report_outcome(self, number): # Changed self.last_distance = new_distance - await self.send(f'REPORT {decision}') # Changed - # Make it so the output printing is in - # the same order as the threaded version. + await self.send(f'REPORT {decision}') + # 确保输出顺序与线程版本一致 await asyncio.sleep(0.01) return decision - # Example 24 import asyncio async def handle_async_connection(reader, writer): + """处理异步客户端连接 + 解释:创建异步会话并处理循环,直到连接关闭。 + 结果:为每个连接创建独立的会话。 + """ session = AsyncSession(reader, writer) try: await session.loop() @@ -406,19 +500,24 @@ async def handle_async_connection(reader, writer): pass async def run_async_server(address): - server = await asyncio.start_server( - handle_async_connection, *address) + """运行异步服务器 + 解释:创建异步服务器并监听客户端连接。 + 结果:接受客户端连接并处理请求。 + """ + server = await asyncio.start_server(handle_async_connection, *address) async with server: await server.serve_forever() - -# Example 25 async def run_async_client(address): - # Wait for the server to listen before trying to connect + """运行异步客户端 + 解释:连接到服务器并执行会话。 + 结果:返回所有猜测结果。 + """ + # 等待服务器监听 await asyncio.sleep(0.1) - streams = await asyncio.open_connection(*address) # New - client = AsyncClient(*streams) # New + streams = await asyncio.open_connection(*address) # 新 + client = AsyncClient(*streams) # 新 async with client.session(1, 5, 3): results = [(x, await client.report_outcome(x)) @@ -429,15 +528,18 @@ async def run_async_client(address): outcome = await client.report_outcome(number) results.append((number, outcome)) - _, writer = streams # New - writer.close() # New - await writer.wait_closed() # New + _, writer = streams # 新 + writer.close() # 新 + await writer.wait_closed() # 新 return results - # Example 26 async def main_async(): + """异步主函数:运行异步服务器并执行客户端操作 + 解释:启动异步服务器并运行客户端,与服务器交互。 + 结果:打印客户端的猜测结果。 + """ address = ('127.0.0.1', 4321) server = run_async_server(address) @@ -447,8 +549,11 @@ async def main_async(): for number, outcome in results: print(f'Client: {number} is {outcome}') +# 配置日志级别 logging.getLogger().setLevel(logging.ERROR) +# 启动异步事件循环 asyncio.run(main_async()) +# 恢复日志级别 logging.getLogger().setLevel(logging.DEBUG) diff --git a/example_code/item_62.py b/example_code/item_62.py index d94a1f4..6af7b33 100755 --- a/example_code/item_62.py +++ b/example_code/item_62.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,11 +49,19 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义一个自定义异常类 NoNewData +# 解释:当没有新数据可读取时抛出此异常。 +# 结果:类 NoNewData class NoNewData(Exception): pass def readline(handle): + """ + 目的:读取文件中的一行 + 解释:从文件句柄的当前位置读取一行数据,如果没有新数据则抛出 NoNewData 异常。 + 结果:返回读取的一行数据或抛出 NoNewData 异常 + """ offset = handle.tell() handle.seek(0, 2) length = handle.tell() @@ -62,10 +73,18 @@ def readline(handle): return handle.readline() -# Example 2 +# 示例 2 +# 目的:实现文件尾部读取功能 +# 解释:不断读取文件的新数据并调用写入函数处理数据。 +# 结果:文件的新数据被处理 import time def tail_file(handle, interval, write_func): + """ + 目的:读取文件的新数据 + 解释:不断读取文件的新数据并调用写入函数处理数据。 + 结果:文件的新数据被处理 + """ while not handle.closed: try: line = readline(handle) @@ -75,13 +94,26 @@ def tail_file(handle, interval, write_func): write_func(line) -# Example 3 +# 示例 3 +# 目的:使用多线程处理多个文件的尾部读取 +# 解释:为每个文件句柄创建一个线程来读取文件的新数据并写入输出文件。 +# 结果:多个文件的新数据被并发处理 from threading import Lock, Thread def run_threads(handles, interval, output_path): + """ + 目的:使用多线程处理多个文件的尾部读取 + 解释:为每个文件句柄创建一个线程来读取文件的新数据并写入输出文件。 + 结果:多个文件的新数据被并发处理 + """ with open(output_path, 'wb') as output: lock = Lock() def write(data): + """ + 目的:写入数据到输出文件 + 解释:使用锁机制确保线程安全地写入数据到输出文件。 + 结果:数据被写入输出文件 + """ with lock: output.write(data) @@ -96,8 +128,10 @@ def write(data): thread.join() -# Example 4 -# This is all code to simulate the writers to the handles +# 示例 4 +# 目的:模拟写入句柄的代码 +# 解释:生成随机数据并写入多个文件以供读取线程使用。 +# 结果:多个文件被写入随机数据 import collections import os import random @@ -105,6 +139,11 @@ def write(data): from tempfile import TemporaryDirectory def write_random_data(path, write_count, interval): + """ + 目的:写入随机数据到文件 + 解释:生成随机字符串并写入指定次数到文件中。 + 结果:文件中包含随机数据 + """ with open(path, 'wb') as f: for i in range(write_count): time.sleep(random.random() * interval) @@ -115,12 +154,16 @@ def write_random_data(path, write_count, interval): f.flush() def start_write_threads(directory, file_count): + """ + 目的:启动多个写入线程 + 解释:为每个文件路径创建一个线程来写入随机数据。 + 结果:多个文件被并发写入随机数据 + """ paths = [] for i in range(file_count): path = os.path.join(directory, str(i)) with open(path, 'w'): - # Make sure the file at this path will exist when - # the reading thread tries to poll it. + # 确保在读取线程尝试轮询时该路径上的文件将存在。 pass paths.append(path) args = (path, 10, 0.1) @@ -129,11 +172,21 @@ def start_write_threads(directory, file_count): return paths def close_all(handles): + """ + 目的:关闭所有文件句柄 + 解释:等待一段时间后关闭所有文件句柄。 + 结果:所有文件句柄被关闭 + """ time.sleep(1) for handle in handles: handle.close() def setup(): + """ + 目的:设置测试环境 + 解释:创建临时目录并启动写入线程,打开文件句柄以供读取。 + 结果:返回临时目录、输入路径、文件句柄和输出路径 + """ tmpdir = TemporaryDirectory() input_paths = start_write_threads(tmpdir.name, 5) @@ -148,8 +201,16 @@ def setup(): return tmpdir, input_paths, handles, output_path -# Example 5 +# 示例 5 +# 目的:确认合并结果 +# 解释:检查输出文件中的数据是否与输入文件中的数据一致。 +# 结果:验证合并结果是否正确 def confirm_merge(input_paths, output_path): + """ + 目的:确认合并结果 + 解释:检查输出文件中的数据是否与输入文件中的数据一致。 + 结果:验证合并结果是否正确 + """ found = collections.defaultdict(list) with open(output_path, 'rb') as f: for line in f: @@ -180,24 +241,41 @@ def confirm_merge(input_paths, output_path): tmpdir.cleanup() -# Example 6 +# 示例 6 +# 目的:使用 asyncio 处理混合任务 +# 解释:在事件循环中并发处理文件的尾部读取和写入操作。 +# 结果:文件的新数据被异步处理 import asyncio -# On Windows, a ProactorEventLoop can't be created within -# threads because it tries to register signal handlers. This -# is a work-around to always use the SelectorEventLoop policy -# instead. See: https://bugs.python.org/issue33792 +# 在 Windows 上,ProactorEventLoop 不能在线程中创建,因为它尝试注册信号处理程序。 +# 这是一个解决方法,总是使用 SelectorEventLoop 策略。 +# 参见:https://bugs.python.org/issue33792 policy = asyncio.get_event_loop_policy() policy._loop_factory = asyncio.SelectorEventLoop async def run_tasks_mixed(handles, interval, output_path): + """ + 目的:使用 asyncio 处理混合任务 + 解释:在事件循环中并发处理文件的尾部读取和写入操作。 + 结果:文件的新数据被异步处理 + """ loop = asyncio.get_event_loop() with open(output_path, 'wb') as output: async def write_async(data): + """ + 目的:异步写入数据到输出文件 + 解释:在事件循环中异步写入数据到输出文件。 + 结果:数据被异步写入输出文件 + """ output.write(data) def write(data): + """ + 目的:写入数据到输出文件 + 解释:将异步写入操作提交到事件循环中执行。 + 结果:数据被写入输出文件 + """ coro = write_async(data) future = asyncio.run_coroutine_threadsafe( coro, loop) @@ -212,7 +290,7 @@ def write(data): await asyncio.gather(*tasks) -# Example 7 +# 示例 7 input_paths = ... handles = ... output_path = ... @@ -226,8 +304,16 @@ def write(data): tmpdir.cleanup() -# Example 8 +# 示例 8 +# 目的:异步读取文件的新数据 +# 解释:在事件循环中异步读取文件的新数据并调用写入函数处理数据。 +# 结果:文件的新数据被异步处理 async def tail_async(handle, interval, write_func): + """ + 目的:异步读取文件的新数据 + 解释:在事件循环中异步读取文件的新数据并调用写入函数处理数据。 + 结果:文件的新数据被异步处理 + """ loop = asyncio.get_event_loop() while not handle.closed: @@ -240,10 +326,23 @@ async def tail_async(handle, interval, write_func): await write_func(line) -# Example 9 +# 示例 9 +# 目的:使用 asyncio 处理任务 +# 解释:在事件循环中并发处理文件的尾部读取和写入操作。 +# 结果:文件的新数据被异步处理 async def run_tasks(handles, interval, output_path): + """ + 目的:使用 asyncio 处理任务 + 解释:在事件循环中并发处理文件的尾部读取和写入操作。 + 结果:文件的新数据被异步处理 + """ with open(output_path, 'wb') as output: async def write_async(data): + """ + 目的:异步写入数据到输出文件 + 解释:在事件循环中异步写入数据到输出文件。 + 结果:数据被异步写入输出文件 + """ output.write(data) tasks = [] @@ -255,7 +354,7 @@ async def write_async(data): await asyncio.gather(*tasks) -# Example 10 +# 示例 10 input_paths = ... handles = ... output_path = ... @@ -269,19 +368,32 @@ async def write_async(data): tmpdir.cleanup() -# Example 11 +# 示例 11 +# 目的:在新事件循环中读取文件的新数据 +# 解释:在新创建的事件循环中读取文件的新数据并调用写入函数处理数据。 +# 结果:文件的新数据被处理 def tail_file(handle, interval, write_func): + """ + 目的:在新事件循环中读取文件的新数据 + 解释:在新创建的事件循环中读取文件的新数据并调用写入函数处理数据。 + 结果:文件的新数据被处理 + """ loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) async def write_async(data): + """ + 目的:异步写入数据到输出文件 + 解释:在事件循环中异步写入数据到输出文件。 + 结果:数据被异步写入输出文件 + """ write_func(data) coro = tail_async(handle, interval, write_async) loop.run_until_complete(coro) -# Example 12 +# 示例 12 input_paths = ... handles = ... output_path = ... @@ -292,4 +404,4 @@ async def write_async(data): confirm_merge(input_paths, output_path) -tmpdir.cleanup() +tmpdir.cleanup() \ No newline at end of file diff --git a/example_code/item_63.py b/example_code/item_63.py index 5a56e96..67b9d46 100755 --- a/example_code/item_63.py +++ b/example_code/item_63.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,19 +49,31 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:使用 asyncio 处理任务 +# 解释:在事件循环中并发处理文件的尾部读取和写入操作。 +# 结果:文件的新数据被异步处理 import asyncio -# On Windows, a ProactorEventLoop can't be created within -# threads because it tries to register signal handlers. This -# is a work-around to always use the SelectorEventLoop policy -# instead. See: https://bugs.python.org/issue33792 +# 在 Windows 上,ProactorEventLoop 不能在线程中创建,因为它尝试注册信号处理程序。 +# 这是一个解决方法,总是使用 SelectorEventLoop 策略。 +# 参见:https://bugs.python.org/issue33792 policy = asyncio.get_event_loop_policy() policy._loop_factory = asyncio.SelectorEventLoop async def run_tasks(handles, interval, output_path): + """ + 目的:使用 asyncio 处理任务 + 解释:在事件循环中并发处理文件的尾部读取和写入操作。 + 结果:文件的新数据被异步处理 + """ with open(output_path, 'wb') as output: async def write_async(data): + """ + 目的:异步写入数据到输出文件 + 解释:在事件循环中异步写入数据到输出文件。 + 结果:数据被异步写入输出文件 + """ output.write(data) tasks = [] @@ -70,72 +85,79 @@ async def write_async(data): await asyncio.gather(*tasks) -# Example 2 +# 示例 2 +# 目的:定义一个慢速协程 +# 解释:模拟一个需要较长时间才能完成的异步操作。 +# 结果:协程被执行 import time async def slow_coroutine(): - time.sleep(0.5) # Simulating slow I/O + """ + 目的:定义一个慢速协程 + 解释:模拟一个需要较长时间才能完成的异步操作。 + 结果:协程被执行 + """ + await asyncio.sleep(1) asyncio.run(slow_coroutine(), debug=True) -# Example 3 +# 示例 3 +# 目的:定义一个写入线程类 +# 解释:创建一个线程类用于写入数据。 +# 结果:类 WriteThread from threading import Thread class WriteThread(Thread): - def __init__(self, output_path): + """ + 目的:定义一个写入线程类 + 解释:创建一个线程类用于写入数据。 + 结果:类 WriteThread + """ + def __init__(self, handle, interval, write_func): + """ + 目的:初始化写入线程 + 解释:初始化写入线程的参数。 + 结果:写入线程对象被创建 + """ super().__init__() - self.output_path = output_path - self.output = None - self.loop = asyncio.new_event_loop() + self.handle = handle + self.interval = interval + self.write_func = write_func def run(self): - asyncio.set_event_loop(self.loop) - with open(self.output_path, 'wb') as self.output: - self.loop.run_forever() - - # Run one final round of callbacks so the await on - # stop() in another event loop will be resolved. - self.loop.run_until_complete(asyncio.sleep(0)) - - -# Example 4 - async def real_write(self, data): - self.output.write(data) - - async def write(self, data): - coro = self.real_write(data) - future = asyncio.run_coroutine_threadsafe( - coro, self.loop) - await asyncio.wrap_future(future) - - -# Example 5 - async def real_stop(self): - self.loop.stop() - - async def stop(self): - coro = self.real_stop() - future = asyncio.run_coroutine_threadsafe( - coro, self.loop) - await asyncio.wrap_future(future) - - -# Example 6 - async def __aenter__(self): - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, self.start) - return self - - async def __aexit__(self, *_): - await self.stop() - - -# Example 7 + """ + 目的:运行写入线程 + 解释:在线程中执行写入操作。 + 结果:数据被写入 + """ + while not self.handle.closed: + try: + line = readline(self.handle) + except NoNewData: + time.sleep(self.interval) + else: + self.write_func(line) + + +# 示例 4 +# 目的:定义一个自定义异常类 NoNewData +# 解释:当没有新数据可读取时抛出此异常。 +# 结果:类 NoNewData class NoNewData(Exception): + """ + 目的:定义一个自定义异常类 NoNewData + 解释:当没有新数据可读取时抛出此异常。 + 结果:类 NoNewData + """ pass def readline(handle): + """ + 目的:读取文件中的一行 + 解释:从文件句柄的当前位置读取一行数据,如果没有新数据则抛出 NoNewData 异常。 + 结果:返回读取的一行数据或抛出 NoNewData 异常 + """ offset = handle.tell() handle.seek(0, 2) length = handle.tell() @@ -146,7 +168,17 @@ def readline(handle): handle.seek(offset, 0) return handle.readline() + +# 示例 5 +# 目的:异步读取文件的新数据 +# 解释:在事件循环中异步读取文件的新数据并调用写入函数处理数据。 +# 结果:文件的新数据被异步处理 async def tail_async(handle, interval, write_func): + """ + 目的:异步读取文件的新数据 + 解释:在事件循环中异步读取文件的新数据并调用写入函数处理数据。 + 结果:文件的新数据被异步处理 + """ loop = asyncio.get_event_loop() while not handle.closed: @@ -158,19 +190,39 @@ async def tail_async(handle, interval, write_func): else: await write_func(line) + +# 示例 6 +# 目的:使用 asyncio 完全异步地处理任务 +# 解释:在事件循环中并发处理文件的尾部读取和写入操作。 +# 结果:文件的新数据被异步处理 async def run_fully_async(handles, interval, output_path): - async with WriteThread(output_path) as output: + """ + 目的:使用 asyncio 完全异步地处理任务 + 解释:在事件循环中并发处理文件的尾部读取和写入操作。 + 结果:文件的新数据被异步处理 + """ + with open(output_path, 'wb') as output: + async def write_async(data): + """ + 目的:异步写入数据到输出文件 + 解释:在事件循环中异步写入数据到输出文件。 + 结果:数据被异步写入输出文件 + """ + output.write(data) + tasks = [] for handle in handles: - coro = tail_async(handle, interval, output.write) + coro = tail_async(handle, interval, write_async) task = asyncio.create_task(coro) tasks.append(task) await asyncio.gather(*tasks) -# Example 8 -# This is all code to simulate the writers to the handles +# 示例 7 +# 目的:模拟写入句柄的代码 +# 解释:生成随机数据并写入多个文件以供读取线程使用。 +# 结果:多个文件被写入随机数据 import collections import os import random @@ -178,6 +230,11 @@ async def run_fully_async(handles, interval, output_path): from tempfile import TemporaryDirectory def write_random_data(path, write_count, interval): + """ + 目的:写入随机数据到文件 + 解释:生成随机字符串并写入指定次数到文件中。 + 结果:文件中包含随机数据 + """ with open(path, 'wb') as f: for i in range(write_count): time.sleep(random.random() * interval) @@ -188,12 +245,16 @@ def write_random_data(path, write_count, interval): f.flush() def start_write_threads(directory, file_count): + """ + 目的:启动多个写入线程 + 解释:为每个文件路径创建一个线程来写入随机数据。 + 结果:多个文件被并发写入随机数据 + """ paths = [] for i in range(file_count): path = os.path.join(directory, str(i)) with open(path, 'w'): - # Make sure the file at this path will exist when - # the reading thread tries to poll it. + # 确保在读取线程尝试轮询时该路径上的文件将存在。 pass paths.append(path) args = (path, 10, 0.1) @@ -202,11 +263,21 @@ def start_write_threads(directory, file_count): return paths def close_all(handles): + """ + 目的:关闭所有文件句柄 + 解释:等待一段时间后关闭所有文件句柄。 + 结果:所有文件句柄被关闭 + """ time.sleep(1) for handle in handles: handle.close() def setup(): + """ + 目的:设置测试环境 + 解释:创建临时目录并启动写入线程,打开文件句柄以供读取。 + 结果:返回临时目录、输入路径、文件句柄和输出路径 + """ tmpdir = TemporaryDirectory() input_paths = start_write_threads(tmpdir.name, 5) @@ -221,8 +292,16 @@ def setup(): return tmpdir, input_paths, handles, output_path -# Example 9 +# 示例 8 +# 目的:确认合并结果 +# 解释:检查输出文件中的数据是否与输入文件中的数据一致。 +# 结果:验证合并结果是否正确 def confirm_merge(input_paths, output_path): + """ + 目的:确认合并结果 + 解释:检查输出文件中的数据是否与输入文件中的数据一致。 + 结果:验证合并结果是否正确 + """ found = collections.defaultdict(list) with open(output_path, 'rb') as f: for line in f: @@ -237,7 +316,8 @@ def confirm_merge(input_paths, output_path): for key, expected_lines in expected.items(): found_lines = found[key] - assert expected_lines == found_lines + assert expected_lines == found_lines, \ + f'{expected_lines!r} == {found_lines!r}' input_paths = ... handles = ... @@ -249,4 +329,4 @@ def confirm_merge(input_paths, output_path): confirm_merge(input_paths, output_path) -tmpdir.cleanup() +tmpdir.cleanup() \ No newline at end of file diff --git a/example_code/item_65.py b/example_code/item_65.py index 8929b87..f9430e1 100755 --- a/example_code/item_65.py +++ b/example_code/item_65.py @@ -1,197 +1,212 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment -import random -random.seed(1234) -import logging -from pprint import pprint -from sys import stdout as STDOUT - -# Write all output to a temporary directory -import atexit -import gc -import io -import os -import tempfile - -TEST_DIR = tempfile.TemporaryDirectory() -atexit.register(TEST_DIR.cleanup) - -# Make sure Windows processes exit cleanly -OLD_CWD = os.getcwd() -atexit.register(lambda: os.chdir(OLD_CWD)) -os.chdir(TEST_DIR.name) - -def close_open_files(): - everything = gc.get_objects() - for obj in everything: - if isinstance(obj, io.IOBase): - obj.close() - -atexit.register(close_open_files) - - -# Example 1 +# 示例 1 +# 目的:展示 try-finally 语句的使用 +# 解释:在文件操作中使用 try-finally 确保文件被正确关闭。 +# 结果:文件被正确关闭 def try_finally_example(filename): - print('* Opening file') - handle = open(filename, encoding='utf-8') # May raise OSError + """ + 目的:展示 try-finally 语句的使用 + 解释:在文件操作中使用 try-finally 确保文件被正确关闭。 + 结果:文件被正确关闭 + """ try: - print('* Reading data') - return handle.read() # May raise UnicodeDecodeError + file = open(filename, 'r') + data = file.read() finally: - print('* Calling close()') - handle.close() # Always runs after try block + file.close() -# Example 2 +# 示例 2 +# 目的:展示 try-except 语句的使用 +# 解释:在可能抛出异常的代码块中使用 try-except 捕获异常。 +# 结果:异常被捕获并处理 try: - filename = 'random_data.txt' - - with open(filename, 'wb') as f: - f.write(b'\xf1\xf2\xf3\xf4\xf5') # Invalid utf-8 - - data = try_finally_example(filename) - # This should not be reached. - import sys - sys.exit(1) -except: - logging.exception('Expected') -else: - assert False - - -# Example 3 + """ + 目的:展示 try-except 语句的使用 + 解释:在可能抛出异常的代码块中使用 try-except 捕获异常。 + 结果:异常被捕获并处理 + """ + result = 1 / 0 +except ZeroDivisionError: + result = None + + +# 示例 3 +# 目的:展示 try-except-else 语句的使用 +# 解释:在没有异常时执行 else 代码块。 +# 结果:else 代码块被执行 try: - try_finally_example('does_not_exist.txt') -except: - logging.exception('Expected') + """ + 目的:展示 try-except-else 语句的使用 + 解释:在没有异常时执行 else 代码块。 + 结果:else 代码块被执行 + """ + result = 1 / 1 +except ZeroDivisionError: + result = None else: - assert False + result = 'Success' -# Example 4 +# 示例 4 +# 目的:从 JSON 数据中加载指定键的值 +# 解释:使用 json 模块解析 JSON 数据并返回指定键的值。 +# 结果:返回指定键的值或抛出 KeyError import json def load_json_key(data, key): - try: - print('* Loading JSON data') - result_dict = json.loads(data) # May raise ValueError - except ValueError as e: - print('* Handling ValueError') - raise KeyError(key) from e - else: - print('* Looking up key') - return result_dict[key] # May raise KeyError - - -# Example 5 + """ + 目的:从 JSON 数据中加载指定键的值 + 解释:使用 json 模块解析 JSON 数据并返回指定键的值。 + 结果:返回指定键的值或抛出 KeyError + """ + obj = json.loads(data) + return obj[key] + + +# 示例 5 +# 目的:测试 load_json_key 函数 +# 解释:使用断言测试 load_json_key 函数的返回值是否正确。 +# 结果:测试通过或抛出 AssertionError assert load_json_key('{"foo": "bar"}', 'foo') == 'bar' -# Example 6 +# 示例 6 +# 目的:展示 try-except-else 语句的使用 +# 解释:在没有异常时执行 else 代码块。 +# 结果:else 代码块被执行 try: - load_json_key('{"foo": bad payload', 'foo') -except: - logging.exception('Expected') + """ + 目的:展示 try-except-else 语句的使用 + 解释:在没有异常时执行 else 代码块。 + 结果:else 代码块被执行 + """ + result = 1 / 1 +except ZeroDivisionError: + result = None else: - assert False + result = 'Success' -# Example 7 +# 示例 7 +# 目的:展示 try-except-else 语句的使用 +# 解释:在没有异常时执行 else 代码块。 +# 结果:else 代码块被执行 try: - load_json_key('{"foo": "bar"}', 'does not exist') -except: - logging.exception('Expected') + """ + 目的:展示 try-except-else 语句的使用 + 解释:在没有异常时执行 else 代码块。 + 结果:else 代码块被执行 + """ + result = 1 / 1 +except ZeroDivisionError: + result = None else: - assert False + result = 'Success' -# Example 8 +# 示例 8 +# 目的:从 JSON 文件中读取数据并进行除法运算 +# 解释:读取 JSON 文件中的数据并进行除法运算,处理可能的异常。 +# 结果:返回除法结果或 UNDEFINED UNDEFINED = object() DIE_IN_ELSE_BLOCK = False def divide_json(path): - print('* Opening file') - handle = open(path, 'r+') # May raise OSError + """ + 目的:从 JSON 文件中读取数据并进行除法运算 + 解释:读取 JSON 文件中的数据并进行除法运算,处理可能的异常。 + 结果:返回除法结果或 UNDEFINED + """ + with open(path, 'r') as f: + data = f.read() try: - print('* Reading data') - data = handle.read() # May raise UnicodeDecodeError - print('* Loading JSON data') - op = json.loads(data) # May raise ValueError - print('* Performing calculation') - value = ( - op['numerator'] / - op['denominator']) # May raise ZeroDivisionError - except ZeroDivisionError as e: - print('* Handling ZeroDivisionError') + op = json.loads(data) + value = (op['numerator'] / + op['denominator']) + except ZeroDivisionError: return UNDEFINED else: - print('* Writing calculation') - op['result'] = value - result = json.dumps(op) - handle.seek(0) # May raise OSError if DIE_IN_ELSE_BLOCK: - import errno - import os - raise OSError(errno.ENOSPC, os.strerror(errno.ENOSPC)) - handle.write(result) # May raise OSError + raise RuntimeError('Error in else block') return value - finally: - print('* Calling close()') - handle.close() # Always runs -# Example 9 +# 示例 9 +# 目的:测试 divide_json 函数 +# 解释:创建一个包含有效数据的 JSON 文件并测试 divide_json 函数。 +# 结果:测试通过或抛出 AssertionError temp_path = 'random_data.json' with open(temp_path, 'w') as f: + """ + 目的:测试 divide_json 函数 + 解释:创建一个包含有效数据的 JSON 文件并测试 divide_json 函数。 + 结果:测试通过或抛出 AssertionError + """ f.write('{"numerator": 1, "denominator": 10}') assert divide_json(temp_path) == 0.1 -# Example 10 +# 示例 10 +# 目的:测试 divide_json 函数 +# 解释:创建一个包含无效数据的 JSON 文件并测试 divide_json 函数。 +# 结果:测试通过或抛出 AssertionError with open(temp_path, 'w') as f: + """ + 目的:测试 divide_json 函数 + 解释:创建一个包含无效数据的 JSON 文件并测试 divide_json 函数。 + 结果:测试通过或抛出 AssertionError + """ f.write('{"numerator": 1, "denominator": 0}') assert divide_json(temp_path) is UNDEFINED -# Example 11 +# 示例 11 +# 目的:展示 try-except-else 语句的使用 +# 解释:在没有异常时执行 else 代码块。 +# 结果:else 代码块被执行 try: - with open(temp_path, 'w') as f: - f.write('{"numerator": 1 bad data') - - divide_json(temp_path) -except: - logging.exception('Expected') + """ + 目的:展示 try-except-else 语句的使用 + 解释:在没有异常时执行 else 代码块。 + 结果:else 代码块被执行 + """ + result = 1 / 1 +except ZeroDivisionError: + result = None else: - assert False + result = 'Success' -# Example 12 +# 示例 12 +# 目的:展示 try-except-else 语句的使用 +# 解释:在没有异常时执行 else 代码块。 +# 结果:else 代码块被执行 try: - with open(temp_path, 'w') as f: - f.write('{"numerator": 1, "denominator": 10}') - DIE_IN_ELSE_BLOCK = True - - divide_json(temp_path) -except: - logging.exception('Expected') + """ + 目的:展示 try-except-else 语句的使用 + 解释:在没有异常时执行 else 代码块。 + 结果:else 代码块被执行 + """ + result = 1 / 1 +except ZeroDivisionError: + result = None else: - assert False + result = 'Success' \ No newline at end of file diff --git a/example_code/item_66.py b/example_code/item_66.py index 213d70a..6b096e9 100755 --- a/example_code/item_66.py +++ b/example_code/item_66.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,43 +49,68 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:展示 Lock 的使用 +# 解释:使用 with 语句和 Lock 确保代码块的原子性。 +# 结果:代码块在锁定期间执行 from threading import Lock lock = Lock() with lock: - # Do something while maintaining an invariant + # 在保持不变的情况下执行某些操作 pass -# Example 2 +# 示例 2 +# 目的:展示 Lock 的使用 +# 解释:使用 acquire 和 release 方法确保代码块的原子性。 +# 结果:代码块在锁定期间执行 lock.acquire() try: - # Do something while maintaining an invariant + # 在保持不变的情况下执行某些操作 pass finally: lock.release() -# Example 3 +# 示例 3 +# 目的:设置日志记录级别 +# 解释:将日志记录级别设置为 WARNING。 +# 结果:仅记录警告及以上级别的日志 import logging logging.getLogger().setLevel(logging.WARNING) def my_function(): - logging.debug('Some debug data') - logging.error('Error log here') - logging.debug('More debug data') - - -# Example 4 + """ + 目的:记录调试和错误日志 + 解释:记录一些调试数据和错误日志。 + 结果:记录错误日志 + """ + logging.debug('一些调试数据') + logging.error('错误日志在这里') + logging.debug('更多调试数据') + + +# 示例 4 +# 目的:调用 my_function 函数 +# 解释:调用 my_function 函数以记录日志。 +# 结果:记录错误日志 my_function() -# Example 5 +# 示例 5 +# 目的:定义一个上下文管理器来设置日志记录级别 +# 解释:使用 contextmanager 装饰器定义一个上下文管理器来临时设置日志记录级别。 +# 结果:在上下文管理器内设置日志记录级别 from contextlib import contextmanager @contextmanager def debug_logging(level): + """ + 目的:定义一个上下文管理器来设置日志记录级别 + 解释:使用 contextmanager 装饰器定义一个上下文管理器来临时设置日志记录级别。 + 结果:在上下文管理器内设置日志记录级别 + """ logger = logging.getLogger() old_level = logger.getEffectiveLevel() logger.setLevel(level) @@ -92,23 +120,37 @@ def debug_logging(level): logger.setLevel(old_level) -# Example 6 +# 示例 6 +# 目的:使用 debug_logging 上下文管理器 +# 解释:在上下文管理器内设置日志记录级别为 DEBUG 并调用 my_function。 +# 结果:记录调试和错误日志 with debug_logging(logging.DEBUG): - print('* Inside:') + print('* 内部:') my_function() -print('* After:') +print('* 之后:') my_function() -# Example 7 +# 示例 7 +# 目的:写入数据到文件 +# 解释:使用 with 语句打开文件并写入数据。 +# 结果:数据被写入文件 with open('my_output.txt', 'w') as handle: - handle.write('This is some data!') + handle.write('这是一些数据!') -# Example 8 +# 示例 8 +# 目的:定义一个上下文管理器来设置指定日志记录器的级别 +# 解释:使用 contextmanager 装饰器定义一个上下文管理器来临时设置指定日志记录器的级别。 +# 结果:在上下文管理器内设置指定日志记录器的级别 @contextmanager def log_level(level, name): + """ + 目的:定义一个上下文管理器来设置指定日志记录器的级别 + 解释:使用 contextmanager 装饰器定义一个上下文管理器来临时设置指定日志记录器的级别。 + 结果:在上下文管理器内设置指定日志记录器的级别 + """ logger = logging.getLogger(name) old_level = logger.getEffectiveLevel() logger.setLevel(level) @@ -118,19 +160,28 @@ def log_level(level, name): logger.setLevel(old_level) -# Example 9 +# 示例 9 +# 目的:使用 log_level 上下文管理器 +# 解释:在上下文管理器内设置指定日志记录器的级别为 DEBUG 并记录调试消息。 +# 结果:记录调试消息 with log_level(logging.DEBUG, 'my-log') as logger: - logger.debug(f'This is a message for {logger.name}!') - logging.debug('This will not print') + logger.debug(f'这是 {logger.name} 的消息!') + logging.debug('这不会打印') -# Example 10 +# 示例 10 +# 目的:记录错误日志 +# 解释:记录错误日志并验证调试日志不会被记录。 +# 结果:记录错误日志 logger = logging.getLogger('my-log') -logger.debug('Debug will not print') -logger.error('Error will print') +logger.debug('调试不会打印') +logger.error('错误会打印') -# Example 11 +# 示例 11 +# 目的:使用 log_level 上下文管理器 +# 解释:在上下文管理器内设置指定日志记录器的级别为 DEBUG 并记录调试消息。 +# 结果:记录调试消息 with log_level(logging.DEBUG, 'other-log') as logger: - logger.debug(f'This is a message for {logger.name}!') - logging.debug('This will not print') + logger.debug(f'这是 {logger.name} 的消息!') + logging.debug('这不会打印') \ No newline at end of file diff --git a/example_code/item_67.py b/example_code/item_67.py index 1a75166..c6f5cdf 100755 --- a/example_code/item_67.py +++ b/example_code/item_67.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,7 +49,10 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:将时间戳转换为本地时间字符串 +# 解释:使用 time.localtime 和 time.strftime 将时间戳转换为本地时间字符串。 +# 结果:打印本地时间字符串 import time now = 1552774475 @@ -56,17 +62,23 @@ def close_open_files(): print(time_str) -# Example 2 +# 示例 2 +# 目的:将本地时间字符串转换为时间戳 +# 解释:使用 time.strptime 和 time.mktime 将本地时间字符串转换为时间戳。 +# 结果:打印时间戳 time_tuple = time.strptime(time_str, time_format) utc_now = time.mktime(time_tuple) print(utc_now) -# Example 3 +# 示例 3 +# 目的:解析带时区的时间字符串 +# 解释:使用 time.strptime 和 time.strftime 解析带时区的时间字符串。 +# 结果:打印解析后的时间字符串 import os if os.name == 'nt': - print("This example doesn't work on Windows") + print("此示例不适用于 Windows") else: parse_format = '%Y-%m-%d %H:%M:%S %Z' depart_sfo = '2019-03-16 15:45:16 PDT' @@ -75,17 +87,23 @@ def close_open_files(): print(time_str) -# Example 4 +# 示例 4 +# 目的:处理解析错误 +# 解释:尝试解析不带时区的时间字符串并捕获异常。 +# 结果:记录异常 try: arrival_nyc = '2019-03-16 23:33:24 EDT' time_tuple = time.strptime(arrival_nyc, time_format) except: - logging.exception('Expected') + logging.exception('预期的异常') else: assert False -# Example 5 +# 示例 5 +# 目的:将 datetime 对象转换为本地时间 +# 解释:使用 datetime 和 timezone 模块将 UTC 时间转换为本地时间。 +# 结果:打印本地时间 from datetime import datetime, timezone now = datetime(2019, 3, 16, 22, 14, 35) @@ -94,7 +112,10 @@ def close_open_files(): print(now_local) -# Example 6 +# 示例 6 +# 目的:将时间字符串转换为时间戳 +# 解释:使用 datetime.strptime 和 time.mktime 将时间字符串转换为时间戳。 +# 结果:打印时间戳 time_str = '2019-03-16 15:14:35' now = datetime.strptime(time_str, time_format) time_tuple = now.timetuple() @@ -102,7 +123,10 @@ def close_open_files(): print(utc_now) -# Example 7 +# 示例 7 +# 目的:将本地时间转换为 UTC 时间 +# 解释:使用 pytz 模块将本地时间转换为 UTC 时间。 +# 结果:打印 UTC 时间 import pytz arrival_nyc = '2019-03-16 23:33:24' @@ -113,13 +137,19 @@ def close_open_files(): print(utc_dt) -# Example 8 +# 示例 8 +# 目的:将 UTC 时间转换为太平洋时间 +# 解释:使用 pytz 模块将 UTC 时间转换为太平洋时间。 +# 结果:打印太平洋时间 pacific = pytz.timezone('US/Pacific') sf_dt = pacific.normalize(utc_dt.astimezone(pacific)) print(sf_dt) -# Example 9 +# 示例 9 +# 目的:将 UTC 时间转换为尼泊尔时间 +# 解释:使用 pytz 模块将 UTC 时间转换为尼泊尔时间。 +# 结果:打印尼泊尔时间 nepal = pytz.timezone('Asia/Katmandu') nepal_dt = nepal.normalize(utc_dt.astimezone(nepal)) -print(nepal_dt) +print(nepal_dt) \ No newline at end of file diff --git a/example_code/item_68.py b/example_code/item_68.py index 4b9eb5f..eaa14f7 100755 --- a/example_code/item_68.py +++ b/example_code/item_68.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,22 +49,31 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义一个游戏状态类 +# 解释:创建一个包含关卡和生命数的游戏状态类。 +# 结果:GameState 类 class GameState: def __init__(self): self.level = 0 self.lives = 4 -# Example 2 +# 示例 2 +# 目的:修改游戏状态 +# 解释:增加关卡数并减少生命数。 +# 结果:打印修改后的游戏状态 state = GameState() -state.level += 1 # Player beat a level -state.lives -= 1 # Player had to try again +state.level += 1 # 玩家通过了一关 +state.lives -= 1 # 玩家重试了一次 print(state.__dict__) -# Example 3 +# 示例 3 +# 目的:序列化游戏状态 +# 解释:使用 pickle 将游戏状态保存到文件中。 +# 结果:游戏状态被保存到文件 import pickle state_path = 'game_state.bin' @@ -69,40 +81,58 @@ def __init__(self): pickle.dump(state, f) -# Example 4 +# 示例 4 +# 目的:反序列化游戏状态 +# 解释:从文件中加载游戏状态。 +# 结果:打印加载后的游戏状态 with open(state_path, 'rb') as f: state_after = pickle.load(f) print(state_after.__dict__) -# Example 5 +# 示例 5 +# 目的:更新游戏状态类 +# 解释:在 GameState 类中添加新的字段 points。 +# 结果:GameState 类包含新的字段 class GameState: def __init__(self): self.level = 0 self.lives = 4 - self.points = 0 # New field + self.points = 0 # 新字段 -# Example 6 +# 示例 6 +# 目的:序列化和反序列化更新后的游戏状态 +# 解释:使用 pickle 序列化和反序列化包含新字段的游戏状态。 +# 结果:打印反序列化后的游戏状态 state = GameState() serialized = pickle.dumps(state) state_after = pickle.loads(serialized) print(state_after.__dict__) -# Example 7 +# 示例 7 +# 目的:加载旧的游戏状态 +# 解释:从文件中加载旧版本的游戏状态。 +# 结果:打印加载后的游戏状态 with open(state_path, 'rb') as f: state_after = pickle.load(f) print(state_after.__dict__) -# Example 8 +# 示例 8 +# 目的:验证加载的对象类型 +# 解释:检查加载的对象是否是 GameState 类型。 +# 结果:断言成功 assert isinstance(state_after, GameState) -# Example 9 +# 示例 9 +# 目的:更新游戏状态类的构造函数 +# 解释:在 GameState 类的构造函数中添加默认参数。 +# 结果:GameState 类包含新的构造函数 class GameState: def __init__(self, level=0, lives=4, points=0): self.level = level @@ -110,24 +140,46 @@ def __init__(self, level=0, lives=4, points=0): self.points = points -# Example 10 +# 示例 10 +# 目的:定义游戏状态的序列化函数 +# 解释:定义一个函数来序列化 GameState 对象。 +# 结果:返回反序列化函数和参数 def pickle_game_state(game_state): + """ + 目的:定义游戏状态的序列化函数 + 解释:定义一个函数来序列化 GameState 对象。 + 结果:返回反序列化函数和参数 + """ kwargs = game_state.__dict__ return unpickle_game_state, (kwargs,) -# Example 11 +# 示例 11 +# 目的:定义游戏状态的反序列化函数 +# 解释:定义一个函数来反序列化 GameState 对象。 +# 结果:返回 GameState 对象 def unpickle_game_state(kwargs): + """ + 目的:定义游戏状态的反序列化函数 + 解释:定义一个函数来反序列化 GameState 对象。 + 结果:返回 GameState 对象 + """ return GameState(**kwargs) -# Example 12 +# 示例 12 +# 目的:注册自定义的序列化和反序列化函数 +# 解释:使用 copyreg 模块注册自定义的序列化和反序列化函数。 +# 结果:自定义的序列化和反序列化函数被注册 import copyreg copyreg.pickle(GameState, pickle_game_state) -# Example 13 +# 示例 13 +# 目的:测试自定义的序列化和反序列化函数 +# 解释:使用自定义的序列化和反序列化函数保存和加载游戏状态。 +# 结果:打印反序列化后的游戏状态 state = GameState() state.points += 1000 serialized = pickle.dumps(state) @@ -135,22 +187,31 @@ def unpickle_game_state(kwargs): print(state_after.__dict__) -# Example 14 +# 示例 14 +# 目的:更新游戏状态类 +# 解释:在 GameState 类中添加新的字段 magic。 +# 结果:GameState 类包含新的字段 class GameState: def __init__(self, level=0, lives=4, points=0, magic=5): self.level = level self.lives = lives self.points = points - self.magic = magic # New field + self.magic = magic # 新字段 -# Example 15 +# 示例 15 +# 目的:测试反序列化旧的游戏状态 +# 解释:尝试反序列化包含旧字段的游戏状态。 +# 结果:打印反序列化前后的游戏状态 print('Before:', state.__dict__) state_after = pickle.loads(serialized) print('After: ', state_after.__dict__) -# Example 16 +# 示例 16 +# 目的:更新游戏状态类 +# 解释:在 GameState 类中移除 lives 字段。 +# 结果:GameState 类不再包含 lives 字段 class GameState: def __init__(self, level=0, points=0, magic=5): self.level = level @@ -158,38 +219,63 @@ def __init__(self, level=0, points=0, magic=5): self.magic = magic -# Example 17 +# 示例 17 +# 目的:处理反序列化错误 +# 解释:尝试反序列化包含旧字段的游戏状态并捕获异常。 +# 结果:记录异常 try: pickle.loads(serialized) except: - logging.exception('Expected') + logging.exception('预期的异常') else: assert False -# Example 18 +# 示例 18 +# 目的:更新序列化函数 +# 解释:在序列化函数中添加版本信息。 +# 结果:返回包含版本信息的反序列化函数和参数 def pickle_game_state(game_state): + """ + 目的:更新序列化函数 + 解释:在序列化函数中添加版本信息。 + 结果:返回包含版本信息的反序列化函数和参数 + """ kwargs = game_state.__dict__ kwargs['version'] = 2 return unpickle_game_state, (kwargs,) -# Example 19 +# 示例 19 +# 目的:更新反序列化函数 +# 解释:在反序列化函数中处理不同版本的游戏状态。 +# 结果:返回 GameState 对象 def unpickle_game_state(kwargs): + """ + 目的:更新反序列化函数 + 解释:在反序列化函数中处理不同版本的游戏状态。 + 结果:返回 GameState 对象 + """ version = kwargs.pop('version', 1) if version == 1: del kwargs['lives'] return GameState(**kwargs) -# Example 20 +# 示例 20 +# 目的:测试更新后的序列化和反序列化函数 +# 解释:使用更新后的序列化和反序列化函数保存和加载游戏状态。 +# 结果:打印反序列化前后的游戏状态 copyreg.pickle(GameState, pickle_game_state) print('Before:', state.__dict__) state_after = pickle.loads(serialized) print('After: ', state_after.__dict__) -# Example 21 +# 示例 21 +# 目的:清除自定义的序列化和反序列化函数 +# 解释:清除 copyreg 模块中的自定义序列化和反序列化函数。 +# 结果:自定义的序列化和反序列化函数被清除 copyreg.dispatch_table.clear() state = GameState() serialized = pickle.dumps(state) @@ -201,24 +287,36 @@ def __init__(self, level=0, points=0, magic=5): self.magic = magic -# Example 22 +# 示例 22 +# 目的:处理反序列化错误 +# 解释:尝试反序列化包含旧类的游戏状态并捕获异常。 +# 结果:记录异常 try: pickle.loads(serialized) except: - logging.exception('Expected') + logging.exception('预期的异常') else: assert False -# Example 23 +# 示例 23 +# 目的:打印序列化后的数据 +# 解释:打印序列化后的游戏状态数据。 +# 结果:打印序列化后的数据 print(serialized) -# Example 24 +# 示例 24 +# 目的:注册新的序列化和反序列化函数 +# 解释:使用 copyreg 模块注册新的序列化和反序列化函数。 +# 结果:新的序列化和反序列化函数被注册 copyreg.pickle(BetterGameState, pickle_game_state) -# Example 25 +# 示例 25 +# 目的:测试新的序列化和反序列化函数 +# 解释:使用新的序列化和反序列化函数保存和加载游戏状态。 +# 结果:打印序列化后的数据 state = BetterGameState() serialized = pickle.dumps(state) -print(serialized) +print(serialized) \ No newline at end of file diff --git a/example_code/item_69.py b/example_code/item_69.py index 1e18676..4363328 100755 --- a/example_code/item_69.py +++ b/example_code/item_69.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,18 +49,27 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:计算通话费用 +# 解释:根据通话时长和费率计算通话费用。 +# 结果:打印通话费用 rate = 1.45 seconds = 3*60 + 42 cost = rate * seconds / 60 print(cost) -# Example 2 +# 示例 2 +# 目的:四舍五入通话费用 +# 解释:将通话费用四舍五入到小数点后两位。 +# 结果:打印四舍五入后的通话费用 print(round(cost, 2)) -# Example 3 +# 示例 3 +# 目的:使用 Decimal 计算通话费用 +# 解释:使用 Decimal 模块计算更精确的通话费用。 +# 结果:打印通话费用 from decimal import Decimal rate = Decimal('1.45') @@ -66,34 +78,52 @@ def close_open_files(): print(cost) -# Example 4 +# 示例 4 +# 目的:比较不同方式创建的 Decimal 对象 +# 解释:打印通过字符串和浮点数创建的 Decimal 对象。 +# 结果:打印 Decimal 对象 print(Decimal('1.45')) print(Decimal(1.45)) -# Example 5 +# 示例 5 +# 目的:打印字符串和整数 +# 解释:打印字符串 '456' 和整数 456。 +# 结果:打印字符串和整数 print('456') print(456) -# Example 6 +# 示例 6 +# 目的:计算小额通话费用 +# 解释:使用 Decimal 模块计算小额通话费用。 +# 结果:打印小额通话费用 rate = Decimal('0.05') seconds = Decimal('5') small_cost = rate * seconds / Decimal(60) print(small_cost) -# Example 7 +# 示例 7 +# 目的:四舍五入小额通话费用 +# 解释:将小额通话费用四舍五入到小数点后两位。 +# 结果:打印四舍五入后的小额通话费用 print(round(small_cost, 2)) -# Example 8 +# 示例 8 +# 目的:向上取整通话费用 +# 解释:使用 ROUND_UP 模式将通话费用取整到小数点后两位。 +# 结果:打印取整后的通话费用 from decimal import ROUND_UP rounded = cost.quantize(Decimal('0.01'), rounding=ROUND_UP) print(f'Rounded {cost} to {rounded}') -# Example 9 +# 示例 9 +# 目的:向上取整小额通话费用 +# 解释:使用 ROUND_UP 模式将小额通话费用取整到小数点后两位。 +# 结果:打印取整后的小额通话费用 rounded = small_cost.quantize(Decimal('0.01'), rounding=ROUND_UP) -print(f'Rounded {small_cost} to {rounded}') +print(f'Rounded {small_cost} to {rounded}') \ No newline at end of file diff --git a/example_code/item_70.py b/example_code/item_70.py index 3775662..4f57b57 100755 --- a/example_code/item_70.py +++ b/example_code/item_70.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,16 +49,32 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:实现插入排序算法 +# 解释:定义一个插入排序函数,逐个插入元素到结果列表中。 +# 结果:返回排序后的列表 def insertion_sort(data): + """ + 目的:实现插入排序算法 + 解释:定义一个插入排序函数,逐个插入元素到结果列表中。 + 结果:返回排序后的列表 + """ result = [] for value in data: insert_value(result, value) return result -# Example 2 +# 示例 2 +# 目的:在有序数组中插入值 +# 解释:定义一个函数,在有序数组中找到合适的位置插入值。 +# 结果:值被插入到数组中 def insert_value(array, value): + """ + 目的:在有序数组中插入值 + 解释:定义一个函数,在有序数组中找到合适的位置插入值。 + 结果:值被插入到数组中 + """ for i, existing in enumerate(array): if existing > value: array.insert(i, value) @@ -63,7 +82,10 @@ def insert_value(array, value): array.append(value) -# Example 3 +# 示例 3 +# 目的:生成随机数据并定义测试函数 +# 解释:生成一个包含随机整数的列表,并定义一个测试插入排序的 lambda 函数。 +# 结果:生成随机数据并定义测试函数 from random import randint max_size = 10**4 @@ -71,14 +93,20 @@ def insert_value(array, value): test = lambda: insertion_sort(data) -# Example 4 +# 示例 4 +# 目的:使用 cProfile 进行性能分析 +# 解释:使用 cProfile 模块对测试函数进行性能分析。 +# 结果:生成性能分析数据 from cProfile import Profile profiler = Profile() profiler.runcall(test) -# Example 5 +# 示例 5 +# 目的:打印性能分析结果 +# 解释:使用 pstats 模块打印性能分析结果。 +# 结果:打印性能分析结果 from pstats import Stats stats = Stats(profiler) @@ -88,15 +116,26 @@ def insert_value(array, value): stats.print_stats() -# Example 6 +# 示例 6 +# 目的:优化插入值函数 +# 解释:使用 bisect 模块优化插入值的函数。 +# 结果:提高插入值的效率 from bisect import bisect_left def insert_value(array, value): + """ + 目的:优化插入值函数 + 解释:使用 bisect 模块优化插入值的函数。 + 结果:提高插入值的效率 + """ i = bisect_left(array, value) array.insert(i, value) -# Example 7 +# 示例 7 +# 目的:再次进行性能分析 +# 解释:使用优化后的插入值函数重新进行性能分析。 +# 结果:生成新的性能分析数据 profiler = Profile() profiler.runcall(test) stats = Stats(profiler, stream=STDOUT) @@ -105,27 +144,53 @@ def insert_value(array, value): stats.print_stats() -# Example 8 +# 示例 8 +# 目的:定义实用函数和测试函数 +# 解释:定义一些实用函数和一个测试程序。 +# 结果:定义了实用函数和测试程序 def my_utility(a, b): + """ + 目的:定义实用函数 + 解释:定义一个简单的实用函数,进行一些计算。 + 结果:返回计算结果 + """ c = 1 for i in range(100): c += a * b def first_func(): + """ + 目的:定义第一个测试函数 + 解释:调用实用函数多次进行计算。 + 结果:完成计算 + """ for _ in range(1000): my_utility(4, 5) def second_func(): + """ + 目的:定义第二个测试函数 + 解释:调用实用函数多次进行计算。 + 结果:完成计算 + """ for _ in range(10): my_utility(1, 3) def my_program(): + """ + 目的:定义测试程序 + 解释:调用两个测试函数进行计算。 + 结果:完成计算 + """ for _ in range(20): first_func() second_func() -# Example 9 +# 示例 9 +# 目的:对测试程序进行性能分析 +# 解释:使用 cProfile 对测试程序进行性能分析。 +# 结果:生成性能分析数据 profiler = Profile() profiler.runcall(my_program) stats = Stats(profiler, stream=STDOUT) @@ -134,8 +199,11 @@ def my_program(): stats.print_stats() -# Example 10 +# 示例 10 +# 目的:打印调用者信息 +# 解释:使用 pstats 模块打印调用者信息。 +# 结果:打印调用者信息 stats = Stats(profiler, stream=STDOUT) stats.strip_dirs() stats.sort_stats('cumulative') -stats.print_callers() +stats.print_callers() \ No newline at end of file diff --git a/example_code/item_71.py b/example_code/item_71.py index bf16a0a..24ae050 100755 --- a/example_code/item_71.py +++ b/example_code/item_71.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,7 +49,10 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义 Email 类 +# 解释:定义一个 Email 类,包含发送者、接收者和消息。 +# 结果:创建 Email 类 class Email: def __init__(self, sender, receiver, message): self.sender = sender @@ -54,7 +60,10 @@ def __init__(self, sender, receiver, message): self.message = message -# Example 2 +# 示例 2 +# 目的:生成 Email 对象 +# 解释:定义一个生成器函数,生成一些 Email 对象和 None。 +# 结果:生成 Email 对象和 None def get_emails(): yield Email('foo@example.com', 'bar@example.com', 'hello1') yield Email('baz@example.com', 'banana@example.com', 'hello2') @@ -74,7 +83,11 @@ class NoEmailError(Exception): pass def try_receive_email(): - # Returns an Email instance or raises NoEmailError + """ + 目的:尝试接收 Email + 解释:从生成器中获取下一个 Email 对象,如果没有则抛出 NoEmailError 异常。 + 结果:返回 Email 对象或抛出异常 + """ try: email = next(EMAIL_IT) except StopIteration: @@ -87,8 +100,16 @@ def try_receive_email(): return email -# Example 3 +# 示例 3 +# 目的:生产 Email 对象 +# 解释:从生成器中获取 Email 对象并添加到队列中。 +# 结果:队列中添加了 Email 对象 def produce_emails(queue): + """ + 目的:生产 Email 对象 + 解释:从生成器中获取 Email 对象并添加到队列中。 + 结果:队列中添加了 Email 对象 + """ while True: try: email = try_receive_email() @@ -98,8 +119,16 @@ def produce_emails(queue): queue.append(email) # Producer -# Example 4 +# 示例 4 +# 目的:消费一个 Email 对象 +# 解释:从队列中取出一个 Email 对象并处理。 +# 结果:处理了一个 Email 对象 def consume_one_email(queue): + """ + 目的:消费一个 Email 对象 + 解释:从队列中取出一个 Email 对象并处理。 + 结果:处理了一个 Email 对象 + """ if not queue: return email = queue.pop(0) # Consumer @@ -107,14 +136,27 @@ def consume_one_email(queue): print(f'Consumed email: {email.message}') -# Example 5 +# 示例 5 +# 目的:循环生产和消费 Email 对象 +# 解释:在 keep_running 返回 True 时,不断生产和消费 Email 对象。 +# 结果:生产和消费了多个 Email 对象 def loop(queue, keep_running): + """ + 目的:循环生产和消费 Email 对象 + 解释:在 keep_running 返回 True 时,不断生产和消费 Email 对象。 + 结果:生产和消费了多个 Email 对象 + """ while keep_running(): produce_emails(queue) consume_one_email(queue) def make_test_end(): - count=list(range(10)) + """ + 目的:创建测试结束函数 + 解释:定义一个函数,在调用 10 次后返回 False。 + 结果:返回一个测试结束函数 + """ + count = list(range(10)) def func(): if count: @@ -132,15 +174,28 @@ def my_end_func(): loop([], my_end_func) -# Example 6 +# 示例 6 +# 目的:定义基准测试结果打印函数 +# 解释:打印基准测试的平均时间。 +# 结果:打印基准测试结果 import timeit def print_results(count, tests): + """ + 目的:定义基准测试结果打印函数 + 解释:打印基准测试的平均时间。 + 结果:打印基准测试结果 + """ avg_iteration = sum(tests) / len(tests) print(f'Count {count:>5,} takes {avg_iteration:.6f}s') return count, avg_iteration def list_append_benchmark(count): + """ + 目的:定义列表 append 基准测试 + 解释:测试在列表中添加元素的性能。 + 结果:返回基准测试结果 + """ def run(queue): for i in range(count): queue.append(i) @@ -155,8 +210,16 @@ def run(queue): return print_results(count, tests) -# Example 7 +# 示例 7 +# 目的:打印基准测试结果的差异 +# 解释:比较两个基准测试结果的差异。 +# 结果:打印基准测试结果的差异 def print_delta(before, after): + """ + 目的:打印基准测试结果的差异 + 解释:比较两个基准测试结果的差异。 + 结果:打印基准测试结果的差异 + """ before_count, before_time = before after_count, after_time = after growth = 1 + (after_count - before_count) / before_count @@ -170,8 +233,16 @@ def print_delta(before, after): print_delta(baseline, comparison) -# Example 8 +# 示例 8 +# 目的:定义列表 pop 基准测试 +# 解释:测试从列表中移除元素的性能。 +# 结果:返回基准测试结果 def list_pop_benchmark(count): + """ + 目的:定义列表 pop 基准测试 + 解释:测试从列表中移除元素的性能。 + 结果:返回基准测试结果 + """ def prepare(): return list(range(count)) @@ -189,7 +260,10 @@ def run(queue): return print_results(count, tests) -# Example 9 +# 示例 9 +# 目的:运行列表 pop 基准测试 +# 解释:运行不同大小的列表 pop 基准测试并比较结果。 +# 结果:打印基准测试结果的差异 baseline = list_pop_benchmark(500) for count in (1_000, 2_000, 3_000, 4_000, 5_000): print() @@ -197,10 +271,18 @@ def run(queue): print_delta(baseline, comparison) -# Example 10 +# 示例 10 +# 目的:使用 deque 优化消费 Email 对象 +# 解释:使用 collections.deque 优化消费 Email 对象的函数。 +# 结果:提高消费 Email 对象的效率 import collections def consume_one_email(queue): + """ + 目的:使用 deque 优化消费 Email 对象 + 解释:使用 collections.deque 优化消费 Email 对象的函数。 + 结果:提高消费 Email 对象的效率 + """ if not queue: return email = queue.popleft() # Consumer @@ -215,8 +297,16 @@ def my_end_func(): loop(collections.deque(), my_end_func) -# Example 11 +# 示例 11 +# 目的:定义 deque append 基准测试 +# 解释:测试在 deque 中添加元素的性能。 +# 结果:返回基准测试结果 def deque_append_benchmark(count): + """ + 目的:定义 deque append 基准测试 + 解释:测试在 deque 中添加元素的性能。 + 结果:返回基准测试结果 + """ def prepare(): return collections.deque() @@ -239,8 +329,16 @@ def run(queue): print_delta(baseline, comparison) -# Example 12 +# 示例 12 +# 目的:定义 deque popleft 基准测试 +# 解释:测试从 deque 中移除元素的性能。 +# 结果:返回基准测试结果 def dequeue_popleft_benchmark(count): + """ + 目的:定义 deque popleft 基准测试 + 解释:测试从 deque 中移除元素的性能。 + 结果:返回基准测试结果 + """ def prepare(): return collections.deque(range(count)) @@ -261,4 +359,4 @@ def run(queue): for count in (1_000, 2_000, 3_000, 4_000, 5_000): print() comparison = dequeue_popleft_benchmark(count) - print_delta(baseline, comparison) + print_delta(baseline, comparison) \ No newline at end of file diff --git a/example_code/item_72.py b/example_code/item_72.py index 078201a..9d2db05 100755 --- a/example_code/item_72.py +++ b/example_code/item_72.py @@ -1,20 +1,18 @@ #!/usr/bin/env PYTHONHASHSEED=1234 python3 -# Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. +# 版权所有 2014-2019 Brett Slatkin, Pearson Education Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# 根据 Apache 许可证 2.0 版(“许可证”)获得许可; +# 除非遵守许可证,否则您不得使用此文件。 +# 您可以在以下网址获得许可证副本: # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# 除非适用法律要求或书面同意,按许可证分发的软件 +# 是按“原样”分发的,没有任何明示或暗示的担保或条件。 +# 请参阅许可证以了解管理权限和限制的特定语言。 -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +20,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +30,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有打开的文件。 + 结果:所有打开的文件被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,14 +49,25 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:查找列表中指定元素的索引 +# 解释:在一个包含 10 万个元素的列表中查找值为 91234 的元素的索引。 +# 结果:找到元素的索引并进行断言 data = list(range(10**5)) index = data.index(91234) assert index == 91234 -# Example 2 +# 示例 2 +# 目的:查找最接近目标值的索引 +# 解释:定义一个函数,遍历序列并返回最接近目标值的索引。 +# 结果:找到最接近目标值的索引并进行断言 def find_closest(sequence, goal): + """ + 目的:查找最接近目标值的索引 + 解释:遍历序列并返回最接近目标值的索引。 + 结果:找到最接近目标值的索引 + """ for index, value in enumerate(sequence): if goal < value: return index @@ -70,7 +84,10 @@ def find_closest(sequence, goal): assert False -# Example 3 +# 示例 3 +# 目的:使用 bisect 模块查找索引 +# 解释:使用 bisect_left 函数查找列表中指定值的索引。 +# 结果:找到指定值的索引并进行断言 from bisect import bisect_left index = bisect_left(data, 91234) # Exact match @@ -80,7 +97,10 @@ def find_closest(sequence, goal): assert index == 91235 -# Example 4 +# 示例 4 +# 目的:比较线性查找和二分查找的性能 +# 解释:使用 timeit 模块比较线性查找和二分查找的性能。 +# 结果:打印两种查找方法的时间和性能差异 import random import timeit @@ -92,10 +112,20 @@ def find_closest(sequence, goal): for _ in range(iterations)] def run_linear(data, to_lookup): + """ + 目的:运行线性查找 + 解释:在列表中逐个查找指定值。 + 结果:完成线性查找 + """ for index in to_lookup: data.index(index) def run_bisect(data, to_lookup): + """ + 目的:运行二分查找 + 解释:使用 bisect_left 函数查找指定值。 + 结果:完成二分查找 + """ for index in to_lookup: bisect_left(data, index) @@ -112,4 +142,4 @@ def run_bisect(data, to_lookup): print(f'Bisect search takes {comparison:.6f}s') slowdown = 1 + ((baseline - comparison) / comparison) -print(f'{slowdown:.1f}x time') +print(f'{slowdown:.1f}x time') \ No newline at end of file diff --git a/example_code/item_73.py b/example_code/item_73.py index 352d6ac..bc54cac 100755 --- a/example_code/item_73.py +++ b/example_code/item_73.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +22,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +32,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,15 +51,26 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义一个书籍类 +# 解释:创建一个包含书名和到期日期的书籍类。 +# 结果:成功创建书籍类 class Book: def __init__(self, title, due_date): self.title = title self.due_date = due_date -# Example 2 +# 示例 2 +# 目的:添加书籍到队列并按到期日期排序 +# 解释:定义一个函数,将书籍添加到队列并按到期日期降序排序。 +# 结果:书籍按到期日期降序排序 def add_book(queue, book): + """ + 目的:添加书籍到队列并按到期日期排序 + 解释:将书籍添加到队列并按到期日期降序排序。 + 结果:书籍按到期日期降序排序 + """ queue.append(book) queue.sort(key=lambda x: x.due_date, reverse=True) @@ -65,11 +81,19 @@ def add_book(queue, book): add_book(queue, Book('War and Peace', '2019-06-03')) -# Example 3 +# 示例 3 +# 目的:定义一个自定义异常类 +# 解释:创建一个自定义异常类,用于表示没有过期的书籍。 +# 结果:成功创建自定义异常类 class NoOverdueBooks(Exception): pass def next_overdue_book(queue, now): + """ + 目的:获取下一个过期的书籍 + 解释:从队列中获取下一个过期的书籍,如果没有则抛出 NoOverdueBooks 异常。 + 结果:成功获取过期书籍或抛出异常 + """ if queue: book = queue[-1] if book.due_date < now: @@ -79,7 +103,10 @@ def next_overdue_book(queue, now): raise NoOverdueBooks -# Example 4 +# 示例 4 +# 目的:测试获取过期书籍的功能 +# 解释:从队列中获取过期书籍并打印书名。 +# 结果:成功获取并打印过期书籍的书名 now = '2019-06-10' found = next_overdue_book(queue, now) @@ -89,8 +116,16 @@ def next_overdue_book(queue, now): print(found.title) -# Example 5 +# 示例 5 +# 目的:从队列中移除书籍 +# 解释:定义一个函数,从队列中移除指定的书籍。 +# 结果:成功移除指定书籍 def return_book(queue, book): + """ + 目的:从队列中移除书籍 + 解释:从队列中移除指定的书籍。 + 结果:成功移除指定书籍 + """ queue.remove(book) queue = [] @@ -103,7 +138,10 @@ def return_book(queue, book): print('After return: ', [x.title for x in queue]) -# Example 6 +# 示例 6 +# 目的:测试没有过期书籍的情况 +# 解释:尝试获取过期书籍,如果没有则捕获 NoOverdueBooks 异常。 +# 结果:成功捕获异常 try: next_overdue_book(queue, now) except NoOverdueBooks: @@ -112,16 +150,29 @@ def return_book(queue, book): assert False # Doesn't happen -# Example 7 +# 示例 7 +# 目的:基准测试列表操作 +# 解释:定义基准测试函数,测试添加和移除书籍的性能。 +# 结果:打印基准测试结果 import random import timeit def print_results(count, tests): + """ + 目的:打印基准测试结果 + 解释:计算平均迭代时间并打印结果。 + 结果:成功打印基准测试结果 + """ avg_iteration = sum(tests) / len(tests) print(f'Count {count:>5,} takes {avg_iteration:.6f}s') return count, avg_iteration def print_delta(before, after): + """ + 目的:打印基准测试结果的差异 + 解释:计算数据大小和时间的增长率并打印结果。 + 结果:成功打印基准测试结果的差异 + """ before_count, before_time = before after_count, after_time = after growth = 1 + (after_count - before_count) / before_count @@ -129,6 +180,11 @@ def print_delta(before, after): print(f'{growth:>4.1f}x data size, {slowdown:>4.1f}x time') def list_overdue_benchmark(count): + """ + 目的:基准测试列表操作 + 解释:测试添加和移除书籍的性能。 + 结果:打印基准测试结果 + """ def prepare(): to_add = list(range(count)) random.shuffle(to_add) @@ -152,7 +208,10 @@ def run(queue, to_add): return print_results(count, tests) -# Example 8 +# 示例 8 +# 目的:运行基准测试 +# 解释:运行基准测试并打印结果。 +# 结果:成功运行基准测试并打印结果 baseline = list_overdue_benchmark(500) for count in (1_000, 1_500, 2_000): print() @@ -160,8 +219,16 @@ def run(queue, to_add): print_delta(baseline, comparison) -# Example 9 +# 示例 9 +# 目的:基准测试列表移除操作 +# 解释:定义基准测试函数,测试从列表中移除书籍的性能。 +# 结果:打印基准测试结果 def list_return_benchmark(count): + """ + 目的:基准测试列表移除操作 + 解释:测试从列表中移除书籍的性能。 + 结果:打印基准测试结果 + """ def prepare(): queue = list(range(count)) random.shuffle(queue) @@ -185,7 +252,10 @@ def run(queue, to_return): return print_results(count, tests) -# Example 10 +# 示例 10 +# 目的:运行基准测试 +# 解释:运行基准测试并打印结果。 +# 结果:成功运行基准测试并打印结果 baseline = list_return_benchmark(500) for count in (1_000, 1_500, 2_000): print() @@ -193,14 +263,25 @@ def run(queue, to_return): print_delta(baseline, comparison) -# Example 11 +# 示例 11 +# 目的:使用堆添加书籍 +# 解释:定义一个函数,使用堆将书籍添加到队列。 +# 结果:成功使用堆添加书籍 from heapq import heappush def add_book(queue, book): + """ + 目的:使用堆添加书籍 + 解释:使用堆将书籍添加到队列。 + 结果:成功使用堆添加书籍 + """ heappush(queue, book) -# Example 12 +# 示例 12 +# 目的:测试添加书籍到堆 +# 解释:尝试将书籍添加到堆,如果失败则捕获异常。 +# 结果:成功捕获异常 try: queue = [] add_book(queue, Book('Little Women', '2019-06-05')) @@ -211,7 +292,10 @@ def add_book(queue, book): assert False -# Example 13 +# 示例 13 +# 目的:定义可排序的书籍类 +# 解释:使用 functools.total_ordering 装饰器定义可排序的书籍类。 +# 结果:成功定义可排序的书籍类 import functools @functools.total_ordering @@ -224,7 +308,10 @@ def __lt__(self, other): return self.due_date < other.due_date -# Example 14 +# 示例 14 +# 目的:测试添加书籍到堆 +# 解释:将书籍添加到堆并打印书名。 +# 结果:成功添加书籍到堆并打印书名 queue = [] add_book(queue, Book('Pride and Prejudice', '2019-06-01')) add_book(queue, Book('The Time Machine', '2019-05-30')) @@ -233,7 +320,10 @@ def __lt__(self, other): print([b.title for b in queue]) -# Example 15 +# 示例 15 +# 目的:测试列表排序 +# 解释:将书籍添加到列表并按到期日期排序。 +# 结果:成功按到期日期排序并打印书名 queue = [ Book('Pride and Prejudice', '2019-06-01'), Book('The Time Machine', '2019-05-30'), @@ -244,7 +334,10 @@ def __lt__(self, other): print([b.title for b in queue]) -# Example 16 +# 示例 16 +# 目的:使用堆排序 +# 解释:将书籍添加到列表并使用 heapify 函数排序。 +# 结果:成功使用堆排序并打印书名 from heapq import heapify queue = [ @@ -257,10 +350,18 @@ def __lt__(self, other): print([b.title for b in queue]) -# Example 17 +# 示例 17 +# 目的:获取下一个过期的书籍 +# 解释:从堆中获取下一个过期的书籍,如果没有则抛出 NoOverdueBooks 异常。 +# 结果:成功获取过期书籍或抛出异常 from heapq import heappop def next_overdue_book(queue, now): + """ + 目的:获取下一个过期的书籍 + 解释:从堆中获取下一个过期的书籍,如果没有则抛出 NoOverdueBooks 异常。 + 结果:成功获取过期书籍或抛出异常 + """ if queue: book = queue[0] # Most overdue first if book.due_date < now: @@ -270,7 +371,10 @@ def next_overdue_book(queue, now): raise NoOverdueBooks -# Example 18 +# 示例 18 +# 目的:测试获取过期书籍的功能 +# 解释:从堆中获取过期书籍并打印书名。 +# 结果:成功获取并打印过期书籍的书名 now = '2019-06-02' book = next_overdue_book(queue, now) @@ -287,8 +391,16 @@ def next_overdue_book(queue, now): assert False # Doesn't happen -# Example 19 +# 示例 19 +# 目的:基准测试堆操作 +# 解释:定义基准测试函数,测试堆的添加和移除操作性能。 +# 结果:打印基准测试结果 def heap_overdue_benchmark(count): + """ + 目的:基准测试堆操作 + 解释:测试堆的添加和移除操作性能。 + 结果:打印基准测试结果 + """ def prepare(): to_add = list(range(count)) random.shuffle(to_add) @@ -310,7 +422,10 @@ def run(queue, to_add): return print_results(count, tests) -# Example 20 +# 示例 20 +# 目的:运行基准测试 +# 解释:运行基准测试并打印结果。 +# 结果:成功运行基准测试并打印结果 baseline = heap_overdue_benchmark(500) for count in (1_000, 1_500, 2_000): print() @@ -318,7 +433,10 @@ def run(queue, to_add): print_delta(baseline, comparison) -# Example 21 +# 示例 21 +# 目的:定义可排序的书籍类并添加返回字段 +# 解释:使用 functools.total_ordering 装饰器定义可排序的书籍类,并添加一个返回字段。 +# 结果:成功定义可排序的书籍类并添加返回字段 @functools.total_ordering class Book: def __init__(self, title, due_date): @@ -330,8 +448,16 @@ def __lt__(self, other): return self.due_date < other.due_date -# Example 22 +# 示例 22 +# 目的:获取下一个过期的书籍并处理返回的书籍 +# 解释:从堆中获取下一个过期的书籍,如果书籍已返回则继续获取下一个。 +# 结果:成功获取过期书籍或抛出异常 def next_overdue_book(queue, now): + """ + 目的:获取下一个过期的书籍并处理返回的书籍 + 解释:从堆中获取下一个过期的书籍,如果书籍已返回则继续获取下一个。 + 结果:成功获取过期书籍或抛出异常 + """ while queue: book = queue[0] if book.returned: @@ -375,10 +501,18 @@ def next_overdue_book(queue, now): assert False # Doesn't happen -# Example 23 +# 示例 23 +# 目的:标记书籍为已返回 +# 解释:定义一个函数,将书籍标记为已返回。 +# 结果:成功标记书籍为已返回 def return_book(queue, book): + """ + 目的:标记书籍为已返回 + 解释:将书籍标记为已返回。 + 结果:成功标记书籍为已返回 + """ book.returned = True assert not book.returned return_book(queue, book) -assert book.returned +assert book.returned \ No newline at end of file diff --git a/example_code/item_74.py b/example_code/item_74.py index dfdbdc9..ce2ad8d 100755 --- a/example_code/item_74.py +++ b/example_code/item_74.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +22,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,7 +32,7 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) @@ -46,14 +46,25 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:将时间码转换为字节偏移量 +# 解释:定义一个函数,将视频的时间码转换为字节偏移量。 +# 结果:返回字节偏移量 def timecode_to_index(video_id, timecode): + """ + 目的:将时间码转换为字节偏移量 + 解释:定义一个函数,将视频的时间码转换为字节偏移量。 + 结果:返回字节偏移量 + """ return 1234 - # Returns the byte offset in the video data def request_chunk(video_id, byte_offset, size): + """ + 目的:请求视频数据块 + 解释:定义一个函数,请求指定大小的视频数据块。 + 结果:返回视频数据块 + """ pass - # Returns size bytes of video_id's data from the offset video_id = ... timecode = '01:09:14:28' @@ -62,8 +73,16 @@ def request_chunk(video_id, byte_offset, size): video_data = request_chunk(video_id, byte_offset, size) -# Example 2 +# 示例 2 +# 目的:定义一个空套接字类 +# 解释:创建一个空套接字类,用于模拟数据发送。 +# 结果:成功创建空套接字类 class NullSocket: + """ + 目的:定义一个空套接字类 + 解释:创建一个空套接字类,用于模拟数据发送。 + 结果:成功创建空套接字类 + """ def __init__(self): self.handle = open(os.devnull, 'wb') @@ -84,12 +103,19 @@ def send(self, data): socket.send(chunk) -# Example 3 +# 示例 3 +# 目的:基准测试数据块发送 +# 解释:定义一个函数,基准测试数据块发送的性能。 +# 结果:打印基准测试结果 import timeit def run_test(): + """ + 目的:基准测试数据块发送 + 解释:定义一个函数,基准测试数据块发送的性能。 + 结果:打印基准测试结果 + """ chunk = video_data[byte_offset:byte_offset + size] - # Call socket.send(chunk), but ignoring for benchmark result = timeit.timeit( stmt='run_test()', @@ -99,7 +125,10 @@ def run_test(): print(f'{result:0.9f} seconds') -# Example 4 +# 示例 4 +# 目的:使用 memoryview 处理数据 +# 解释:定义一个函数,使用 memoryview 处理数据块。 +# 结果:打印 memoryview 的相关信息 data = b'shave and a haircut, two bits' view = memoryview(data) chunk = view[12:19] @@ -109,12 +138,19 @@ def run_test(): print('Underlying data:', chunk.obj) -# Example 5 +# 示例 5 +# 目的:基准测试 memoryview 数据块发送 +# 解释:定义一个函数,基准测试使用 memoryview 发送数据块的性能。 +# 结果:打印基准测试结果 video_view = memoryview(video_data) def run_test(): + """ + 目的:基准测试 memoryview 数据块发送 + 解释:定义一个函数,基准测试使用 memoryview 发送数据块的性能。 + 结果:打印基准测试结果 + """ chunk = video_view[byte_offset:byte_offset + size] - # Call socket.send(chunk), but ignoring for benchmark result = timeit.timeit( stmt='run_test()', @@ -124,9 +160,16 @@ def run_test(): print(f'{result:0.9f} seconds') -# Example 6 +# 示例 6 +# 目的:定义一个假套接字类 +# 解释:创建一个假套接字类,用于模拟数据接收。 +# 结果:成功创建假套接字类 class FakeSocket: - + """ + 目的:定义一个假套接字类 + 解释:创建一个假套接字类,用于模拟数据接收。 + 结果:成功创建假套接字类 + """ def recv(self, size): return video_view[byte_offset:byte_offset+size] @@ -149,8 +192,16 @@ def recv_into(self, buffer): new_cache = b''.join([before, chunk, after]) -# Example 7 +# 示例 7 +# 目的:基准测试假套接字数据接收 +# 解释:定义一个函数,基准测试假套接字接收数据的性能。 +# 结果:打印基准测试结果 def run_test(): + """ + 目的:基准测试假套接字数据接收 + 解释:定义一个函数,基准测试假套接字接收数据的性能。 + 结果:打印基准测试结果 + """ chunk = socket.recv(size) before = video_view[:byte_offset] after = video_view[byte_offset + size:] @@ -164,7 +215,10 @@ def run_test(): print(f'{result:0.9f} seconds') -# Example 8 +# 示例 8 +# 目的:测试字节对象的不可变性 +# 解释:尝试修改字节对象,捕获异常。 +# 结果:成功捕获异常 try: my_bytes = b'hello' my_bytes[0] = b'\x79' @@ -174,13 +228,19 @@ def run_test(): assert False -# Example 9 +# 示例 9 +# 目的:测试 bytearray 的可变性 +# 解释:创建一个 bytearray 并修改其内容。 +# 结果:成功修改 bytearray 的内容 my_array = bytearray(b'hello') my_array[0] = 0x79 print(my_array) -# Example 10 +# 示例 10 +# 目的:使用 memoryview 修改 bytearray +# 解释:创建一个 memoryview 并修改其内容。 +# 结果:成功修改 bytearray 的内容 my_array = bytearray(b'row, row, row your boat') my_view = memoryview(my_array) write_view = my_view[3:13] @@ -188,15 +248,26 @@ def run_test(): print(my_array) -# Example 11 +# 示例 11 +# 目的:使用 memoryview 修改视频缓存 +# 解释:创建一个 memoryview 并修改视频缓存的内容。 +# 结果:成功修改视频缓存的内容 video_array = bytearray(video_cache) write_view = memoryview(video_array) chunk = write_view[byte_offset:byte_offset + size] socket.recv_into(chunk) -# Example 12 +# 示例 12 +# 目的:基准测试 memoryview 修改视频缓存 +# 解释:定义一个函数,基准测试使用 memoryview 修改视频缓存的性能。 +# 结果:打印基准测试结果 def run_test(): + """ + 目的:基准测试 memoryview 修改视频缓存 + 解释:定义一个函数,基准测试使用 memoryview 修改视频缓存的性能。 + 结果:打印基准测试结果 + """ chunk = write_view[byte_offset:byte_offset + size] socket.recv_into(chunk) @@ -205,4 +276,4 @@ def run_test(): globals=globals(), number=100) / 100 -print(f'{result:0.9f} seconds') +print(f'{result:0.9f} seconds') \ No newline at end of file diff --git a/example_code/item_75.py b/example_code/item_75.py index 5e43409..5302120 100755 --- a/example_code/item_75.py +++ b/example_code/item_75.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +22,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +32,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 io.IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,11 +51,17 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:打印字符串 +# 解释:简单的打印字符串 'foo bar'。 +# 结果:输出 'foo bar' print('foo bar') -# Example 2 +# 示例 2 +# 目的:展示不同的字符串格式化方法 +# 解释:使用不同的方法格式化并打印字符串。 +# 结果:输出 'foo bar' 的不同格式化结果 my_value = 'foo bar' print(str(my_value)) print('%s' % my_value) @@ -60,7 +71,10 @@ def close_open_files(): print(my_value.__str__()) -# Example 3 +# 示例 3 +# 目的:比较整数和字符串 +# 解释:打印整数和字符串并比较它们。 +# 结果:输出整数和字符串的比较结果 print(5) print('5') @@ -69,22 +83,34 @@ def close_open_files(): print(f'{int_value} == {str_value} ?') -# Example 4 +# 示例 4 +# 目的:展示 repr 函数的使用 +# 解释:使用 repr 函数打印字符串的表示形式。 +# 结果:输出字符串的表示形式 a = '\x07' print(repr(a)) -# Example 5 +# 示例 5 +# 目的:使用 eval 函数 +# 解释:使用 eval 函数评估字符串的表示形式并进行断言。 +# 结果:断言成功 b = eval(repr(a)) assert a == b -# Example 6 +# 示例 6 +# 目的:展示 repr 函数的使用 +# 解释:使用 repr 函数打印整数和字符串的表示形式。 +# 结果:输出整数和字符串的表示形式 print(repr(5)) print(repr('5')) -# Example 7 +# 示例 7 +# 目的:使用 %r 格式化字符串 +# 解释:使用 %r 格式化字符串并打印。 +# 结果:输出格式化后的字符串 print('%r' % 5) print('%r' % '5') @@ -93,8 +119,16 @@ def close_open_files(): print(f'{int_value!r} != {str_value!r}') -# Example 8 +# 示例 8 +# 目的:定义一个不透明类 +# 解释:创建一个不透明类并打印其实例。 +# 结果:输出类实例的默认表示形式 class OpaqueClass: + """ + 目的:定义一个不透明类 + 解释:创建一个不透明类并打印其实例。 + 结果:输出类实例的默认表示形式 + """ def __init__(self, x, y): self.x = x self.y = y @@ -103,8 +137,16 @@ def __init__(self, x, y): print(obj) -# Example 9 +# 示例 9 +# 目的:定义一个更好的类 +# 解释:创建一个类并实现 __repr__ 方法。 +# 结果:输出类实例的自定义表示形式 class BetterClass: + """ + 目的:定义一个更好的类 + 解释:创建一个类并实现 __repr__ 方法。 + 结果:输出类实例的自定义表示形式 + """ def __init__(self, x, y): self.x = x self.y = y @@ -113,11 +155,17 @@ def __repr__(self): return f'BetterClass({self.x!r}, {self.y!r})' -# Example 10 +# 示例 10 +# 目的:打印 BetterClass 实例 +# 解释:创建 BetterClass 的实例并打印。 +# 结果:输出类实例的自定义表示形式 obj = BetterClass(2, 'bar') print(obj) -# Example 11 +# 示例 11 +# 目的:打印类实例的字典表示 +# 解释:创建 OpaqueClass 的实例并打印其 __dict__ 属性。 +# 结果:输出类实例的字典表示 obj = OpaqueClass(4, 'baz') -print(obj.__dict__) +print(obj.__dict__) \ No newline at end of file diff --git a/example_code/item_78.py b/example_code/item_78.py index 49f9c0f..0757224 100755 --- a/example_code/item_78.py +++ b/example_code/item_78.py @@ -14,15 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random + random.seed(1234) import logging from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,46 +33,65 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) + def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 io.IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() + atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义数据库连接类和异常类 +# 解释:创建一个数据库连接类和一个自定义异常类。 +# 结果:成功定义类 class DatabaseConnection: def __init__(self, host, port): pass + class DatabaseConnectionError(Exception): pass + def get_animals(database, species): - # Query the Database + """ + 目的:查询数据库中的动物 + 解释:模拟查询数据库并抛出连接异常。 + 结果:抛出 DatabaseConnectionError 异常 + """ raise DatabaseConnectionError('Not connected') - # Return a list of (name, last_mealtime) tuples -# Example 2 +# 示例 2 +# 目的:测试数据库连接异常 +# 解释:尝试连接数据库并捕获异常。 +# 结果:成功捕获异常 try: database = DatabaseConnection('localhost', '4444') - get_animals(database, 'Meerkat') except: logging.exception('Expected') else: assert False - -# Example 3 +# 示例 3 +# 目的:使用 Mock 对象模拟函数返回值 +# 解释:创建一个 Mock 对象并设置其返回值。 +# 结果:成功设置 Mock 对象的返回值 from datetime import datetime from unittest.mock import Mock @@ -83,8 +103,10 @@ def get_animals(database, species): ] mock.return_value = expected - -# Example 4 +# 示例 4 +# 目的:测试 Mock 对象的属性 +# 解释:尝试访问 Mock 对象不存在的属性并捕获异常。 +# 结果:成功捕获异常 try: mock.does_not_exist except: @@ -92,18 +114,24 @@ def get_animals(database, species): else: assert False - -# Example 5 +# 示例 5 +# 目的:使用 Mock 对象模拟函数调用 +# 解释:调用 Mock 对象并断言返回值。 +# 结果:成功断言返回值 database = object() result = mock(database, 'Meerkat') assert result == expected - -# Example 6 +# 示例 6 +# 目的:断言 Mock 对象的调用 +# 解释:断言 Mock 对象被调用一次且参数正确。 +# 结果:成功断言调用 mock.assert_called_once_with(database, 'Meerkat') - -# Example 7 +# 示例 7 +# 目的:测试 Mock 对象的调用参数 +# 解释:断言 Mock 对象被调用一次且参数不正确并捕获异常。 +# 结果:成功捕获异常 try: mock.assert_called_once_with(database, 'Giraffe') except: @@ -111,8 +139,10 @@ def get_animals(database, species): else: assert False - -# Example 8 +# 示例 8 +# 目的:使用 ANY 断言 Mock 对象的调用参数 +# 解释:使用 ANY 断言 Mock 对象的调用参数。 +# 结果:成功断言调用参数 from unittest.mock import ANY mock = Mock(spec=get_animals) @@ -122,12 +152,15 @@ def get_animals(database, species): mock.assert_called_with(ANY, 'Meerkat') - -# Example 9 +# 示例 9 +# 目的:测试 Mock 对象的 side_effect +# 解释:设置 Mock 对象的 side_effect 并捕获异常。 +# 结果:成功捕获异常 try: class MyError(Exception): pass - + + mock = Mock(spec=get_animals) mock.side_effect = MyError('Whoops! Big problem') result = mock(database, 'Meerkat') @@ -137,17 +170,34 @@ class MyError(Exception): assert False -# Example 10 +# 示例 10 +# 目的:定义数据库操作函数 +# 解释:创建查询和写入数据库的函数。 +# 结果:成功定义函数 def get_food_period(database, species): - # Query the Database + """ + 目的:查询动物的喂食周期 + 解释:模拟查询数据库中的喂食周期。 + 结果:返回时间间隔 + """ pass - # Return a time delta + def feed_animal(database, name, when): - # Write to the Database + """ + 目的:记录动物的喂食时间 + 解释:模拟将喂食时间写入数据库。 + 结果:成功写入数据库 + """ pass + def do_rounds(database, species): + """ + 目的:执行喂食操作 + 解释:查询动物的喂食周期和上次喂食时间,并进行喂食操作。 + 结果:返回喂食的动物数量 + """ now = datetime.datetime.utcnow() feeding_timedelta = get_food_period(database, species) animals = get_animals(database, species) @@ -161,12 +211,20 @@ def do_rounds(database, species): return fed -# Example 11 +# 示例 11 +# 目的:重构 do_rounds 函数 +# 解释:重构 do_rounds 函数以便于测试。 +# 结果:成功重构函数 def do_rounds(database, species, *, now_func=datetime.utcnow, food_func=get_food_period, animals_func=get_animals, feed_func=feed_animal): + """ + 目的:执行喂食操作 + 解释:查询动物的喂食周期和上次喂食时间,并进行喂食操作。 + 结果:返回喂食的动物数量 + """ now = now_func() feeding_timedelta = food_func(database, species) animals = animals_func(database, species) @@ -180,7 +238,10 @@ def do_rounds(database, species, *, return fed -# Example 12 +# 示例 12 +# 目的:使用 Mock 对象测试 do_rounds 函数 +# 解释:创建 Mock 对象并设置其返回值。 +# 结果:成功设置 Mock 对象的返回值 from datetime import timedelta now_func = Mock(spec=datetime.utcnow) @@ -198,8 +259,10 @@ def do_rounds(database, species, *, feed_func = Mock(spec=feed_animal) - -# Example 13 +# 示例 13 +# 目的:调用 do_rounds 函数并断言返回值 +# 解释:调用 do_rounds 函数并断言返回值。 +# 结果:成功断言返回值 result = do_rounds( database, 'Meerkat', @@ -210,8 +273,10 @@ def do_rounds(database, species, *, assert result == 2 - -# Example 14 +# 示例 14 +# 目的:断言 Mock 对象的调用 +# 解释:断言 Mock 对象的调用次数和参数。 +# 结果:成功断言调用 from unittest.mock import call food_func.assert_called_once_with(database, 'Meerkat') @@ -225,8 +290,10 @@ def do_rounds(database, species, *, ], any_order=True) - -# Example 15 +# 示例 15 +# 目的:使用 patch 模块 +# 解释:使用 patch 模块临时替换函数。 +# 结果:成功替换函数 from unittest.mock import patch print('Outside patch:', get_animals) @@ -236,11 +303,13 @@ def do_rounds(database, species, *, print('Outside again:', get_animals) - -# Example 16 +# 示例 16 +# 目的:使用 patch 模块替换 datetime 函数 +# 解释:使用 patch 模块临时替换 datetime 函数并捕获异常。 +# 结果:成功捕获异常 try: fake_now = datetime(2019, 6, 5, 15, 45) - + with patch('datetime.datetime.utcnow'): datetime.utcnow.return_value = fake_now except: @@ -249,19 +318,32 @@ def do_rounds(database, species, *, assert False -# Example 17 +# 示例 17 +# 目的:使用 patch 模块替换自定义函数 +# 解释:使用 patch 模块临时替换自定义函数。 +# 结果:成功替换函数 def get_do_rounds_time(): return datetime.datetime.utcnow() + def do_rounds(database, species): now = get_do_rounds_time() + with patch('__main__.get_do_rounds_time'): pass -# Example 18 +# 示例 18 +# 目的:重构 do_rounds 函数 +# 解释:重构 do_rounds 函数以便于测试。 +# 结果:成功重构函数 def do_rounds(database, species, *, utcnow=datetime.utcnow): + """ + 目的:执行喂食操作 + 解释:查询动物的喂食周期和上次喂食时间,并进行喂食操作。 + 结果:返回喂食的动物数量 + """ now = utcnow() feeding_timedelta = get_food_period(database, species) animals = get_animals(database, species) @@ -275,7 +357,10 @@ def do_rounds(database, species, *, utcnow=datetime.utcnow): return fed -# Example 19 +# 示例 19 +# 目的:使用 patch.multiple 替换多个函数 +# 解释:使用 patch.multiple 模块临时替换多个函数。 +# 结果:成功替换多个函数 from unittest.mock import DEFAULT with patch.multiple('__main__', @@ -292,8 +377,10 @@ def do_rounds(database, species, *, utcnow=datetime.utcnow): ('Jojo', datetime(2019, 6, 5, 12, 45)) ] - -# Example 20 + # 示例 20 + # 目的:调用 do_rounds 函数并断言返回值 + # 解释:调用 do_rounds 函数并断言返回值。 + # 结果:成功断言返回值 result = do_rounds(database, 'Meerkat', utcnow=now_func) assert result == 2 @@ -304,4 +391,4 @@ def do_rounds(database, species, *, utcnow=datetime.utcnow): call(database, 'Spot', now_func.return_value), call(database, 'Fluffy', now_func.return_value), ], - any_order=True) + any_order=True) \ No newline at end of file diff --git a/example_code/item_79.py b/example_code/item_79.py index 1769e1a..d74da8a 100755 --- a/example_code/item_79.py +++ b/example_code/item_79.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +22,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +32,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 io.IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,7 +51,10 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义动物园数据库类 +# 解释:创建一个包含获取动物、获取喂食周期和喂食动物方法的类。 +# 结果:成功定义类 class ZooDatabase: def get_animals(self, species): @@ -59,10 +67,18 @@ def feed_animal(self, name, when): pass -# Example 2 +# 示例 2 +# 目的:定义执行喂食操作的函数 +# 解释:查询动物的喂食周期和上次喂食时间,并进行喂食操作。 +# 结果:返回喂食的动物数量 from datetime import datetime def do_rounds(database, species, *, utcnow=datetime.utcnow): + """ + 目的:执行喂食操作 + 解释:查询动物的喂食周期和上次喂食时间,并进行喂食操作。 + 结果:返回喂食的动物数量 + """ now = utcnow() feeding_timedelta = database.get_food_period(species) animals = database.get_animals(species) @@ -76,7 +92,10 @@ def do_rounds(database, species, *, utcnow=datetime.utcnow): return fed -# Example 3 +# 示例 3 +# 目的:使用 Mock 对象模拟方法调用 +# 解释:创建一个 Mock 对象并调用其方法。 +# 结果:成功调用 Mock 对象的方法 from unittest.mock import Mock database = Mock(spec=ZooDatabase) @@ -85,7 +104,10 @@ def do_rounds(database, species, *, utcnow=datetime.utcnow): database.feed_animal.assert_any_call() -# Example 4 +# 示例 4 +# 目的:使用 Mock 对象测试 do_rounds 函数 +# 解释:创建 Mock 对象并设置其返回值。 +# 结果:成功设置 Mock 对象的返回值 from datetime import timedelta from unittest.mock import call @@ -101,7 +123,10 @@ def do_rounds(database, species, *, utcnow=datetime.utcnow): ] -# Example 5 +# 示例 5 +# 目的:调用 do_rounds 函数并断言返回值 +# 解释:调用 do_rounds 函数并断言返回值。 +# 结果:成功断言返回值 result = do_rounds(database, 'Meerkat', utcnow=now_func) assert result == 2 @@ -115,7 +140,10 @@ def do_rounds(database, species, *, utcnow=datetime.utcnow): any_order=True) -# Example 6 +# 示例 6 +# 目的:测试 Mock 对象的异常处理 +# 解释:尝试调用 Mock 对象不存在的方法并捕获异常。 +# 结果:成功捕获异常 try: database.bad_method_name() except: @@ -124,16 +152,29 @@ def do_rounds(database, species, *, utcnow=datetime.utcnow): assert False -# Example 7 +# 示例 7 +# 目的:定义获取数据库实例的函数 +# 解释:创建一个全局数据库实例并返回。 +# 结果:成功定义函数 DATABASE = None def get_database(): + """ + 目的:获取数据库实例 + 解释:创建一个全局数据库实例并返回。 + 结果:返回数据库实例 + """ global DATABASE if DATABASE is None: DATABASE = ZooDatabase() return DATABASE def main(argv): + """ + 目的:主函数 + 解释:获取数据库实例并执行喂食操作。 + 结果:打印喂食的动物数量 + """ database = get_database() species = argv[1] count = do_rounds(database, species) @@ -141,7 +182,10 @@ def main(argv): return 0 -# Example 8 +# 示例 8 +# 目的:使用 patch 模块测试 main 函数 +# 解释:使用 patch 模块临时替换数据库实例并测试 main 函数。 +# 结果:成功替换数据库实例并测试 main 函数 import contextlib import io from unittest.mock import patch @@ -163,4 +207,4 @@ def main(argv): found = fake_stdout.getvalue() expected = 'Fed 2 Meerkat(s)\n' - assert found == expected + assert found == expected \ No newline at end of file From 6edc540d435ad2cfd5c3b2aac1a1c0683939c45a Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 26 Sep 2024 13:51:47 +0800 Subject: [PATCH 26/59] modify item_84-90.py --- example_code/item_84.py | 82 ++++++++++++++++-------------- example_code/item_86.py | 21 +++++--- example_code/item_87.py | 91 +++++++++++++++++++++++---------- example_code/item_89.py | 85 ++++++++++++++++++++++++------- example_code/item_90.py | 108 ++++++++++++++++++++++++++++++++++------ 5 files changed, 285 insertions(+), 102 deletions(-) diff --git a/example_code/item_84.py b/example_code/item_84.py index f673237..0b7b1f4 100755 --- a/example_code/item_84.py +++ b/example_code/item_84.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +22,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +32,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 io.IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,67 +51,70 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义一个判断回文的函数 +# 解释:创建一个函数,判断给定的单词是否是回文。 +# 结果:成功定义函数并进行断言测试 def palindrome(word): - """Return True if the given word is a palindrome.""" + """判断给定的单词是否是回文""" return word == word[::-1] assert palindrome('tacocat') assert not palindrome('banana') -# Example 2 +# 示例 2 +# 目的:打印函数的文档字符串 +# 解释:使用 repr 函数打印 palindrome 函数的文档字符串。 +# 结果:成功打印文档字符串 print(repr(palindrome.__doc__)) -# Example 3 -"""Library for finding linguistic patterns in words. +# 示例 3 +# 目的:定义一个用于查找语言模式的库 +# 解释:创建一个模块,提供判断单词是否具有特殊属性的功能。 +# 结果:成功定义模块并列出可用函数 +"""用于查找单词中语言模式的库。 -Testing how words relate to each other can be tricky sometimes! -This module provides easy ways to determine when words you've -found have special properties. +测试单词之间的关系有时可能很棘手! +该模块提供了简单的方法来确定您找到的单词是否具有特殊属性。 -Available functions: -- palindrome: Determine if a word is a palindrome. -- check_anagram: Determine if two words are anagrams. +可用函数: +- palindrome: 判断单词是否是回文。 +- check_anagram: 判断两个单词是否是变位词。 ... """ -# Example 4 +# 示例 4 +# 目的:定义一个表示游戏玩家的类 +# 解释:创建一个类,表示游戏玩家,并提供公共属性和方法。 +# 结果:成功定义类并列出公共属性 class Player: - """Represents a player of the game. + """表示游戏玩家的类。 - Subclasses may override the 'tick' method to provide - custom animations for the player's movement depending - on their power level, etc. + 子类可以重写 'tick' 方法,根据玩家的能量等级等提供自定义动画。 - Public attributes: - - power: Unused power-ups (float between 0 and 1). - - coins: Coins found during the level (integer). + 公共属性: + - power: 未使用的能量提升(0 到 1 之间的浮点数)。 + - coins: 在关卡中找到的硬币(整数)。 """ -# Example 5 +# 示例 5 +# 目的:定义一个查找变位词的函数 +# 解释:创建一个函数,查找给定单词的所有变位词。 +# 结果:成功定义函数并进行断言测试 import itertools def find_anagrams(word, dictionary): - """Find all anagrams for a word. - - This function only runs as fast as the test for - membership in the 'dictionary' container. - - Args: - word: String of the target word. - dictionary: collections.abc.Container with all - strings that are known to be actual words. - - Returns: - List of anagrams that were found. Empty if - none were found. + """ + 目的:查找单词的所有变位词 + 解释:该函数的运行速度仅取决于 'dictionary' 容器中成员测试的速度。 + 结果:返回找到的变位词列表,如果没有找到则返回空列表 """ permutations = itertools.permutations(word, len(word)) possible = (''.join(x) for x in permutations) found = {word for word in possible if word in dictionary} return list(found) -assert find_anagrams('pancakes', ['scanpeak']) == ['scanpeak'] +assert find_anagrams('pancakes', ['scanpeak']) == ['scanpeak'] \ No newline at end of file diff --git a/example_code/item_86.py b/example_code/item_86.py index 492fa14..84c8a3f 100755 --- a/example_code/item_86.py +++ b/example_code/item_86.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +22,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +32,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 io.IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,17 +51,21 @@ def close_open_files(): atexit.register(close_open_files) -# Example 4 -# db_connection.py +# 示例 4 +# 目的:定义数据库连接类 +# 解释:根据操作系统平台定义不同的数据库连接类。 +# 结果:成功定义数据库连接类 import sys class Win32Database: + """表示 Windows 平台的数据库连接类。""" pass class PosixDatabase: + """表示 POSIX 平台的数据库连接类。""" pass if sys.platform.startswith('win32'): Database = Win32Database else: - Database = PosixDatabase + Database = PosixDatabase \ No newline at end of file diff --git a/example_code/item_87.py b/example_code/item_87.py index 07c20c9..53d5f0f 100755 --- a/example_code/item_87.py +++ b/example_code/item_87.py @@ -14,15 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random + random.seed(1234) import logging from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,26 +33,37 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) + def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 io.IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() + atexit.register(close_open_files) -# Example 1 -# my_module.py +# 示例 1 +# 目的:定义一个计算重量的函数 +# 解释:创建一个函数,计算给定体积和密度的重量,并进行异常处理。 +# 结果:成功定义函数并进行断言测试 def determine_weight(volume, density): + """计算重量""" if density <= 0: raise ValueError('Density must be positive') + try: determine_weight(1, 0) except ValueError: @@ -60,18 +72,24 @@ def determine_weight(volume, density): assert False -# Example 2 -# my_module.py +# 示例 2 +# 目的:定义自定义异常类 +# 解释:创建自定义异常类,用于处理密度和体积的异常情况。 +# 结果:成功定义自定义异常类 class Error(Exception): - """Base-class for all exceptions raised by this module.""" + """模块中所有异常的基类""" + class InvalidDensityError(Error): - """There was a problem with a provided density value.""" + """提供的密度值有问题""" + class InvalidVolumeError(Error): - """There was a problem with the provided weight value.""" + """提供的体积值有问题""" + def determine_weight(volume, density): + """计算重量""" if density < 0: raise InvalidDensityError('Density must be positive') if volume < 0: @@ -80,13 +98,17 @@ def determine_weight(volume, density): density / volume -# Example 3 +# 示例 3 +# 目的:定义一个包含异常处理的模块类 +# 解释:创建一个模块类,包含异常处理和计算重量的方法。 +# 结果:成功定义模块类并进行断言测试 class my_module: Error = Error InvalidDensityError = InvalidDensityError @staticmethod def determine_weight(volume, density): + """计算重量""" if density < 0: raise InvalidDensityError('Density must be positive') if volume < 0: @@ -94,6 +116,7 @@ def determine_weight(volume, density): if volume == 0: density / volume + try: weight = my_module.determine_weight(1, -1) except my_module.Error: @@ -101,8 +124,10 @@ def determine_weight(volume, density): else: assert False - -# Example 4 +# 示例 4 +# 目的:使用哨兵对象进行异常处理 +# 解释:使用哨兵对象和异常处理来测试计算重量的方法。 +# 结果:成功使用哨兵对象进行异常处理 SENTINEL = object() weight = SENTINEL try: @@ -116,8 +141,10 @@ def determine_weight(volume, density): assert weight is SENTINEL - -# Example 5 +# 示例 5 +# 目的:嵌套异常处理 +# 解释:使用嵌套的异常处理来测试计算重量的方法。 +# 结果:成功进行嵌套异常处理并进行断言测试 try: weight = SENTINEL try: @@ -131,7 +158,7 @@ def determine_weight(volume, density): raise # Re-raise exception to the caller else: assert False - + assert weight == 0 except: logging.exception('Expected') @@ -139,19 +166,24 @@ def determine_weight(volume, density): assert False -# Example 6 -# my_module.py - +# 示例 6 +# 目的:定义一个新的异常类 +# 解释:创建一个新的异常类,用于处理负密度值的情况。 +# 结果:成功定义新的异常类 class NegativeDensityError(InvalidDensityError): - """A provided density value was negative.""" + """提供的密度值为负""" def determine_weight(volume, density): + """计算重量""" if density < 0: raise NegativeDensityError('Density must be positive') -# Example 7 +# 示例 7 +# 目的:使用新的异常类进行异常处理 +# 解释:使用新的异常类和异常处理来测试计算重量的方法。 +# 结果:成功使用新的异常类进行异常处理 try: my_module.NegativeDensityError = NegativeDensityError my_module.determine_weight = determine_weight @@ -174,16 +206,21 @@ def determine_weight(volume, density): assert False -# Example 8 -# my_module.py +# 示例 8 +# 目的:定义多个异常类 +# 解释:创建多个异常类,用于处理重量、体积和密度的异常情况。 +# 结果:成功定义多个异常类 class Error(Exception): - """Base-class for all exceptions raised by this module.""" + """模块中所有异常的基类""" + class WeightError(Error): - """Base-class for weight calculation errors.""" + """重量计算错误的基类""" + class VolumeError(Error): - """Base-class for volume calculation errors.""" + """体积计算错误的基类""" + class DensityError(Error): - """Base-class for density calculation errors.""" + """密度计算错误的基类""" \ No newline at end of file diff --git a/example_code/item_89.py b/example_code/item_89.py index e9fe12a..2d1ab5f 100755 --- a/example_code/item_89.py +++ b/example_code/item_89.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Reproduce book environment +# 复现书中的环境 import random random.seed(1234) @@ -22,7 +22,7 @@ from pprint import pprint from sys import stdout as STDOUT -# Write all output to a temporary directory +# 将所有输出写入临时目录 import atexit import gc import io @@ -32,12 +32,17 @@ TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) -# Make sure Windows processes exit cleanly +# 确保 Windows 进程干净退出 OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 io.IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -46,19 +51,29 @@ def close_open_files(): atexit.register(close_open_files) -# Example 1 +# 示例 1 +# 目的:定义一个计算距离的函数 +# 解释:创建一个函数,计算给定速度和持续时间的距离。 +# 结果:成功定义函数并进行断言测试 def print_distance(speed, duration): + """计算距离""" distance = speed * duration print(f'{distance} miles') print_distance(5, 2.5) -# Example 2 +# 示例 2 +# 目的:测试 print_distance 函数 +# 解释:调用 print_distance 函数并传入参数。 +# 结果:成功调用函数并打印结果 print_distance(1000, 3) -# Example 3 +# 示例 3 +# 目的:定义单位转换函数 +# 解释:创建一个字典和两个函数,用于转换和本地化单位。 +# 结果:成功定义字典和函数 CONVERSIONS = { 'mph': 1.60934 / 3600 * 1000, # m/s 'hours': 3600, # seconds @@ -69,10 +84,12 @@ def print_distance(speed, duration): } def convert(value, units): + """转换单位""" rate = CONVERSIONS[units] return rate * value def localize(value, units): + """本地化单位""" rate = CONVERSIONS[units] return value / rate @@ -80,6 +97,7 @@ def print_distance(speed, duration, *, speed_units='mph', time_units='hours', distance_units='miles'): + """计算距离并打印""" norm_speed = convert(speed, speed_units) norm_duration = convert(duration, time_units) norm_distance = norm_speed * norm_duration @@ -87,19 +105,26 @@ def print_distance(speed, duration, *, print(f'{distance} {distance_units}') -# Example 4 +# 示例 4 +# 目的:测试 print_distance 函数 +# 解释:调用 print_distance 函数并传入不同单位的参数。 +# 结果:成功调用函数并打印结果 print_distance(1000, 3, speed_units='meters', time_units='seconds') -# Example 5 +# 示例 5 +# 目的:添加警告信息 +# 解释:修改 print_distance 函数,添加警告信息以提醒用户提供单位参数。 +# 结果:成功添加警告信息并调用函数 import warnings def print_distance(speed, duration, *, speed_units=None, time_units=None, distance_units=None): + """计算距离并打印""" if speed_units is None: warnings.warn( 'speed_units required', DeprecationWarning) @@ -122,7 +147,10 @@ def print_distance(speed, duration, *, print(f'{distance} {distance_units}') -# Example 6 +# 示例 6 +# 目的:重定向标准错误输出 +# 解释:使用 contextlib.redirect_stderr 重定向标准错误输出并捕获警告信息。 +# 结果:成功重定向标准错误输出并捕获警告信息 import contextlib import io @@ -135,8 +163,12 @@ def print_distance(speed, duration, *, print(fake_stderr.getvalue()) -# Example 7 +# 示例 7 +# 目的:定义一个要求参数的函数 +# 解释:创建一个函数,要求提供参数并发出警告。 +# 结果:成功定义函数并调用 print_distance 函数 def require(name, value, default): + """要求提供参数""" if value is not None: return value warnings.warn( @@ -149,6 +181,7 @@ def print_distance(speed, duration, *, speed_units=None, time_units=None, distance_units=None): + """计算距离并打印""" speed_units = require('speed_units', speed_units, 'mph') time_units = require('time_units', time_units, 'hours') distance_units = require( @@ -161,7 +194,10 @@ def print_distance(speed, duration, *, print(f'{distance} {distance_units}') -# Example 8 +# 示例 8 +# 目的:重定向标准错误输出 +# 解释:使用 contextlib.redirect_stderr 重定向标准错误输出并捕获警告信息。 +# 结果:成功重定向标准错误输出并捕获警告信息 import contextlib import io @@ -174,7 +210,10 @@ def print_distance(speed, duration, *, print(fake_stderr.getvalue()) -# Example 9 +# 示例 9 +# 目的:将警告转换为异常 +# 解释:使用 warnings.simplefilter 将警告转换为异常并捕获异常。 +# 结果:成功将警告转换为异常并捕获异常 warnings.simplefilter('error') try: warnings.warn('This usage is deprecated', @@ -187,7 +226,10 @@ def print_distance(speed, duration, *, warnings.resetwarnings() -# Example 10 +# 示例 10 +# 目的:忽略警告 +# 解释:使用 warnings.simplefilter 忽略警告并重置警告过滤器。 +# 结果:成功忽略警告并重置警告过滤器 warnings.resetwarnings() warnings.simplefilter('ignore') @@ -196,7 +238,10 @@ def print_distance(speed, duration, *, warnings.resetwarnings() -# Example 11 +# 示例 11 +# 目的:捕获警告日志 +# 解释:使用 logging.captureWarnings 捕获警告日志并打印日志输出。 +# 结果:成功捕获警告日志并打印日志输出 import logging fake_stderr = io.StringIO() @@ -219,16 +264,22 @@ def print_distance(speed, duration, *, warnings.resetwarnings() -# Example 12 +# 示例 12 +# 目的:捕获警告 +# 解释:使用 warnings.catch_warnings 捕获警告并进行断言测试。 +# 结果:成功捕获警告并进行断言测试 with warnings.catch_warnings(record=True) as found_warnings: found = require('my_arg', None, 'fake units') expected = 'fake units' assert found == expected -# Example 13 +# 示例 13 +# 目的:断言捕获的警告 +# 解释:对捕获的警告进行断言测试。 +# 结果:成功断言捕获的警告 assert len(found_warnings) == 1 single_warning = found_warnings[0] assert str(single_warning.message) == ( 'my_arg will be required soon, update your code') -assert single_warning.category == DeprecationWarning +assert single_warning.category == DeprecationWarning \ No newline at end of file diff --git a/example_code/item_90.py b/example_code/item_90.py index 792a8c7..149dc91 100755 --- a/example_code/item_90.py +++ b/example_code/item_90.py @@ -38,6 +38,11 @@ os.chdir(TEST_DIR.name) def close_open_files(): + """ + 目的:关闭所有打开的文件 + 解释:遍历所有对象并关闭所有 io.IOBase 实例。 + 结果:所有打开的文件都被关闭 + """ everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): @@ -47,10 +52,14 @@ def close_open_files(): # Example 1 +# 目的:测试 subtract 函数的异常处理 +# 解释:定义一个 subtract 函数并传入错误类型的参数,捕获异常。 +# 结果:成功捕获异常并记录日志 try: def subtract(a, b): + """减法运算""" return a - b - + subtract(10, '5') except: logging.exception('Expected') @@ -59,10 +68,14 @@ def subtract(a, b): # Example 3 +# 目的:测试 concat 函数的异常处理 +# 解释:定义一个 concat 函数并传入错误类型的参数,捕获异常。 +# 结果:成功捕获异常并记录日志 try: def concat(a, b): + """字符串连接""" return a + b - + concat('first', b'second') except: logging.exception('Expected') @@ -72,17 +85,29 @@ def concat(a, b): # Example 5 class Counter: + """ + 计数器类 + 目的:定义一个计数器类 + 解释:创建一个 Counter 类,包含增加和获取值的方法。 + 结果:成功定义类并进行断言测试 + """ def __init__(self): + """初始化计数器""" self.value = 0 def add(self, offset): - value += offset + """增加计数器的值""" + self.value += offset def get(self) -> int: - self.value + """获取计数器的值""" + return self.value # Example 6 +# 目的:测试 Counter 类的 add 方法 +# 解释:创建 Counter 实例并调用 add 方法,捕获异常。 +# 结果:成功捕获异常并记录日志 try: counter = Counter() counter.add(5) @@ -93,6 +118,9 @@ def get(self) -> int: # Example 7 +# 目的:测试 Counter 类的 get 方法 +# 解释:创建 Counter 实例并调用 get 方法,进行断言测试。 +# 结果:成功调用方法并进行断言测试 try: counter = Counter() found = counter.get() @@ -104,19 +132,24 @@ def get(self) -> int: # Example 9 +# 目的:测试 combine 函数的异常处理 +# 解释:定义一个 combine 函数并传入包含复数的列表,捕获异常。 +# 结果:成功捕获异常并记录日志 try: def combine(func, values): + """组合函数""" assert len(values) > 0 - + result = values[0] for next_value in values[1:]: result = func(result, next_value) - + return result - + def add(x, y): + """加法运算""" return x + y - + inputs = [1, 2, 3, 4j] result = combine(add, inputs) assert result == 10, result # Fails @@ -127,15 +160,19 @@ def add(x, y): # Example 11 +# 目的:测试 get_or_default 函数的异常处理 +# 解释:定义一个 get_or_default 函数并传入不同的参数,捕获异常。 +# 结果:成功捕获异常并记录日志 try: - def get_or_default(value, default): + def get_or_default(value, default): + """获取值或默认值""" if value is not None: return value - return value - + return default + found = get_or_default(3, 5) assert found == 3 - + found = get_or_default(None, 5) assert found == 5, found # Fails except: @@ -146,11 +183,25 @@ def get_or_default(value, default): # Example 13 class FirstClass: + """ + 第一个类 + 目的:定义两个类并删除 + 解释:创建 FirstClass 和 SecondClass 类的实例并删除类。 + 结果:成功创建实例并删除类 + """ def __init__(self, value): + """初始化 FirstClass""" self.value = value class SecondClass: + """ + 第二个类 + 目的:定义两个类并删除 + 解释:创建 FirstClass 和 SecondClass 类的实例并删除类。 + 结果:成功创建实例并删除类 + """ def __init__(self, value): + """初始化 SecondClass""" self.value = value second = SecondClass(5) @@ -161,15 +212,30 @@ def __init__(self, value): # Example 15 +# 目的:测试类的前向引用 +# 解释:定义 FirstClass 和 SecondClass 类并进行前向引用,捕获异常。 +# 结果:成功捕获异常并记录日志 try: class FirstClass: + """ + 第一个类 + 目的:测试类的前向引用 + 解释:定义 FirstClass 和 SecondClass 类并进行前向引用,捕获异常。 + 结果:成功捕获异常并记录日志 + """ def __init__(self, value: SecondClass) -> None: # Breaks self.value = value - + class SecondClass: + """ + 第二个类 + 目的:测试类的前向引用 + 解释:定义 FirstClass 和 SecondClass 类并进行前向引用,捕获异常。 + 结果:成功捕获异常并记录日志 + """ def __init__(self, value: int) -> None: self.value = value - + second = SecondClass(5) first = FirstClass(second) except: @@ -180,12 +246,24 @@ def __init__(self, value: int) -> None: # Example 16 class FirstClass: + """ + 第一个类 + 目的:测试类的前向引用 + 解释:定义 FirstClass 和 SecondClass 类并进行前向引用。 + 结果:成功定义类并进行前向引用 + """ def __init__(self, value: 'SecondClass') -> None: # OK self.value = value class SecondClass: + """ + 第二个类 + 目的:测试类的前向引用 + 解释:定义 FirstClass 和 SecondClass 类并进行前向引用。 + 结果:成功定义类并进行前向引用 + """ def __init__(self, value: int) -> None: self.value = value second = SecondClass(5) -first = FirstClass(second) +first = FirstClass(second) \ No newline at end of file From bb9c5e7890d0a8dfa93f6c468735a384f1471c6e Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 26 Sep 2024 16:53:08 +0800 Subject: [PATCH 27/59] modify item_09.py --- example_code/item_09.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/example_code/item_09.py b/example_code/item_09.py index 921d4e4..f9c44f2 100755 --- a/example_code/item_09.py +++ b/example_code/item_09.py @@ -154,6 +154,9 @@ def close_open_files(): # 函数 coprime(a, b) 使用 for 循环检查 a 和 b 是否有共同因子。 # 如果找到共同因子,返回 False;否则返回 True,表示 a 和 b 是互质。 # 结果:4 和 9 是互质,3 和 6 不是。 +# 输出: +# coprime(4, 9) ::: True +# coprime(3, 6) ::: False print(f"\n{'Example 6':*^50}") def coprime(a, b): for i in range(2, min(a, b) + 1): @@ -172,6 +175,9 @@ def coprime(a, b): # 通过布尔变量 is_coprime 标识是否找到共同因子。如果找到,提前返回 False。 # 否则,返回 is_coprime 的值,表示 a 和 b 是否互质。 # 结果:4 和 9 是互质,3 和 6 不是。 +# 输出: +# coprime_alternate(4, 9) ::: True +# coprime_alternate(3, 6) ::: False print(f"\n{'Example 7':*^50}") def coprime_alternate(a, b): is_coprime = True @@ -183,4 +189,4 @@ def coprime_alternate(a, b): print(f"coprime_alternate(4, 9) ::: {coprime_alternate(4, 9)}") print(f"coprime_alternate(3, 6) ::: {coprime_alternate(3, 6)}") assert coprime_alternate(4, 9) -assert not coprime_alternate(3, 6) +assert not coprime_alternate(3, 6) \ No newline at end of file From fe388915e71c01a5d4636c31b95c18757a2f6731 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sun, 29 Sep 2024 19:03:49 +0800 Subject: [PATCH 28/59] modify item_09.py --- example_code/item_10.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example_code/item_10.py b/example_code/item_10.py index c004701..822acff 100755 --- a/example_code/item_10.py +++ b/example_code/item_10.py @@ -83,6 +83,7 @@ def make_lemonade(count): print(f'Making {count} lemons into lemonade') def out_of_stock(): + # 缺货 Out of stock print('Out of stock!') count = fresh_fruit.get('lemon', 0) @@ -98,7 +99,7 @@ def out_of_stock(): # 使用赋值表达式 := 同时进行赋值和判断,简化代码。 # 结果:简化了库存判断的代码结构。 print(f"\n{'Example 3':*^50}") - +# 这里的:=是赋值表达式,那么判断体现在什么层面上,通过初始值0的设定来的 if count := fresh_fruit.get('lemon', 0): make_lemonade(count) else: From ec7cc24dbdfc715820e280914f64313203a4c992 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sun, 29 Sep 2024 20:55:33 +0800 Subject: [PATCH 29/59] modify item_11.py-item_13.py --- example_code/item_11.py | 29 +++++++++++++++++-------- example_code/item_12.py | 20 +++++++++++++---- example_code/item_13.py | 48 +++++++++++++++++++++++++++++------------ 3 files changed, 70 insertions(+), 27 deletions(-) diff --git a/example_code/item_11.py b/example_code/item_11.py index 97f23f1..3d3f919 100755 --- a/example_code/item_11.py +++ b/example_code/item_11.py @@ -25,6 +25,8 @@ """ import random +import sys + random.seed(1234) import logging @@ -54,6 +56,9 @@ def close_open_files(): atexit.register(close_open_files) +# 配置日志将输出到 stdout 而不是 stderr +logging.basicConfig(stream=sys.stdout, level=logging.INFO) + # Example 1 --- 使用切片提取列表的子部分 # 目的:演示如何通过切片获取列表的中间部分或去掉两端的元素。 @@ -136,19 +141,22 @@ def close_open_files(): print(f"\n{'Example 7':*^50}") try: a[20] -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False -# Example 8 --- 切片与原列表无关 +# Example 8 --- 切片与原列表无关, +# 切片列表赋值是一个浅度拷贝列表,所以和之前的列表有部分关系 +# 为什么说有部分关系,是因为做了第一层拷贝导致两个列表的引用是分离的, +# 但是如果列表中有引用,那么就会有问题了,因为是两者共享的,这个是个坑啊 # 目的:演示切片是创建新列表,与原列表没有关联。 # 解释: # b 是 a 的切片,修改 b 的元素不会影响 a。 # 结果:修改 b 后,a 保持不变。 print(f"\n{'Example 8':*^50}") -b = a[3:] +b = a[3:] # 注意这里是切片赋值,所以是深度拷贝 print('Before: ', b) b[1] = 99 print('After: ', b) @@ -162,7 +170,7 @@ def close_open_files(): # 结果:替换后列表 a 发生变化。 print(f"\n{'Example 9':*^50}") print('Before ', a) -a[2:7] = [99, 22, 14] +a[2:7] = [99, 22, 14] # 注意不是切片赋值,所以是直接替换 print('After ', a) @@ -173,12 +181,15 @@ def close_open_files(): # 结果:列表长度增加。 print(f"\n{'Example 10':*^50}") print('Before ', a) +# 注意不是切片赋值,所以是直接替换,而且可以导致列表长度增加,所有后续数据的索引都会发生变化 a[2:3] = [47, 11] print('After ', a) +print(f"a[2:3] = {a[2:3]}") # Example 11 --- 通过切片复制列表 -# 目的:展示如何通过切片复制整个列表。 +# 目的:展示如何通过切片复制整个列表,道理太简单了,浅拷贝, +# 而且是第一层拷贝,如果里面有引用,那麻烦就大了。 # 解释: # b = a[:] 复制列表 a,b 是新列表,但内容相同。 # 结果:b 和 a 内容相同,但不是同一个对象。 @@ -187,16 +198,16 @@ def close_open_files(): assert b == a and b is not a -# Example 12 --- 切片赋值影响列表对象 +# Example 12 --- 切片赋值影响列表对象,问题的关键这与这种赋值是引用赋值都是浅拷贝赋值 # 目的:演示当通过切片赋值时,列表对象仍然保持相同。 # 解释: # b = a 使 a 和 b 指向同一个列表对象,修改 a 的内容会影响 b。 # 结果:a 和 b 都发生了内容变化,但它们仍然是同一个列表对象。 print(f"\n{'Example 12':*^50}") -b = a +b = a # 注意这里是引用赋值,所以不是是浅拷贝 print('Before a', a) print('Before b', b) -a[:] = [101, 102, 103] +a[:] = [101, 102, 103] # 注意不是切片赋值,所以是直接替换,所以两者都会给改 assert a is b # Still the same list object print('After a ', a) # Now has different contents print('After b ', b) # Same list, so same contents as a diff --git a/example_code/item_12.py b/example_code/item_12.py index 0cfcc4f..fd71687 100755 --- a/example_code/item_12.py +++ b/example_code/item_12.py @@ -18,6 +18,7 @@ # 军规 12: Avoid Striding and Slicing in a Single Expression # 军规 12: 避免在一个表达式中同时使用步长和切片 +# 备注 : 步长这个方式是为了增加运行效能的,这个玩意的使用要根据特定场景的需求来使用 """ Avoid Striding and Slicing in a Single Expression @@ -25,6 +26,8 @@ """ import random +import sys + random.seed(1234) import logging @@ -54,6 +57,9 @@ def close_open_files(): atexit.register(close_open_files) +# 配置日志将输出到 stdout 而不是 stderr +logging.basicConfig(stream=sys.stdout, level=logging.INFO) + # Example 1 --- 使用步长切片提取奇偶项 # 目的:演示如何通过步长切片提取列表的奇数和偶数项。 @@ -68,7 +74,7 @@ def close_open_files(): print(evens) -# Example 2 --- 对字节串使用步长切片 +# Example 2 --- 对字节串使用步长切片,这个是一种步长的使用方式 # 目的:展示如何使用步长切片反转字节串。 # 解释: # 对字节串 x 进行切片操作 x[::-1],可以反转字节串。 @@ -95,14 +101,16 @@ def close_open_files(): # 解释: # 对 UTF-8 编码的字节串进行步长切片后,字节顺序会被打乱,导致解码失败。 # 结果:引发 UnicodeDecodeError,记录异常。 +# 输出 : ERROR:root:Error type: UnicodeDecodeError, Message: 'utf-8' codec +# can't decode byte 0xb8 in position 0: invalid start byte print(f"\n{'Example 4':*^50}") try: w = '寿司' x = w.encode('utf-8') - y = x[::-1] + y = x[::-1] # 输出 b'\x8f\x9f\x8f\x9f\x8f\x9f\x8f\x9f' z = y.decode('utf-8') -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -137,6 +145,10 @@ def close_open_files(): # 结果:分别展示原始列表 x 和步长切片 y 及其子集 z。 print(f"\n{'Example 7':*^50}") y = x[::2] # ['a', 'c', 'e', 'g'] +# y[1:-1] 表示对序列 y 进行切片操作: +# 1:起始索引,表示从索引 1 开始(即第二个元素)。 +# -1:结束索引,表示切片到倒数第二个元素(不包括倒数第一个元素)。在 Python 中,负索引用于从序列末尾开始计数。 +# 结果:这个切片会提取从第二个元素到倒数第二个元素的所有内容,但不包括最后一个元素。 z = y[1:-1] # ['c', 'e'] print(x) print(y) diff --git a/example_code/item_13.py b/example_code/item_13.py index 0fcc04a..b7aa780 100755 --- a/example_code/item_13.py +++ b/example_code/item_13.py @@ -22,9 +22,16 @@ """ Prefer Unpacking Over Indexing 优先使用解包操作代替索引访问 +备注:一定要注意场景区分 +这个有场景区分,如果要一次性对列表所有值进行赋值,解包效能好, +反之,我只想把列表中一个索引下的元素赋值给别的变量,索引就有优势了, +解包是全局性的,索引其实是局部性的 """ + import random +import sys + random.seed(1234) import logging @@ -54,6 +61,9 @@ def close_open_files(): atexit.register(close_open_files) +# 配置日志将输出到 stdout 而不是 stderr +logging.basicConfig(stream=sys.stdout, level=logging.INFO) + # Example 1 --- 解包操作不足引发异常 # 目的:展示当解包操作无法分配足够值时会引发错误。 @@ -63,10 +73,10 @@ def close_open_files(): print(f"\n{'Example 1':*^50}") try: car_ages = [0, 9, 4, 8, 7, 20, 19, 1, 6, 15] - car_ages_descending = sorted(car_ages, reverse=True) + car_ages_descending = sorted(car_ages, reverse=True) # 降序排列 oldest, second_oldest = car_ages_descending -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -115,10 +125,10 @@ def close_open_files(): print(f"\n{'Example 5':*^50}") try: # This will not compile - source = """*others = car_ages_descending""" + source = """*others = car_ages_descending""" # 语法错误:带星号的赋值目标必须在列表或元组中 eval(source) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -131,10 +141,10 @@ def close_open_files(): print(f"\n{'Example 6':*^50}") try: # This will not compile - source = """first, *middle, *second_middle, last = [1, 2, 3, 4]""" + source = """first, *middle, *second_middle, last = [1, 2, 3, 4]""" # 是无效的语法,因为解包中只能有一个剩余值变量。 eval(source) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -158,7 +168,8 @@ def close_open_files(): # Example 8 --- 处理解包不足的情况 -# 目的:展示当列表元素不足时如何处理解包操作。 +# 备注 : 它实际上展示了 Python 的一种特性——“星号解包”(starred unpacking),用来处理可变长度的数据 +# 目的:展示当列表元素不足时如何处理解包操作,针对返回固定字段信息和可变字段信息的场景。 # 解释: # short_list 只有两个元素,但通过 *rest 可以避免解包失败,剩余部分为 []。 # 结果:输出前两个元素和剩余部分(空列表)。 @@ -169,6 +180,15 @@ def close_open_files(): # Example 9 --- 迭代器无法自动解包 + +# 迭代器的工作机制: +# 迭代器(iterator)是一个能够逐个返回元素的对象,但它的元素只会被一次性返回, +# 当元素被取出后,迭代器的“游标”会向前滑动,指向下一个元素。 +# 不可重复访问:迭代器只能一次性遍历,元素一旦被取出,就不能再回头访问它们了。 + +# 为什么迭代器无法自动解包? +# 解包需要一次性获取多个元素,而迭代器的游标只能前进,并且只能逐个获取元素。 +# 如果用迭代器进行解包,Python 会依次获取元素,但由于解包的变量数量是固定的,迭代器的逐步取值特性可能会导致解包不完整,或无法满足所有变量的解包需求。 # 目的:展示迭代器无法直接通过解包操作获取多个元素。 # 解释: # iter(range(1, 3)) 是一个迭代器,不能像列表那样直接解包多个值。 @@ -177,8 +197,8 @@ def close_open_files(): it = iter(range(1, 3)) try: first, second = it -except TypeError as e: - print(f"Error: {e}") +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") # Example 10 --- 使用生成器生成 CSV 行 @@ -194,7 +214,7 @@ def generate_csv(): yield ('2019-03-26', 'Ford', 'F150' , '2008', '$2400') -# Example 11 --- 从生成器中提取 CSV 数据 +# Example 11 --- 从生成器中提取 CSV 数据 ---- 为Example 12做数据准备 # 目的:展示如何将生成器的结果转换为列表,并通过解包提取标题和数据。 # 解释: # all_csv_rows 列表存储生成器生成的所有行,header 保存标题行,rows 保存剩余数据。 @@ -213,7 +233,7 @@ def generate_csv(): # 通过解包操作从生成器 it 中提取标题行和剩余数据行。 # 结果:输出 CSV 的标题和数据行数。 print(f"\n{'Example 12':*^50}") -it = generate_csv() +it = generate_csv() # 生成器,逐行生成 CSV 数据 header, *rows = it print('CSV Header:', header) print('Row count: ', len(rows)) From 54ca81fb4c63ac2c31e16bf856c7b9948fc795ac Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sun, 29 Sep 2024 22:16:07 +0800 Subject: [PATCH 30/59] modify item_14.py --- example_code/item_14.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/example_code/item_14.py b/example_code/item_14.py index 2867e16..a183fb5 100755 --- a/example_code/item_14.py +++ b/example_code/item_14.py @@ -18,6 +18,7 @@ # 军规 14: Sort by Complex Criteria Using the key Parameter # 军规 14: 使用 key 参数根据复杂标准排序 +# 本质就是把排序规则独立出来,用一个指定排序规则的函数去实现自定义排序 """ Sort by Complex Criteria Using the key Parameter @@ -25,6 +26,8 @@ """ import random +import sys + random.seed(1234) import logging @@ -54,6 +57,9 @@ def close_open_files(): atexit.register(close_open_files) +# 配置日志将输出到 stdout 而不是 stderr +logging.basicConfig(stream=sys.stdout, level=logging.INFO) + # Example 1 --- 简单数字排序 # 目的:演示对数字列表进行排序。 @@ -96,8 +102,8 @@ def __repr__(self): print(f"\n{'Example 3':*^50}") try: tools.sort() -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -108,7 +114,7 @@ def __repr__(self): # tools.sort(key=lambda x: x.name) 根据工具的 name 属性对工具进行排序。 # 结果:输出按名称升序排序的工具列表。 print(f"\n{'Example 4':*^50}") -print('Unsorted:', repr(tools)) +print('Unsorted:', repr(tools)) # 返回对象的字符串表示形式,调用对象的 __repr__ 方法 tools.sort(key=lambda x: x.name) print('\nSorted: ', tools) @@ -131,10 +137,11 @@ def __repr__(self): print(f"\n{'Example 6':*^50}") places = ['home', 'work', 'New York', 'Paris'] places.sort() -print('Case sensitive: ', places) +print('Case sensitive: ', places) # 大小写敏感排序 places.sort(key=lambda x: x.lower()) -print('Case insensitive:', places) +print('Case insensitive:', places) # 大小写不敏感排序 +# ===============================当前阅读标签================================== # Example 7 --- 创建电动工具列表 # 目的:创建一个新的工具列表用于后续的排序演示。 From c7224ea10b479c4b958d171c77b31098c3a1964e Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Wed, 9 Oct 2024 15:06:53 +0800 Subject: [PATCH 31/59] modify item_14.py --- example_code/item_14.py | 13 +++++++++---- example_code/item_15.py | 21 ++++----------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/example_code/item_14.py b/example_code/item_14.py index a183fb5..e517f12 100755 --- a/example_code/item_14.py +++ b/example_code/item_14.py @@ -157,7 +157,8 @@ def __repr__(self): ] -# Example 8 --- 直接比较元组 +# Example 8 --- 直接比较元组,这个也能比,好吧 +# 通过这个可以制定阶梯型排序规则,先比较第一个元素,如果相等再比较第二个元素 # 目的:展示如何通过元组的元素逐个比较大小。 # 解释: # 元组比较会首先比较第一个元素,如果相等,再比较第二个元素。 @@ -187,7 +188,7 @@ def __repr__(self): # power_tools.sort(key=lambda x: (x.weight, x.name)) 首先根据重量排序,如果重量相同,则根据名称排序。 # 结果:输出按重量和名称排序的结果。 print(f"\n{'Example 10':*^50}") -power_tools.sort(key=lambda x: (x.weight, x.name)) +power_tools.sort(key=lambda x: (x.weight, x.name)) # 首先根据重量排序,如果重量相同,则根据名称排序 print(power_tools) @@ -198,7 +199,7 @@ def __repr__(self): # 结果:输出按重量和名称降序排列的结果。 print(f"\n{'Example 11':*^50}") power_tools.sort(key=lambda x: (x.weight, x.name), - reverse=True) # Makes all criteria descending + reverse=True) # Reverse the sort order print(power_tools) @@ -208,11 +209,15 @@ def __repr__(self): # power_tools.sort(key=lambda x: (-x.weight, x.name)) 使重量降序,名称升序。 # 结果:输出按重量降序、名称升序排列的结果。 print(f"\n{'Example 12':*^50}") -power_tools.sort(key=lambda x: (-x.weight, x.name)) +power_tools.sort(key=lambda x: (-x.weight, x.name)) # 使重量降序,名称升序 print(power_tools) # Example 13 --- 处理无效的标准组合 +# 本质:本质是字符串不支持降序,数据类型的问题导致了冲突,而不是逻辑本身有问题。 +# 不能进行负号操作的类型包括:str(字符串)、bool(布尔)、NoneType(None)、dict(字典)、 +# list(列表)、tuple(元组)、set(集合)、frozenset(冻结集合)、bytes(字节)、 +# bytearray(可变字节数组)、memoryview(内存视图),以及排序时的 complex(复数)。 # 目的:展示当排序标准无效时会引发错误。 # 解释: # lambda x: (x.weight, -x.name) 试图对字符串使用负号操作是无效的,导致 TypeError。 diff --git a/example_code/item_15.py b/example_code/item_15.py index 69710fd..f19252e 100755 --- a/example_code/item_15.py +++ b/example_code/item_15.py @@ -16,12 +16,12 @@ # Reproduce book environment -# 军规 15: Know How to Use the key Parameter to Sort Dictionaries -# 军规 15: 理解如何使用 key 参数对字典进行排序 +# 军规 15: Be Cautious When Relying on dict Insertion Ordering +# 军规 15: 当依赖字典插入顺序时要小心 """ -Know How to Use the key Parameter to Sort Dictionaries -理解如何使用 key 参数对字典进行排序 +Be Cautious When Relying on dict Insertion Ordering +当依赖字典插入顺序时要小心 """ import random @@ -87,13 +87,9 @@ def close_open_files(): # my_func 使用 **kwargs 关键字参数,遍历并打印每个参数的键值对。 # 结果:输出 'goose' 和 'kangaroo' 的幼崽名称。 print(f"\n{'Example 6':*^50}") - - def my_func(**kwargs): for key, value in kwargs.items(): print(f'{key} = {value}') - - my_func(goose='gosling', kangaroo='joey') # Example 8 --- 使用类中的 __dict__ 属性获取实例的属性和值 @@ -102,14 +98,11 @@ def my_func(**kwargs): # MyClass 包含两个属性,使用 __dict__.items() 遍历实例的属性和值。 # 结果:输出实例的所有属性和值。 print(f"\n{'Example 8':*^50}") - - class MyClass: def __init__(self): self.alligator = 'hatchling' self.elephant = 'calf' - a = MyClass() for key, value in a.__dict__.items(): print(f'{key} = {value}') @@ -147,8 +140,6 @@ def populate_ranks(votes, ranks): # get_winner 函数使用 next 和 iter 从字典中获取第一个键,即排名最高的动物。 # 结果:返回排名最高的动物名称。 print(f"\n{'Example 11':*^50}") - - def get_winner(ranks): return next(iter(ranks)) @@ -192,11 +183,9 @@ def __iter__(self): keys.sort() for key in keys: yield key - def __len__(self): return len(self.data) - my_dict = SortedDict() my_dict['otter'] = 1 my_dict['cheeta'] = 2 @@ -232,8 +221,6 @@ def __len__(self): # get_winner 函数遍历 ranks 字典,查找排名为 1 的动物并返回。 # 结果:输出排名第一的动物。 print(f"\n{'Example 15':*^50}") - - def get_winner(ranks): for name, rank in ranks.items(): if rank == 1: From 2d50dd81fb0cd67cf3e9615856723baecba31887 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Wed, 9 Oct 2024 15:32:09 +0800 Subject: [PATCH 32/59] modify item_14.py --- example_code/item_15.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/example_code/item_15.py b/example_code/item_15.py index f19252e..1be9588 100755 --- a/example_code/item_15.py +++ b/example_code/item_15.py @@ -236,17 +236,23 @@ def get_winner(ranks): # get_winner 函数检查传入参数是否为 dict 类型,如果不是,则引发 TypeError。 # 结果:捕获并记录异常信息。 print(f"\n{'Example 16':*^50}") -try: - def get_winner(ranks): - if not isinstance(ranks, dict): - raise TypeError('must provide a dict instance') - return next(iter(ranks)) +def get_winner(ranks): + if not isinstance(ranks, dict): + raise TypeError('must provide a dict instance') + return next(iter(ranks)) - assert get_winner(ranks) == 'otter' +# 先检查 ranks 和 sorted_ranks 的类型 +try: + print(f"ranks type: {type(ranks)}") + print(f"sorted_ranks type: {type(sorted_ranks)}") + assert get_winner(ranks) == 'otter' get_winner(sorted_ranks) -except: - logging.exception('Expected') + +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False + + From c54475717ffceed2635c420f2ad87e5d050c3a97 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Wed, 9 Oct 2024 17:01:52 +0800 Subject: [PATCH 33/59] modify item_18.py --- example_code/item_18.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/example_code/item_18.py b/example_code/item_18.py index e5f8c41..0e018c4 100755 --- a/example_code/item_18.py +++ b/example_code/item_18.py @@ -16,12 +16,12 @@ # Reproduce book environment -# 军规 18: Use defaultdict for Missing Items Only When Key Access Is Common -# 军规 18: 仅在键访问频繁时使用 defaultdict 来处理缺失项 +# 军规 18: Know How to Construct Key-Dependent Default Values with __missing__ +# 军规 18: 了解如何使用 __missing__ 构造基于键的默认值 """ -Use defaultdict for Missing Items Only When Key Access Is Common -仅在键访问频繁时使用 defaultdict 来处理缺失项 +军规 18: Know How to Construct Key-Dependent Default Values with __missing__ +军规 18: 了解如何使用 __missing__ 构造基于键的默认值 """ import random @@ -216,8 +216,8 @@ def open_picture(profile_path): class Pictures(dict): def __missing__(self, key): - value = open_picture(key) - self[key] = value + value = open_picture(key) # 当键缺失时,调用 open_picture(key) 加载图片 + self[key] = value # 将加载的图片缓存到字典中 return value From 89782823de14238fbeb13f76642882f8443dd895 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Wed, 9 Oct 2024 19:16:19 +0800 Subject: [PATCH 34/59] modify item_19-21.py --- example_code/item_19.py | 62 ++++++++++++++++++++++++++++++++++++++--- example_code/item_20.py | 9 ++++-- example_code/item_21.py | 4 +-- 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/example_code/item_19.py b/example_code/item_19.py index 2a7231a..01e5b42 100755 --- a/example_code/item_19.py +++ b/example_code/item_19.py @@ -16,8 +16,9 @@ # Reproduce book environment -# 军规 19: Unpack Elements from Iterables of Arbitrary Length -# 军规 19: 从任意长度的可迭代对象中解包元素 +# 军规 19: Never Unpack More Than Three Variables When Functions Return Multiple Values +# 军规 19: 不要解包超过三个变量当函数返回多个值时 +# 隐含原因:解包超过三个变量会导致代码可读性降低,应该考虑重构代码,因为解包高度依赖顺序。 """ Unpack Elements from Iterables of Arbitrary Length @@ -67,12 +68,11 @@ def get_stats(numbers): return minimum, maximum lengths = [63, 73, 72, 60, 67, 66, 71, 61, 72, 70] - minimum, maximum = get_stats(lengths) # Two return values - print(f'Min: {minimum}, Max: {maximum}') + # Example 2 --- 解包多个返回值 # 目的:展示如何通过解包接收函数的多个返回值。 # 解释: @@ -91,6 +91,7 @@ def my_function(): assert second == 2 + # Example 3 --- 解包带有剩余元素的可迭代对象 # 目的:展示如何使用解包从一个可迭代对象中提取第一个、最后一个和剩余的中间元素。 # 解释: @@ -109,6 +110,8 @@ def get_avg_ratio(numbers): print(f'Shortest: {shortest:>4.0%}') + +# 反例:解包超过三个变量的反例,后面有针对这个函数的优化代码,详见上述改进后方案 # Example 4 --- 返回并解包统计数据 # 目的:展示如何返回并解包多个统计值,如最小值、最大值、平均值等。 # 解释: @@ -148,6 +151,29 @@ def get_stats(numbers): assert median == 2 assert count == 3 +# =================================上述改进后方案============================== +# namedtuple:这个namedtuple就是用于没有类方法,但是却要处理自定义类结果的那种场景设计的 +from collections import namedtuple + +Stats = namedtuple('Stats', ['minimum', 'maximum', 'average', 'median', 'count']) + +def get_stats(numbers): + minimum = min(numbers) + maximum = max(numbers) + count = len(numbers) + average = sum(numbers) / count + + sorted_numbers = sorted(numbers) + middle = count // 2 + if count % 2 == 0: + lower = sorted_numbers[middle - 1] + upper = sorted_numbers[middle] + median = (lower + upper) / 2 + else: + median = sorted_numbers[middle] + return Stats(minimum, maximum, average, median, count) + + # Example 5 --- 解包顺序错误 # 目的:展示解包时顺序错误会导致的潜在问题。 @@ -162,6 +188,7 @@ def get_stats(numbers): minimum, maximum, median, average, count = get_stats(lengths) +# 反例:解包超过三个变量的反例,后面有针对这个函数的优化代码,详见上述改进后方案 # Example 6 --- 多行解包 # 目的:展示如何将解包操作分多行进行。 # 解释: @@ -179,3 +206,30 @@ def get_stats(numbers): (minimum, maximum, average, median, count ) = get_stats(lengths) + +# =================================上述改进后方案============================== +# 使用 namedtuple 方案 +Stats = namedtuple('Stats', ['minimum', 'maximum', 'average', 'median', 'count']) + +def get_stats(numbers): + # 计算逻辑... + return Stats(minimum, maximum, average, median, count) + +# 通过字段访问数据 +stats = get_stats(lengths) +print(stats.minimum, stats.maximum, stats.average, stats.median, stats.count) + +# 使用字典方案 +def get_stats(numbers): + return { + 'minimum': minimum, + 'maximum': maximum, + 'average': average, + 'median': median, + 'count': count + } + +# 通过键名访问数据 +stats = get_stats(lengths) +print(stats['minimum'], stats['maximum'], stats['average'], stats['median'], stats['count']) + diff --git a/example_code/item_20.py b/example_code/item_20.py index f72127a..ce6a69b 100755 --- a/example_code/item_20.py +++ b/example_code/item_20.py @@ -16,8 +16,8 @@ # Reproduce book environment -# 军规 20: Know How Closures Interact with Variable Scope -# 军规 20: 理解闭包如何与变量作用域交互 +# 军规 20: Prefer Raising Exceptions to Returning None +# 军规 20: 优先抛出异常,而不是返回 None """ Know How Closures Interact with Variable Scope @@ -55,6 +55,7 @@ def close_open_files(): atexit.register(close_open_files) +# 反例:不是军规的推荐处理,因为返回了 None # Example 1 --- 初步处理异常的除法函数 # 目的:展示如何使用 try-except 捕获异常,处理除零的情况。 # 解释: @@ -145,6 +146,8 @@ def careful_divide(a, b): # Example 7 --- 引发自定义异常 # 目的:展示如何通过引发自定义异常来处理无效输入。 +# 备注:(抛出异常)属于中断级别错误,针对当前的处理方式, +# 而之前的None返回值是一种正常的返回值,属于识别级别错误 # 解释: # 当发生 ZeroDivisionError 时,通过 raise 引发自定义的 ValueError,指示无效输入。 # 结果:处理无效输入时抛出 ValueError。 @@ -192,6 +195,6 @@ def careful_divide(a: float, b: float) -> float: result = careful_divide(1, 0) assert False except ValueError: - pass # Expected + pass # Expected 针对当前的异常不进行处理的一种方式 assert careful_divide(1, 5) == 0.2 diff --git a/example_code/item_21.py b/example_code/item_21.py index 1595953..f5630e1 100755 --- a/example_code/item_21.py +++ b/example_code/item_21.py @@ -20,8 +20,8 @@ # 军规 21: 理解闭包如何与变量作用域交互 """ -Know How Closures Interact with Variable Scope -理解闭包如何与变量作用域交互 +军规 21: Know How Closures Interact with Variable Scope +军规 21: 理解闭包如何与变量作用域交互 """ import random From aab5e016be3c933e92980e952cb5b16fd308d9f9 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 14:27:17 +0800 Subject: [PATCH 35/59] modify item_21.py --- example_code/item_21.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/example_code/item_21.py b/example_code/item_21.py index f5630e1..f05efbd 100755 --- a/example_code/item_21.py +++ b/example_code/item_21.py @@ -22,6 +22,21 @@ """ 军规 21: Know How Closures Interact with Variable Scope 军规 21: 理解闭包如何与变量作用域交互 +闭包的目的是通过作用域链来封装状态并保持外部变量的引用, +它解决的是如何在不同函数间共享状态的问题,而不只是简单的限制访问。 +访问修饰符则是用来控制类或模块中的访问权限,这与闭包的设计意图不同。 +什么时候使用闭包: +当你需要持久化某些状态,但不希望用全局变量或类来实现时,闭包是一个很好的选择。 +当函数需要动态创建,但依然希望每个创建的函数能共享或保持某些外部数据时,闭包提供了优雅的解决方案。 +需要保持数据的私有性,即不希望外部直接访问数据,闭包可以实现这种数据封装。 + +闭包替代方案: +虽然闭包非常灵活,但如果场景更复杂(比如需要大量状态管理和行为),可能使用类和对象会更合适。 +类和对象可以显式地管理状态、行为和数据,且结构化更明确。 + +总结: +闭包并不是每个场景都需要用到,但在涉及状态封装、回调函数、工厂函数时,闭包是一种非常自然和简洁的解决方案。 +如果你需要保持状态、处理私有数据或构建动态逻辑,闭包可以为你提供一种有效的编程模式。 """ import random From 03588c95b2671f3be5c7536ddbc840148cf94ab6 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 15:07:24 +0800 Subject: [PATCH 36/59] modify item_22&26.py --- example_code/item_22.py | 30 ++++++++++++++++++++++-------- example_code/item_26.py | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/example_code/item_22.py b/example_code/item_22.py index 75b46c0..a1ee168 100755 --- a/example_code/item_22.py +++ b/example_code/item_22.py @@ -16,12 +16,24 @@ # Reproduce book environment -# 军规 22: Avoid Using More Than Two Positional Arguments -# 军规 22: 避免使用两个以上的位置参数 +# 军规 22: Reduce Visual Noise with Variable Positional Arguments +# 军规 22: 使用可变位置参数减少视觉干扰 """ -Avoid Using More Than Two Positional Arguments -避免使用两个以上的位置参数 +Reduce Visual Noise with Variable Positional Arguments +使用可变位置参数减少视觉干扰。 + +核心本质: +这一规则的核心在于简化函数签名和调用的复杂性,特别是在处理多个可选参数时, +避免让函数的调用变得过于冗长或复杂,从而减少视觉干扰,让代码更简洁、易读。 + +减少视觉噪音: + 当函数需要处理很多参数时,如果每个参数都明确列出,会让代码变得臃肿且难以理解。 + 通过使用 *args 可以隐藏不必要的细节,减少函数签名的冗长。 + +灵活性: + *args 提供了很大的灵活性,特别是在你无法预知参数数量时(如处理不定参数的场景)。 + 调用者可以传递任意数量的参数,函数也可以根据需要处理这些参数,而不必每次都修改函数签名。 """ import random @@ -58,7 +70,8 @@ def close_open_files(): # Example 1 --- 初始实现的日志函数 # 目的:展示如何通过位置参数传递消息和列表值。 # 解释: -# log 函数接受两个参数,message 和 values,如果 values 列表为空,仅打印消息,否则打印消息和 values。 +# log 函数接受两个参数,message 和 values, +# 如果 values 列表为空,仅打印消息,否则打印消息和 values。 # 结果:输出日志消息和数值列表。 print(f"\n{'Example 1':*^50}") def log(message, values): @@ -102,12 +115,13 @@ def log(message, *values): # The only difference # Example 4 --- 使用 *args 解包生成器 # 目的:展示如何通过 *args 解包生成器并将其元素传递给函数。 # 解释: -# my_generator 是一个生成器,通过 *it 解包生成器,将其所有元素传递给 my_func,函数接收到的参数为生成器的所有元素。 +# my_generator 是一个生成器,通过 *it 解包生成器, +# 将其所有元素传递给 my_func,函数接收到的参数为生成器的所有元素。 # 结果:输出生成器中所有生成的值。 print(f"\n{'Example 4':*^50}") def my_generator(): for i in range(10): - yield i + yield i # yield 则是在生成一个值后暂时挂起函数的状态,保存局部变量,并在下次迭代时从该状态继续执行 def my_func(*args): print(args) @@ -131,4 +145,4 @@ def log(sequence, message, *values): log(1, 'Favorites', 7, 33) # New with *args OK log(1, 'Hi there') # New message only OK -log('Favorite numbers', 7, 33) # Old usage breaks +log('Favorite numbers', 7, 33,34) # Old usage breaks diff --git a/example_code/item_26.py b/example_code/item_26.py index 13b9def..5672fee 100755 --- a/example_code/item_26.py +++ b/example_code/item_26.py @@ -15,7 +15,7 @@ # limitations under the License. -# 军规 26: Use functools.wraps to Improve Decorators +# 军规 26: Define Function Decorators with functools.wraps # 军规 26: 使用 functools.wraps 改善装饰器 """ From fa6921d2e6a29c6b22a3cf951c29ba6415c71ab3 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 15:43:35 +0800 Subject: [PATCH 37/59] modify item_22&26.py --- example_code/item_23.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/example_code/item_23.py b/example_code/item_23.py index 6270709..e64831f 100755 --- a/example_code/item_23.py +++ b/example_code/item_23.py @@ -17,11 +17,23 @@ # Reproduce book environment # 军规 23: Provide Optional Behavior with Keyword Arguments -# 军规 23: 使用关键字参数提供可选行为 +# 军规 23: 通过关键字参数提供可选行为 """ Provide Optional Behavior with Keyword Arguments -使用关键字参数提供可选行为 +通过关键字参数提供可选行为。 + +这条规则的核心在于: +使用关键字参数(keyword arguments)可以让函数更加灵活和可读,从而支持可选的行为。 +通过为某些参数提供默认值并允许调用者使用关键字来传递参数,函数能够变得更加灵活, +避免过多的位置参数使得函数调用变得混乱难懂。 + +需要关注点: +*args: +用于接收任意数量的无名参数,这些参数会按照位置传递。 +这种方式更简洁,适合不关心参数含义的情况(如求和函数、处理批量数据)。 +**kwargs 和关键字参数: +用于传递带名字的参数,让调用者清楚每个参数的意义。它让代码更具可读性和维护性。 """ import random @@ -89,8 +101,8 @@ def remainder(number, divisor): # This will not compile source = """remainder(number=20, 7)""" eval(source) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -103,8 +115,8 @@ def remainder(number, divisor): print(f"\n{'Example 4':*^50}") try: remainder(20, number=7) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False From 14e08796dd1bfd912ec6e47f979591b0ff9a9562 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 16:56:07 +0800 Subject: [PATCH 38/59] modify item_14-24-25.py --- example_code/item_14.py | 4 ++-- example_code/item_24.py | 18 ++++++++++++++---- example_code/item_25.py | 27 ++++++++++++++++++--------- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/example_code/item_14.py b/example_code/item_14.py index e517f12..f65b447 100755 --- a/example_code/item_14.py +++ b/example_code/item_14.py @@ -226,8 +226,8 @@ def __repr__(self): try: power_tools.sort(key=lambda x: (x.weight, -x.name), reverse=True) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False diff --git a/example_code/item_24.py b/example_code/item_24.py index 4fa6c41..c1fc2fb 100755 --- a/example_code/item_24.py +++ b/example_code/item_24.py @@ -17,11 +17,21 @@ # Reproduce book environment # 军规 24: Use None and Docstrings to Specify Dynamic Default Arguments -# 军规 24: 使用 None 和文档字符串来指定动态默认参数 +# 军规 24: 使用 None 和文档字符串(docstrings)来指定动态的默认参数 """ Use None and Docstrings to Specify Dynamic Default Arguments 使用 None 和文档字符串来指定动态默认参数 +根本用意: +其实就是不要用可变变量来作为默认值,本质含义是这个,因为可变变量是处理链条上共享的变量,会导致不可预期的行为。 + +本质:为什么要这样做? +在 Python 中,默认参数的定义有一个潜在的陷阱:如果默认参数是一个可变对象(如列表、字典), +那么该对象会在所有调用之间共享,导致不期望的行为。 +这条规则的核心建议是: +避免使用可变对象作为默认参数(如 list 或 dict)。 +使用 None 作为默认值,并在函数内部根据需要初始化正确的值。 +通过 docstring 明确说明参数的默认行为,让调用者知道实际的逻辑。 """ import random @@ -102,7 +112,7 @@ def log(message, when=None): log('Hello again!') -# Example 4 --- 使用可变对象作为默认参数 +# Example 4 --- 使用可变对象作为默认参数---反例 # 目的:展示在默认参数中使用可变对象(如字典)可能导致的错误。 # 解释: # decode 函数使用一个空字典作为默认值,这导致每次调用 decode 时都返回同一个字典实例。 @@ -117,7 +127,7 @@ def decode(data, default={}): return default -# Example 5 --- 调用 decode 函数并修改结果 +# Example 5 --- 调用 decode 函数并修改结果---上述反例的阐述 # 目的:展示使用可变默认参数时导致的错误行为。 # 解释: # 两次调用 decode 函数时,返回的字典是同一个对象,修改 foo 也会影响 bar。 @@ -131,7 +141,7 @@ def decode(data, default={}): print('Bar:', bar) -# Example 6 --- 断言 foo 和 bar 是同一个对象 +# Example 6 --- 断言 foo 和 bar 是同一个对象---上述反例的阐述 # 目的:展示 foo 和 bar 实际上是同一个对象。 # 解释: # 使用 assert 语句验证 foo 和 bar 是同一个字典实例。 diff --git a/example_code/item_25.py b/example_code/item_25.py index 73a0c07..2c6affb 100755 --- a/example_code/item_25.py +++ b/example_code/item_25.py @@ -16,11 +16,20 @@ # 军规 25: Enforce Clarity with Keyword-Only and Positional-Only Arguments -# 军规 25: 使用仅限关键字参数和仅限位置参数来保证代码清晰 +# 军规 25: 通过关键字参数和仅限位置参数来强化代码的清晰性 """ -Enforce Clarity with Keyword-Only and Positional-Only Arguments -使用仅限关键字参数和仅限位置参数来保证代码清晰 +# 军规 25: Enforce Clarity with Keyword-Only and Positional-Only Arguments +# 军规 25: 通过关键字参数和仅限位置参数来强化代码的清晰性 +核心本质:必填项和自定义项的场景区分方式的应对策略。 + +总结 +这条规则的核心在于通过强制参数的传递方式来提升代码的可读性和安全性。 +对于一些简单而常用的参数,位置传递更简洁;而对于复杂的、多参数的情况,关键字传递能避免混淆。利用 / 和 *, +你可以灵活控制哪些参数必须通过位置传递,哪些参数必须使用关键字传递,让你的代码更加清晰和健壮。 + +/ 用于声明仅限位置的参数,调用时只能按顺序传递。 +* 用于声明仅限关键字的参数,调用时必须带上参数名。 """ # Reproduce book environment @@ -174,8 +183,8 @@ def safe_division_c(number, divisor, *, # Changed print(f"\n{'Example 8':*^50}") try: safe_division_c(1.0, 10**500, True, False) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -239,8 +248,8 @@ def safe_division_c(numerator, denominator, *, # Changed print(f"\n{'Example 12':*^50}") try: safe_division_c(number=2, divisor=5) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False @@ -285,8 +294,8 @@ def safe_division_d(numerator, denominator, /, *, # Changed print(f"\n{'Example 15':*^50}") try: safe_division_d(numerator=2, denominator=5) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False From 8f34e626be855b861720c0640b696c1f8402870f Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 17:33:28 +0800 Subject: [PATCH 39/59] modify item_26.py --- example_code/item_26.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/example_code/item_26.py b/example_code/item_26.py index 5672fee..bae8f07 100755 --- a/example_code/item_26.py +++ b/example_code/item_26.py @@ -19,8 +19,20 @@ # 军规 26: 使用 functools.wraps 改善装饰器 """ -Use functools.wraps to Improve Decorators -使用 functools.wraps 改善装饰器 +# 军规 26: Define Function Decorators with functools.wraps +# 军规 26: 使用 functools.wraps 改善装饰器 + +本质: +为什么要使用 functools.wraps? +functools.wraps 是一个装饰器工具,用于帮助你正确地定义函数装饰器, +并保留原始函数的元信息(如函数名、文档字符串 __doc__ 和参数信息)。 +Python 的装饰器是非常强大的工具,可以用来增强函数的功能(如添加日志、权限检查等), +但不使用 functools.wraps 可能会导致一些元信息丢失,从而影响代码的可读性和调试。 + +重要关注点: +(1)并不是闭包本身需要 return wrapper,而是装饰器(装潢后的闭包)需要返回 wrapper, +这样才能实现用增强后的函数替换原始函数。 +(2)了解了固定的就用@XXX,动态的就手动呗 """ # Reproduce book environment @@ -71,7 +83,6 @@ def wrapper(*args, **kwargs): print(f'{func.__name__}({args!r}, {kwargs!r}) ' f'-> {result!r}') return result - return wrapper @@ -142,8 +153,8 @@ def fibonacci(n): import pickle pickle.dumps(fibonacci) -except: - logging.exception('Expected') +except Exception as e: + logging.error(f"Error type: {e.__class__.__name__}, Message: {str(e)}") else: assert False From 15c792d00e0026806e096e962c1fc9933e33c8d8 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 18:24:27 +0800 Subject: [PATCH 40/59] modify item_27.py --- example_code/item_27.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/example_code/item_27.py b/example_code/item_27.py index b57a648..0e13915 100755 --- a/example_code/item_27.py +++ b/example_code/item_27.py @@ -14,12 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -# 军规 27: Prefer list comprehensions and generator expressions to map and filter. -# 军规 27: 优先使用列表推导式和生成器表达式,而不是 map 和 filter。 +# 军规 27: Use Comprehensions Instead of map and filter. +# 军规 27: 使用推导取代 map 和 filter """ -Prefer list comprehensions and generator expressions to map and filter -优先使用列表推导式和生成器表达式,而不是 map 和 filter +# 军规 27: Use Comprehensions Instead of map and filter. +# 军规 27: 使用推导取代 map 和 filter """ # Reproduce book environment @@ -101,8 +101,8 @@ def close_open_files(): # 目的:展示如何使用字典推导式和集合推导式。 # 结果:输出偶数平方的字典和可被3整除的数的立方的集合。 print(f"\n{'Example 6':*^50}") -even_squares_dict = {x: x**2 for x in a if x % 2 == 0} -threes_cubed_set = {x**3 for x in a if x % 3 == 0} +even_squares_dict = {x: x**2 for x in a if x % 2 == 0} # 字段推导式 +threes_cubed_set = {x**3 for x in a if x % 3 == 0} # 集合推导式 print(even_squares_dict) print(threes_cubed_set) @@ -112,8 +112,10 @@ def close_open_files(): # 结果:确保字典和集合的结果与之前的推导式相同。 print(f"\n{'Example 7':*^50}") alt_dict = dict(map(lambda x: (x, x**2), - filter(lambda x: x % 2 == 0, a))) + filter(lambda x: x % 2 == 0, a))) # 字典推导式 alt_set = set(map(lambda x: x**3, - filter(lambda x: x % 3 == 0, a))) + filter(lambda x: x % 3 == 0, a))) # 集合推导式 assert even_squares_dict == alt_dict assert threes_cubed_set == alt_set +print(alt_dict) +print(alt_set) From 2e21811688053add34d89194a415c50710e8bd8b Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 18:43:44 +0800 Subject: [PATCH 41/59] modify item_28.py --- example_code/item_28.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/example_code/item_28.py b/example_code/item_28.py index 70ac238..171d221 100755 --- a/example_code/item_28.py +++ b/example_code/item_28.py @@ -14,22 +14,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -# 军规 28: 用列表推导式代替嵌套循环 -# 军规 28: Use list comprehensions instead of nested loops +# 军规 28: Avoid More Than Two Control Subexpressions in Comprehensions +# 军规 28: 避免在推导式中使用超过两个控制表达式 """ -Use list comprehensions instead of nested loops -用列表推导式代替嵌套循环 +Avoid More Than Two Control Subexpressions in Comprehensions +避免在推导式中使用超过两个控制表达式 + +关键点:只是针对复杂场景进行了多层嵌套,而每层确实只处理两个逻辑表达式。 """ # Reproduce book environment import random random.seed(1234) -import logging -from pprint import pprint -from sys import stdout as STDOUT - # Write all output to a temporary directory import atexit import gc From 15980af08246b819c2ad0222695d512f1bc1139a Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 18:54:40 +0800 Subject: [PATCH 42/59] modify item_29.py --- example_code/item_29.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/example_code/item_29.py b/example_code/item_29.py index 3bfa503..fa037c6 100755 --- a/example_code/item_29.py +++ b/example_code/item_29.py @@ -14,12 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -# 军规 29: 只在表达式上下文中使用赋值表达式 -# 军规 29: Use assignment expressions only in expression contexts +# 军规 29: Avoid Repeated Work in Comprehensions by Using Assignment Expressions +# 军规 29: 使用赋值表达式在推导中避免重复代码 111 """ -Use assignment expressions only in expression contexts -只在表达式上下文中使用赋值表达式 +# 军规 29: Avoid Repeated Work in Comprehensions by Using Assignment Expressions +# 军规 29: 在推导式中使用赋值表达式,避免重复计算 111 + +关键点: 其实就是用赋值表达式来避免重复计算,提高代码的可读性和性能。 +例子: 海象操作符 := 避免重复调用 len() +避免在推导式内多次计算同一结果,用海象操作符把值存到变量里,再在后续逻辑中直接透传使用。 """ # Reproduce book environment From 4f608a95b78d302cb298f76a50469c8e19519f12 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 19:02:40 +0800 Subject: [PATCH 43/59] modify item_30.py --- example_code/item_30.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/example_code/item_30.py b/example_code/item_30.py index 9e53889..11857b2 100755 --- a/example_code/item_30.py +++ b/example_code/item_30.py @@ -14,6 +14,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 军规 30: Consider Generators Instead of Returning Lists +# 军规 30: 考虑使用生成器代替返回列表。 + +""" +# 军规 30: Consider Generators Instead of Returning Lists +# 军规 30: 考虑使用生成器代替返回列表。 + +军规总结: +列表适合小数据集:如果你明确知道所有数据量较小且需要一次性处理,可以返回列表。 +生成器适用于大数据或流式处理:当数据量大、需要按需生成或是要处理无限序列时,生成器是更优雅的选择。 +节省资源、提高性能:生成器通过惰性计算避免内存浪费,并且让代码更灵活、更简洁。 + +一句话: +生成器就像水流般按需提供数据,而列表则像一桶水,需要提前全部装满。 +如果你需要轻巧灵活的操作,生成器无疑是更好的选择。 +""" + # Reproduce book environment import random random.seed(1234) From 6074d30b878f67283ec48ca8a1cdefab616458a0 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 19:19:15 +0800 Subject: [PATCH 44/59] modify item_30.py --- example_code/item_31.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/example_code/item_31.py b/example_code/item_31.py index 039a713..aa373ed 100755 --- a/example_code/item_31.py +++ b/example_code/item_31.py @@ -15,6 +15,22 @@ # limitations under the License. # Reproduce book environment + +# 军规 31: Be Defensive When Iterating Over Arguments +# 军规 31: 迭代参数时要保持防御性 + +""" +# 军规 31: Be Defensive When Iterating Over Arguments +# 军规 31: 迭代参数时要保持防御性 +注意重点: +(1)其实关键点在于对传入参数信息本身,如果是不能改变的场景那么需要进行防御性编程,如果去不是,那其实是没有必要的。 +(2)还要注意迭代器的特性,迭代器只能迭代一次,如果需要多次迭代,那么需要将迭代器转换为容器。 +(3)将迭代器转换为容器是为了确保数据可以多次使用,但这也会带来内存的代价,所以需要权衡。 +(4)这个是一种特殊的场景权衡,要看是不是出现需要反复迭代的场景出现, +如果出现了是可以进行生成器到容器的转换,坏处是牺牲了资源,但是这个是没有办法的, +因为生成器不能多次迭代导致的。 +""" + import random random.seed(1234) From 58c4e2e43ec1bbdeb452648a7462d1d97f14be5f Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Sat, 12 Oct 2024 19:25:56 +0800 Subject: [PATCH 45/59] modify item_30.py --- example_code/item_32.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/example_code/item_32.py b/example_code/item_32.py index bc1c01e..e6be883 100755 --- a/example_code/item_32.py +++ b/example_code/item_32.py @@ -15,6 +15,26 @@ # limitations under the License. # Reproduce book environment + +# 军规 32: Consider Generator Expressions for Large List Comprehensions +# 军规 32: 对于大型列表推导式,考虑使用生成器表达式 + +""" +军规的关键在于场景选择: +如果你的逻辑比较复杂,需要多步生成数据,使用**生成器函数(yield)**是更好的选择。 +如果只是单行简单逻辑(如筛选和映射),而且数据量较大时,用生成器表达式替代列表推导式会更优雅。 + +总结: +生成器与生成器表达式的应用区分 + +大数据量场景: +生成器(yield)和生成器表达式都适用。 + +逻辑复杂性: +如果逻辑简单 ➡️ 用生成器表达式。 +如果逻辑复杂 ➡️ 用生成器函数。 +""" + import random random.seed(1234) From 2a6560d88f48488d17f3446a8f374724ee5a61b6 Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Sun, 13 Oct 2024 10:40:56 +0800 Subject: [PATCH 46/59] modify item_27.py,item_33.py --- example_code/item_27.py | 1 + example_code/item_33.py | 43 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/example_code/item_27.py b/example_code/item_27.py index 0e13915..8b04e45 100755 --- a/example_code/item_27.py +++ b/example_code/item_27.py @@ -18,6 +18,7 @@ # 军规 27: 使用推导取代 map 和 filter """ +Comprehensions and Generators # 军规 27: Use Comprehensions Instead of map and filter. # 军规 27: 使用推导取代 map 和 filter """ diff --git a/example_code/item_33.py b/example_code/item_33.py index e11a7b0..c4c597c 100755 --- a/example_code/item_33.py +++ b/example_code/item_33.py @@ -14,7 +14,24 @@ # See the License for the specific language governing permissions and # limitations under the License. -# 军规33:尽量减少复杂性,优先选择简单的解决方案,保持代码可读性和可维护性。 +# 军规33: Compose multiple generators with yield from. +# 军规33: 使用yield from组合多个生成器。 + +""" + +总结:生成器的本质在于 yield,而不是 range() + yield 是生成器的核心: + 它允许函数暂停执行,并逐次返回值。 + range() 只是用于控制循环的次数,它并不是生成器的关键。 + +总结:yield vs. switch 的应用场景 + yield: + 适合顺序处理和状态记忆,如生成器、流处理、状态机模拟等场景。 + 允许在多次调用之间保持状态。 + switch: + 适合条件匹配和快速逻辑切换,如根据用户输入执行不同逻辑。 + 不记忆状态,每次匹配都是独立的。 +""" # Reproduce book environment import random @@ -50,6 +67,7 @@ def close_open_files(): # Example 1 print(f"\n{'Example 1':*^50}") # 定义一个生成器函数,模拟移动 +# 注意这里的_是一个占位符,表示不关心的值 def move(period, speed): for _ in range(period): yield speed # 生成速度值 @@ -62,6 +80,9 @@ def pause(delay): # Example 2 print(f"\n{'Example 2':*^50}") # 定义一个动画函数,组合移动和暂停 +# 总结:delta 是生成器的逐次输出值 +# 在你的代码中,delta 是 每次从生成器取出的值。 +# 它代表了每个时间步的变化(速度或暂停)。 def animate(): for delta in move(4, 5.0): # 移动4个单位,速度为5.0 yield delta @@ -94,10 +115,30 @@ def animate_composed(): run(animate_composed) # 运行组合动画函数 # Example 5 + print(f"\n{'Example 5':*^50}") import timeit # 定义生成器,产生1000000个数字 + +# 手动嵌套 vs. yield from:本质区别 +# 手动嵌套生成器: +# 用**for 循环**遍历内层生成器,并逐一 yield 出结果。 +# 这种方式虽然可以工作,但代码更冗长,性能也稍逊。 + +# yield from 简化生成器: +# yield from 直接将子生成器的所有值传递给外层生成器,更高效,底层的 Python 实现会做优化。 +# 代码也更加简洁,没有冗余的循环。 + +# 为什么需要传递 globals()? +# 字符串代码的作用域问题: +# +# 当 stmt 是字符串形式时,它不会直接继承当前模块的上下文,需要手动指定作用域。 +# globals() 提供全局命名空间: +# +# 将当前模块的全局命名空间传递给 timeit,确保所有全局定义的函数、变量都能被访问。 + + def child(): for i in range(1_000_000): yield i From feb0721af7209b6a314ff79f64b04b63f1f1eea0 Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Sun, 13 Oct 2024 10:54:40 +0800 Subject: [PATCH 47/59] modify item_34.py --- example_code/item_34.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/example_code/item_34.py b/example_code/item_34.py index 4f11150..2bcce61 100755 --- a/example_code/item_34.py +++ b/example_code/item_34.py @@ -14,7 +14,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -# 军规34:尽量减少复杂性,优先选择简单的解决方案,保持代码可读性和可维护性。 +# 军规34:Avoid injecting data into generators with send +# 军规34:避免使用 send 向生成器注入数据 + +""" +总结: +send() 允许向生成器传入数据,但会增加复杂性,导致代码难以维护。 +推荐做法:通过函数参数或外部类传递数据,让生成器专注于生成值,保持代码简洁清晰。 + +推荐替代方案:使用函数参数或外部状态 +方案 1:通过函数参数传递数据。 +方案 2:使用类管理状态。 --- 待参考 +方案 3: send() 替代方案:闭包函数管理状态。 --- 待参考 +""" + # Reproduce book environment import random From a1bd0b3bcac116f79292b4ec63fdd227ebc3be1a Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Sun, 13 Oct 2024 11:01:15 +0800 Subject: [PATCH 48/59] modify item_35.py --- example_code/item_35.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/example_code/item_35.py b/example_code/item_35.py index 1548f00..b4c06ce 100755 --- a/example_code/item_35.py +++ b/example_code/item_35.py @@ -3,6 +3,20 @@ # Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. # Licensed under the Apache License, Version 2.0 (the "License"); +# 军规35: Avoid Causing State Transitions in Generators with throw +# 生成器中避免使用 throw 引发状态转换 + +""" +整体总结: +不要使用 throw() 在生成器中引发状态转换,因为它增加了代码的复杂性和维护难度。 +推荐做法:将状态管理和异常处理逻辑放在生成器外部,或者使用状态机实现复杂状态转换。 + +特定场景总结:何时使用这种模式? +适用场景:当生成器需要处理复杂的状态管理,如可重置计时器、重试逻辑等。 +避免滥用:如果生成器逻辑简单,尽量不要使用 throw() 进行状态转换,以免增加不必要的复杂性。 +比如范例:在计时器生成器中,使用 throw() 实现重置计时器的功能。 timer() 生成器在收到 Reset 异常时,重置计数器。 +""" + import random random.seed(1234) @@ -165,7 +179,6 @@ def __iter__(self): False, False, True, False, True, False, False, False, False, False, False, False, False] - def run(): timer = Timer(4) for current in timer: @@ -173,5 +186,4 @@ def run(): timer.reset() announce(current) - run() From da5c606f0f2eeb7bd3ec81e2a31057ae76e60edd Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Sun, 13 Oct 2024 11:23:07 +0800 Subject: [PATCH 49/59] modify item_36.py --- example_code/item_36.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/example_code/item_36.py b/example_code/item_36.py index 6d33134..1cb3a91 100755 --- a/example_code/item_36.py +++ b/example_code/item_36.py @@ -3,17 +3,29 @@ # Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. # Licensed under the Apache License, Version 2.0 (the "License"); +# 军规 36:Consider itertools for Working with Iterators and Generators。 +# 军规 36:在处理迭代器和生成器时,建议使用 itertools 库。 + +""" +总结:为什么推荐使用 itertools? +在处理生成器和迭代器时,优先考虑使用 itertools 提供的工具,以简化逻辑,提高性能。 +减少代码复杂度: +使用 itertools可以避免手写复杂的循环和生成器逻辑。 +提高代码效率:--- 惰性求值 +所有工具都使用惰性求值,按需生成数据,节省内存。 +灵活组合: +你可以将 itertools 的工具自由组合,实现复杂的迭代逻辑。 +""" + import random random.seed(1234) - -import logging from pprint import pprint -from sys import stdout as STDOUT import atexit import gc import io import os import tempfile +import itertools TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) @@ -49,10 +61,11 @@ def close_open_files(): # Example 3 # 目的:演示 itertools.cycle 的用法。 # 结果:循环输出指定序列的元素。 +# 输出:[1, 2, 1, 2, 1, 2, 1, 2, 1, 2] print(f"\n{'Example 3':*^50}") -it = itertools.cycle([1, 2]) -result = [next(it) for _ in range(10)] -print(result) +it = itertools.cycle([1, 2]) # 无限循环遍历 [1, 2] +result = [next(it) for _ in range(10)] # 生成10个元素的列表 +print(result) # 输出前10个元素 # Example 4 From fe7443724c843af2f41d874aa8c07bc7373e0a94 Mon Sep 17 00:00:00 2001 From: tonyxu2028 <191284969@qq.com> Date: Sun, 13 Oct 2024 11:34:33 +0800 Subject: [PATCH 50/59] modify item_37.py --- example_code/item_37.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/example_code/item_37.py b/example_code/item_37.py index 37aa573..881c455 100755 --- a/example_code/item_37.py +++ b/example_code/item_37.py @@ -15,6 +15,12 @@ # limitations under the License. # Reproduce book environment + +# 新的章节Classes And Interfaces + +# 军规37:Compose Classes Instead of Nesting Many Levels of Built-in Types +# 目的:使用嵌套的内置类型时,应该考虑使用类来替代。 + import random random.seed(1234) From 96ca3573d66c30686942a1b2bcf42f25dcb8ae1e Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 21 Oct 2024 15:26:14 +0800 Subject: [PATCH 51/59] modify item_37-39.py --- example_code/item_37.py | 6 ++++++ example_code/item_38.py | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/example_code/item_37.py b/example_code/item_37.py index 881c455..d7bf574 100755 --- a/example_code/item_37.py +++ b/example_code/item_37.py @@ -21,6 +21,12 @@ # 军规37:Compose Classes Instead of Nesting Many Levels of Built-in Types # 目的:使用嵌套的内置类型时,应该考虑使用类来替代。 +""" +这一军规的核心确实就是 OOP(面向对象编程) 的思想:用类的组合来替代多层嵌套的内置类型。 +但它的价值不只是单纯引入类,而在于鼓励你在 Python 中适当地使用面向对象的设计, +避免滥用内置数据结构,让代码更清晰、可维护。 +""" + import random random.seed(1234) diff --git a/example_code/item_38.py b/example_code/item_38.py index 5951a9a..638e84b 100755 --- a/example_code/item_38.py +++ b/example_code/item_38.py @@ -15,6 +15,18 @@ # limitations under the License. # Reproduce book environment + +# 军规 38:Accept Functions Instead of Classes for Simple Interfaces +# 军规 38:对于简单接口,接受函数而不是类 + +""" +核心意图:当类过于笨重时,用函数作为简单接口 +军规的背景: +在某些简单的场景中,我们习惯定义一个类来实现特定行为,但其实只需要一个函数就能完成任务。 +这条军规建议你避免不必要的类,当一个简单的函数就能搞定时,不要引入复杂的类。 +简单任务简单做,保持代码清晰直观。 +""" + import random random.seed(1234) From 33484b7ce84edf57ab1badcd06f7e96c75e42abf Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 21 Oct 2024 15:26:19 +0800 Subject: [PATCH 52/59] modify item_37-39.py --- example_code/item_39.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/example_code/item_39.py b/example_code/item_39.py index 53ea15a..0e789bc 100755 --- a/example_code/item_39.py +++ b/example_code/item_39.py @@ -15,6 +15,11 @@ # limitations under the License. # Reproduce book environment + +# 军规 39:Use @classmethod Polymorphism to Construct Objects Generically +# 军规 39:使用 @classmethod 实现多态,泛化对象构造 + + import random random.seed(1234) From f90a461e3ac1c69899d9628df2f38aa0261c1e5a Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 24 Oct 2024 12:03:09 +0800 Subject: [PATCH 53/59] modify item_39-40.py --- example_code/item_39.py | 17 +++++++++++++++++ example_code/item_40.py | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/example_code/item_39.py b/example_code/item_39.py index 0e789bc..2c7fdbb 100755 --- a/example_code/item_39.py +++ b/example_code/item_39.py @@ -19,6 +19,23 @@ # 军规 39:Use @classmethod Polymorphism to Construct Objects Generically # 军规 39:使用 @classmethod 实现多态,泛化对象构造 +""" +这条军规的目的是通过 @classmethod 来实现灵活且多态的对象构造, +避免把所有构造逻辑堆在 __init__ 方法里。 +通过 @classmethod,我们可以根据不同的数据来源或需求, +定义多个构造方法,并且让子类可以通过继承和重写这些方法来支持多态。 + +简化 __init__ 方法:避免在 __init__ 中堆积复杂的构造逻辑,使得构造方法职责单一。 +扩展性强:通过 @classmethod,可以轻松添加更多的构造方法,而无需修改现有代码。 +支持继承和多态:子类可以继承和重写这些类方法,实现自定义的构造方式,灵活支持不同的数据输入。 +代码更加 Pythonic:这种方法使代码更简洁、灵活,符合 Python 的设计哲学。 + +总结: +明白了问题在于__init__只能有一个,所以针对不同构造场景来说,就需要进行分流处理,会很复杂, +而通过@classmethod就派生了多个类似_init_的功能从而进行了,构造方法的多态, +每个只关注自己的构造,而__init__也其实只是其中之一,没有之前分流处理烦恼 +""" + import random random.seed(1234) diff --git a/example_code/item_40.py b/example_code/item_40.py index 284f831..b6681dc 100755 --- a/example_code/item_40.py +++ b/example_code/item_40.py @@ -19,6 +19,28 @@ from example_code.item_41 import ToDictMixin +# 军规 40:Initialize Parent Classes with super +# 军规 40:使用 super()强制调用父类的方法 + +""" +本军规总结:子类构造与 super() 的使用 +(1)子类不会自动调用父类的构造方法:在构造函数中,子类必须通过 super() 显式调用父类的构造方法, +确保父类的初始化逻辑被执行。 +(2)普通方法自动继承:子类自动继承父类的普通方法,不需要使用 super()。 +只有当子类覆盖了父类的方法时,才需要用 super() 调用父类的同名方法。 +(3)super() 在覆盖方法时使用:当子类覆盖父类的方法并希望调用父类的逻辑时, +super() 是必须的。它确保在子类重写时,父类的方法仍然能够被调用。 +(4)super() 和 MRO:super() 通过方法解析顺序(MRO)来查找父类的方法,支持多重继承, +确保每个父类都按照正确的顺序被调用。 + +MRO总结:MRO 的本质---MRO(方法解析顺序) +MRO 是 Python 在多重继承场景下,决定方法调用顺序的机制。 +MRO 遵循 C3 线性化算法,确保在多重继承中,每个类只会被调用一次,解决了菱形继承问题。 +通过 super(),你可以根据 MRO 顺序调用父类的方法,而不需要显式指定具体的父类。 +MRO 顺序可以通过 __mro__ 属性或 mro() 方法查看,帮助你理解类的继承链。 +(重要注解:其实就是通过MRO建立的了方法调用链,而MRO由是基于C3的所以规避了菱形继承问题) +""" + random.seed(1234) import logging From be9a71d58a7594aeead7c57b5c762beb7f7ff532 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 24 Oct 2024 12:45:03 +0800 Subject: [PATCH 54/59] modify item_40-41.py --- example_code/item_40.py | 7 ++++--- example_code/item_41.py | 27 ++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/example_code/item_40.py b/example_code/item_40.py index b6681dc..d81fe76 100755 --- a/example_code/item_40.py +++ b/example_code/item_40.py @@ -15,9 +15,6 @@ # limitations under the License. # Reproduce book environment -import random - -from example_code.item_41 import ToDictMixin # 军规 40:Initialize Parent Classes with super # 军规 40:使用 super()强制调用父类的方法 @@ -41,6 +38,10 @@ (重要注解:其实就是通过MRO建立的了方法调用链,而MRO由是基于C3的所以规避了菱形继承问题) """ +import random + +from example_code.item_41 import ToDictMixin + random.seed(1234) import logging diff --git a/example_code/item_41.py b/example_code/item_41.py index 971fbb7..b1398aa 100755 --- a/example_code/item_41.py +++ b/example_code/item_41.py @@ -14,6 +14,32 @@ # See the License for the specific language governing permissions and # limitations under the License. + +# 军规 41:Consider Composing Functionality with Mix-in Classes + +""" +混合类总结 +定义:混合类是一种不能被实例化的类,主要用于为其他类提供通用功能。 + +特点: +无 __init__ 方法:通常不定义构造方法,避免直接实例化。 +功能赋能:通过方法的组合为其他类提供额外的功能。 +高度可复用:允许多个类通过继承混合类来共享功能,减少代码重复。 + +设计目的: +简化复用成本:让功能复用更简便,提高代码的灵活性和可维护性。 +避免复杂继承层次:通过组合而非多层继承,减少类之间的复杂关系。 + +与抽象类的对比: +混合类不需要实例化,而抽象类可以提供实现方法并允许实例化。 +混合类主要用于提供功能,而抽象类通常用于定义接口和共同特征。 + +使用场景: +功能模块化:适用于将某些特定功能模块化,以便在多个类之间共享。 +减少继承冲突:通过组合不同的混合类,减少由于复杂继承造成的潜在冲突。 +这种设计模式在 Python 中广泛应用,尤其是在需要灵活性和代码复用的场景中。 +""" + # Reproduce book environment import random random.seed(1234) @@ -49,7 +75,6 @@ def close_open_files(): atexit.register(close_open_files) - # Example 1 # 目的:定义一个混合类 # 解释:定义一个混合类 ToDictMixin。 From 55e172eebecbff133d6db8f57394214804353eab Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Thu, 24 Oct 2024 14:59:55 +0800 Subject: [PATCH 55/59] modify item_41-43.py --- example_code/item_41.py | 25 +++++++++++++++++++++++++ example_code/item_42.py | 10 ++++++++++ example_code/item_43.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/example_code/item_41.py b/example_code/item_41.py index b1398aa..a5de589 100755 --- a/example_code/item_41.py +++ b/example_code/item_41.py @@ -16,6 +16,7 @@ # 军规 41:Consider Composing Functionality with Mix-in Classes +# 军规 41:考虑使用混合类组合功能 """ 混合类总结 @@ -213,6 +214,30 @@ def __init__(self, name, tree_with_parent): # 目的:定义一个 JSON 混合类 # 解释:定义一个 JSON 混合类 JsonMixin。 # 结果:JSON 混合类 +""" + +ChatGPT said: +正是如此!总结如下: +JsonMixin 类总结 +目的:JsonMixin 作为一个混合类,旨在为其他类提供 JSON 序列化和反序列化的功能,而不需要直接实例化自己。 + +@classmethod 的使用: +工厂方法:from_json 方法允许从 JSON 字符串创建对象,并能进行额外的处理。 +与类相关:类方法能够接收任何继承自 JsonMixin 的子类作为 cls,实现灵活性。 +多态性:创建的实例是调用该方法的子类的实例,而非混合类本身,符合混合类的设计初衷。 +因此,@classmethod 的使用与混合类的定位并不矛盾,反而增强了混合类的功能复用性和灵活性。 + +You said: +那么也就是混合类里面可以有@classmethod,但是绝对不会有__init__ +ChatGPT said: +是的,混合类通常不定义 __init__ 方法,因为它们的目的是为了提供附加功能,而不是实例化。以下是总结: + +混合类特征: +无 __init__:不定义构造函数,不能被实例化。 +可以包含 @classmethod:用于创建子类实例或实现其他类相关功能。 +功能复用:通过提供通用功能,使得其他类能够轻松继承和使用这些功能。 +这种设计方式确保混合类专注于功能的增强,而不干扰实例的创建逻辑。 +""" print(f"\n{'Example 9':*^50}") import json diff --git a/example_code/item_42.py b/example_code/item_42.py index 45ac6c2..385cae2 100755 --- a/example_code/item_42.py +++ b/example_code/item_42.py @@ -15,6 +15,16 @@ # limitations under the License. # Reproduce book environment + +# 军规 42:Prefer Public Attributes Over Private Ones +# 军规 42:优先使用公有属性表示应受保护的数据,避免使用私有属性表示 + +""" +优先使用公共属性,这样可以更加灵活和复用,PYTHON强调的是任用的人的判断,所以一般不建议强制限制, +如果需要限制可以优先提示_为保护类型方式别人误用,但是权利还是留给了调用者判断,除非非常特殊的情况, +必须强制为私有的才进行私有设定,我的理解对么。 +""" + import random random.seed(1234) diff --git a/example_code/item_43.py b/example_code/item_43.py index ccb575a..f04ef21 100755 --- a/example_code/item_43.py +++ b/example_code/item_43.py @@ -15,6 +15,35 @@ # limitations under the License. # Reproduce book environment + +# 军规 43:Inherit from collections.abc for Custom Container Types +# 军规 43:自定义容器类型时应继承collections.abc。 + +""" +核心思想: +自定义容器类型时,必须继承collections.abc而不是从头开始 +这是一份Python官方提供的容器建设标准图纸 +避免重复造轮子和方向性迷失 + +主要价值: +明确告知必须实现的核心方法 +自动获得大量通用功能实现 +确保容器行为符合Python标准 +无缝接入Python生态系统 + +使用建议: +只读容器用Sequence +可修改容器用MutableSequence +键值对结构用Mapping/MutableMapping +集合结构用Set/MutableSet + +实际好处: +开发更规范,避免遗漏关键功能 +减少代码量,提高开发效率 +自动集成Python高级特性 +类型安全,易于调试 +""" + import random random.seed(1234) From d6ba3dcd7713e6f7ba417feaf77fe1b3cc6b71c0 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 28 Oct 2024 11:11:31 +0800 Subject: [PATCH 56/59] modify item_44-46.py --- example_code/item_44.py | 28 +++++++++++++ example_code/item_45.py | 80 +++++++++++++++++++++++++++++++---- example_code/item_46.py | 93 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 9 deletions(-) diff --git a/example_code/item_44.py b/example_code/item_44.py index b5d69ce..10ae88c 100755 --- a/example_code/item_44.py +++ b/example_code/item_44.py @@ -15,6 +15,34 @@ # limitations under the License. # Reproduce book environment + +# 军规 44 :Use plain attributes instead of setter and getter methods + +""" +军规要点:属性访问的Python之道】 + +核心思想: +Python推崇简单直接的属性访问 +不需要像Java那样强制使用getter/setter +需要控制时才用@property,而不是过度设计 + +优雅的进化过程: +开始:直接使用属性访问 obj.value +需要时:无缝升级为@property +特点:外部调用方式完全不变 + +@property的使用时机: +需要验证数据时 +需要动态计算属性时 +需要在访问时触发特定操作 +需要保护属性又想保持简洁访问 + +Python的设计哲学: +相信用户,提供直接访问 +需要控制时,才加"智能门禁" +保持简单,拒绝过度工程化 +""" + import random random.seed(1234) diff --git a/example_code/item_45.py b/example_code/item_45.py index 14e9555..d1538db 100755 --- a/example_code/item_45.py +++ b/example_code/item_45.py @@ -15,6 +15,25 @@ # limitations under the License. # Reproduce book environment + +# 军规 45 : Consider @property Instead of Refactoring Attributes +# 军规 45 :考虑使用 @property 代替直接重构属性。 + +""" +解读: +核心含义:在Python中,属性的访问通常是公开的,用户可以直接操作类的属性。 +而@property是一种灵活的方式,可以在不改变外部调用方式的情况下,给属性增加逻辑控制。这避免了直接修改属性结构(即重构),同时实现了数据封装和控制。 + +Python哲学: +Python提倡“简单而直接”的代码风格。 +如果可以使用@property无缝添加控制,何必费力重构属性结构呢? + +总结 +无需直接重构:通过 @property 可以灵活控制属性,而无需重新设计整个访问逻辑。 +保持简单:代码清晰易懂,符合Python的简洁风格。 +优雅过渡:@property使得简单属性可以无缝转变为控制属性,不影响调用方式。 +""" + import random random.seed(1234) @@ -49,6 +68,49 @@ def close_open_files(): atexit.register(close_open_files) +""" +传统GET和SET的方式: +""" +class MyClass: + def __init__(self, value): + self._value = value # 改为私有属性 + + def get_value(self): + return self._value + + def set_value(self, value): + if value < 0: + raise ValueError("Value cannot be negative") + self._value = value + +# 现在需要通过get和set方法访问 +obj = MyClass(10) +print(obj.get_value()) +obj.set_value(20) + +""" +使用@property的方式: +""" +class MyClass: + def __init__(self, value): + self._value = value + + @property + def value(self): + return self._value + + @value.setter + def value(self, value): + if value < 0: + raise ValueError("Value cannot be negative") + self._value = value + +# 外部访问方式不变 +obj = MyClass(10) +print(obj.value) +obj.value = 20 + + # Example 1 # 目的:定义一个类 Bucket @@ -237,19 +299,19 @@ def quota(self, amount): assert bucket.quota == 70 fill(bucket, 50) -assert bucket.max_quota == 150 -assert bucket.quota_consumed == 30 -assert bucket.quota == 120 +# assert bucket.max_quota == 150 +# assert bucket.quota_consumed == 30 +# assert bucket.quota == 120 assert deduct(bucket, 40) -assert bucket.max_quota == 150 -assert bucket.quota_consumed == 70 -assert bucket.quota == 80 +# assert bucket.max_quota == 150 +# assert bucket.quota_consumed == 70 +# assert bucket.quota == 80 assert not deduct(bucket, 81) -assert bucket.max_quota == 150 -assert bucket.quota_consumed == 70 -assert bucket.quota == 80 +# assert bucket.max_quota == 150 +# assert bucket.quota_consumed == 70 +# assert bucket.quota == 80 bucket.reset_time += bucket.period_delta - timedelta(1) assert bucket.quota == 80 diff --git a/example_code/item_46.py b/example_code/item_46.py index cceffbb..a25c871 100755 --- a/example_code/item_46.py +++ b/example_code/item_46.py @@ -15,6 +15,30 @@ # limitations under the License. # Reproduce book environment + +# 军规 46 : Use Descriptors for Reusable @property Methods + +# 军规 46 :使用描述符实现可重用的 @property 方法。 + +""" +解读: +属性控制的局限:@property虽然方便,但如果多个类需要相似的属性控制逻辑, +用@property重写每个类会带来重复代码。而**描述符(Descriptor)**提供了一种将属性控制逻辑封装成独立类的机制,使得我们可以将相同的逻辑在多个类中复用。 + +描述符概念: +描述符是一种带有__get__、__set__和__delete__方法的类, +通过将描述符类的实例赋值给另一个类的属性,描述符的逻辑会自动用于属性的读写控制。 + +总结: +消除重复代码:描述符封装复用逻辑,避免在多个类中重复@property方法。 +清晰简洁:逻辑集中在描述符类中,类定义更清晰。 +增强代码复用性:描述符让属性控制逻辑更易于扩展和维护。 + +本质说明: +本质就是提供了一个专属Descriptor的封装类,来解决了多类都需要针对类同的属性进行get,set的方式, +这种是一种优化处理。 +""" + import random random.seed(1234) @@ -50,6 +74,75 @@ def close_open_files(): atexit.register(close_open_files) +# GPT - Example +print(f"\n{'GPT - Example':*^50}") +""" +Celsius 没有使用描述符来进行属性控制,用的是之前的@property方法。 +""" +class Celsius: + def __init__(self, temp=0): + self._temp = temp + + @property + def temp(self): + return self._temp + + @temp.setter + def temp(self, value): + if value < -273.15: + raise ValueError("Temperature cannot go below -273.15") + self._temp = value + +""" +Kelvin 没有使用描述符来进行属性控制,用的是之前的@property方法。 +""" +class Kelvin: + def __init__(self, temp=0): + self._temp = temp + + @property + def temp(self): + return self._temp + + @temp.setter + def temp(self, value): + if value < 0: + raise ValueError("Temperature cannot be below 0 in Kelvin") + self._temp = value + +""" +Celsius 和 Kelvin 都有相同的属性控制逻辑,但是代码重复, +所以我们可以使用描述符来实现属性控制逻辑的复用。 +通过TemperatureDescriptor类,我们可以将属性控制逻辑封装到一个类中, +然后将这个类的实例赋值给Celsius和Kelvin的temp属性。 +""" +class TemperatureDescriptor: + def __init__(self, min_temp): + self.min_temp = min_temp + self._temp = None + + def __get__(self, instance, owner): + return self._temp + + def __set__(self, instance, value): + if value < self.min_temp: + raise ValueError(f"Temperature cannot go below {self.min_temp}") + self._temp = value + +class Celsius: + temp = TemperatureDescriptor(-273.15) + +class Kelvin: + temp = TemperatureDescriptor(0) + +# 使用描述符属性 +c = Celsius() +c.temp = 25 +print(c.temp) # 25 +k = Kelvin() +k.temp = 5 +print(k.temp) # 5 + # Example 1 # 目的:定义一个类 Homework # 解释:定义一个类 Homework,包含 grade 属性。 From ff6d78cf81348dffe1477e7de4719f9e0d5ef0f3 Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 28 Oct 2024 11:29:13 +0800 Subject: [PATCH 57/59] modify item_47-48.py --- example_code/item_47.py | 74 +++++++++++++++++++++++++++++++++++++++++ example_code/item_48.py | 8 +++++ 2 files changed, 82 insertions(+) diff --git a/example_code/item_47.py b/example_code/item_47.py index f5fa163..88600f7 100755 --- a/example_code/item_47.py +++ b/example_code/item_47.py @@ -15,6 +15,26 @@ # limitations under the License. # Reproduce book environment + +# 军规 47 : Use __getattr__, __getattribute__, and __setattr__ for Lazy Attributes +# 军规 47 : 使用 __getattr__、__getattribute__ 和 __setattr__ 以实现延迟属性加载。 + +""" +解读: +延迟加载:延迟加载(Lazy Loading)是一种在需要时才初始化或加载对象属性的方式, +通常用于提升效率,尤其在属性的计算、资源占用较大的情况下。 + +三种魔术方法: +__getattr__:仅在属性未定义(不存在)时被调用,可以用于实现缺省值或动态属性。 +__getattribute__:每次访问属性时都会调用此方法,使其适合监控、延迟加载等高级控制逻辑。 +__setattr__:在对属性赋值时调用,适用于在属性赋值时加入校验或懒加载的逻辑。 + +总结: +延迟加载优化:通过 __getattr__ 和 __getattribute__ 延迟初始化属性,减少不必要的资源占用。 +代码控制力增强:这些魔术方法允许精细控制属性的加载与访问,为特定需求提供更多优化空间。 +适用场景:适用于复杂计算、资源密集或外部请求的属性。 +""" + import random random.seed(1234) @@ -49,6 +69,60 @@ def close_open_files(): atexit.register(close_open_files) +# GPT - Example 懒加载属性 +class MyClass: + def __init__(self, data): + # 直接初始化属性,计算较耗时 + self.heavy_data = self.load_heavy_data(data) + + def load_heavy_data(self, data): + # 假设此方法较为耗时 + return data * 2 + +obj = MyClass(10) +print(obj.heavy_data) + +class MyClass: + def __init__(self, data): + self.data = data + self._heavy_data = None # 延迟初始化属性 + + def load_heavy_data(self): + print("Loading heavy data...") + return self.data * 2 + + def __getattr__(self, name): + if name == "heavy_data": + # 仅当访问 heavy_data 时才计算 + self._heavy_data = self.load_heavy_data() + return self._heavy_data + raise AttributeError(f"{name} not found") + +obj = MyClass(10) +print(obj.heavy_data) # 首次访问时加载 +print(obj.heavy_data) # 后续直接返回已有值 + +class MyClass: + def __init__(self, data): + self.data = data + self._heavy_data = None + + def load_heavy_data(self): + print("Loading heavy data...") + return self.data * 2 + + def __getattribute__(self, name): + if name == "heavy_data": + if object.__getattribute__(self, "_heavy_data") is None: + # 延迟加载 + object.__setattr__(self, "_heavy_data", self.load_heavy_data()) + return object.__getattribute__(self, "_heavy_data") + return object.__getattribute__(self, name) + +obj = MyClass(10) +print(obj.heavy_data) # 首次访问时加载 +print(obj.heavy_data) # 后续直接返回已有值 + # Example 1 # 目的:定义一个类 LazyRecord diff --git a/example_code/item_48.py b/example_code/item_48.py index a958be4..d89ec60 100755 --- a/example_code/item_48.py +++ b/example_code/item_48.py @@ -15,6 +15,14 @@ # limitations under the License. # Reproduce book environment + +# 军规 48 : Validate Subclasses with __init_subclass__ +# 军规 48 : 使用 __init_subclass__ 对子类进行验证。 + +""" + +""" + import random random.seed(1234) From 9961808be5a8e780c042f00e79e033df624e733d Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Tue, 29 Oct 2024 09:46:48 +0800 Subject: [PATCH 58/59] modify item_48-51.py --- example_code/item_48.py | 52 ++++++++++++++++++++++- example_code/item_49.py | 91 +++++++++++++++++++++++++++++++++++++++++ example_code/item_50.py | 50 ++++++++++++++++++++++ example_code/item_51.py | 21 ++++++++++ 4 files changed, 213 insertions(+), 1 deletion(-) diff --git a/example_code/item_48.py b/example_code/item_48.py index d89ec60..e42cba1 100755 --- a/example_code/item_48.py +++ b/example_code/item_48.py @@ -20,7 +20,19 @@ # 军规 48 : 使用 __init_subclass__ 对子类进行验证。 """ - +解读 +规则意图:__init_subclass__ 是 Python 3 中用于在父类中执行子类注册和验证的一种机制。 +它会在每次定义子类时自动调用,可以通过这个方法检查子类是否正确实现了特定的属性、方法, +或符合某些结构要求。 + +继承关系的控制: +通过 __init_subclass__,父类能够在子类定义时进行约束和检查(而不是等到实例化时), +从而提前捕捉到可能的错误,保证类层次结构的稳定性。 + +总结: +定义时检查:__init_subclass__在定义子类时进行验证,而不是等待到实例化后检查,捕捉错误更及时。 +结构稳定:父类能够在子类生成时就进行约束,保证类结构的一致性。 +适用场景:用于父类需要对子类的结构做要求,如特定方法实现、属性存在性等。 """ import random @@ -57,6 +69,44 @@ def close_open_files(): atexit.register(close_open_files) +# GPT - Example 验证子类 +print(f"\n{'GPT - Example 验证子类':*^50}") +""" +错误示例:不验证子类 +没有验证时,子类可能缺少关键属性或方法,导致运行时出错: +""" +class BaseClass: + def action(self): + raise NotImplementedError("子类必须实现 'action' 方法") + +class SubClass(BaseClass): + pass # 忘记实现 action 方法 + +obj = SubClass() +obj.action() # 会抛出错误 + +""" +推荐做法:使用 __init_subclass__ 验证子类 +在父类中定义 __init_subclass__,确保所有子类实现关键方法或满足特定条件: +""" +class BaseClass: + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + # 验证子类是否实现了 'action' 方法 + if not hasattr(cls, "action") or not callable(getattr(cls, "action")): + raise NotImplementedError(f"{cls.__name__} 必须实现 'action' 方法") + +class SubClass(BaseClass): + def action(self): + print("SubClass action") + +# 正常使用 +obj = SubClass() +obj.action() # 输出 "SubClass action" + +class InvalidSubClass(BaseClass): + pass # 缺少 action 实现,定义时就会出错 + # Example 1 # 目的:定义一个类 Meta diff --git a/example_code/item_49.py b/example_code/item_49.py index 254a7e6..cb11925 100755 --- a/example_code/item_49.py +++ b/example_code/item_49.py @@ -14,6 +14,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +# 军规 49 : Register Class Existence with __init_subclass__ +# 军规 49 : 使用 __init_subclass__ 记录现有的子类。 + +""" +解读: +主要目的:__init_subclass__ 主要用于在定义子类时自动执行特定的操作。 +它允许基类在创建每个子类时自动记录或注册该子类,便于管理和跟踪所有现有的子类。 + +Python的类管理: +使用 __init_subclass__,父类可以在创建子类时立即知晓其存在,从而动态地记录子类信息, +形成子类注册表。这种机制特别适用于管理庞大继承结构、动态插件系统或工厂模式设计。 + +总结: +自动化记录机制:__init_subclass__方法在每次创建子类时自动记录子类,无需手动注册。 +便于子类管理:父类通过自动记录,可以高效地追踪和管理所有现有子类。 +适用场景:适合需要管理大量子类的场景,如动态模块系统、插件注册、工厂模式设计等。 + +一个重要的注意事项: +__init_subclass__方法是在子类定义时调用的,而不是在实例化子类时调用的。 +使用 weakref 自动追踪子类删除,weakref 模块提供了 WeakSet,当子类被垃圾回收时,它们会自动从集合中移除。 +""" + # Reproduce book environment import random random.seed(1234) @@ -49,6 +71,75 @@ def close_open_files(): atexit.register(close_open_files) +# GPT - Example +print(f"\n{'GPT - Example':*^50}") +class BaseClass: + subclasses = [] # 手动记录子类列表 + + def register_subclass(cls): + BaseClass.subclasses.append(cls) + +class SubClass1(BaseClass): + pass + +# 每次定义子类后都需手动调用注册 +BaseClass.register_subclass(SubClass1) +print(BaseClass.subclasses) # 可能遗漏某些子类 + +class BaseClass: + subclasses = [] # 自动记录子类 + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseClass.subclasses.append(cls) # 每次定义子类时自动添加到列表 + print(f"Registered subclass: {cls.__name__}") + +class SubClass1(BaseClass): + pass + +class SubClass2(BaseClass): + pass + +# 查看所有注册的子类 +print(BaseClass.subclasses) # 输出: [, ] + +""" +要点: +实现思路 +为了能动态更新子类注册列表,可以采取以下策略: + +手动删除: +在删除子类前手动从父类的注册列表中移除子类。 +上下文管理:设计一个上下文管理类,自动处理子类的创建和删除。 + +基于 weakref 模块: +使用 Python 的 weakref 模块创建一个弱引用注册表, +这样当子类不再被引用时,它们会自动从注册列表中移除。 +""" +import weakref + +class BaseClass: + subclasses = weakref.WeakSet() # 使用弱引用集合追踪子类 + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseClass.subclasses.add(cls) + print(f"Registered subclass: {cls.__name__}") + +# 定义一些子类 +class SubClass1(BaseClass): + pass + +class SubClass2(BaseClass): + pass + +print("Registered subclasses:", [cls.__name__ for cls in BaseClass.subclasses]) + +# 删除一个子类 +del SubClass1 + +print("Updated subclasses:", [cls.__name__ for cls in BaseClass.subclasses]) # SubClass1 会被自动移除 + # Example 1 # 目的:定义一个类 BetterSerializable diff --git a/example_code/item_50.py b/example_code/item_50.py index c652477..0f4539c 100755 --- a/example_code/item_50.py +++ b/example_code/item_50.py @@ -15,6 +15,34 @@ # limitations under the License. # Reproduce book environment + +# 军规 50:Annotate Class Attributes with __set_name__ +# 军规 50:使用 __set_name__ 为类属性添加注释(或名称关联)。 + +# 重要注意事项: +# __set_name__ 是描述符协议中的一部分,核心功能是为描述符类提供绑定属性名的能力。 +# 对于普通类来说,__set_name__ 并不会被自动调用,因此它在普通类中是没有实际意义的, +# 只有在描述符类中才会体现出作用。 +# 注意关注之后GPT的范例。 + +""" +解读: +主要功能: +__set_name__ 是描述符协议中的一个方法,用于在类创建时, +将描述符实例与它们的属性名进行绑定。这个方法在Python 3.6中引入, +允许描述符在其所属类被定义时,自动获得它的属性名(即被赋值的属性名称), +从而简化属性名称管理,便于后续操作和注解。 + +典型场景: +当我们在一个类中使用多个相同类型的描述符时,通常需要让每个描述符知道它对应的属性名。 +通过 __set_name__,描述符能够自动获得属性名,并以此实现更加灵活、清晰的类结构。 + +总结: +描述符类中的 __set_name__ 本质上就是为了让描述符自动获取属性名,而常规类用不到它, +所以理解时关键是识别“描述符”这个特殊角色。一旦认清了这个背景, +__set_name__ 的作用就一目了然了——它只是为描述符的自动绑定提供便利,与普通类无关。 +""" + import random random.seed(1234) @@ -49,6 +77,28 @@ def close_open_files(): atexit.register(close_open_files) +# GPT - Example +print(f"\n{'GPT - Example':*^50}") +class Descriptor: + def __set_name__(self, owner, name): # 注意这里的使用场景是描述符类 + self.name = name # 自动获取属性名 + + def __get__(self, instance, owner): + # 获取描述符的值 + return instance.__dict__.get(self.name) + + def __set__(self, instance, value): + # 设置描述符的值 + instance.__dict__[self.name] = value + +class MyClass: + attr1 = Descriptor() # 自动绑定名称 attr1 + attr2 = Descriptor() # 自动绑定名称 attr2 + +obj = MyClass() +obj.attr1 = 10 +obj.attr2 = 20 +print(obj.attr1, obj.attr2) # 输出 10 20 # Example 1 # 目的:定义一个类 Field diff --git a/example_code/item_51.py b/example_code/item_51.py index 680ede8..e997301 100755 --- a/example_code/item_51.py +++ b/example_code/item_51.py @@ -15,6 +15,27 @@ # limitations under the License. # Reproduce book environment + +# 军规 51: Prefer Class Decorators Over Metaclasses for Composable Class Extensions +# 军规 51: 优先使用类装饰器而不是元类来扩展类。 + +""" +1. 类装饰器的本质 +装饰器就像在蛋糕上加糖霜,或在房子里装修新窗户和门。 +装饰器包装现有类功能,保持类的原结构不变,可以轻松叠加和组合。 +就像在业务逻辑上添加功能而不改变底层逻辑,装饰器让代码更易读、便于扩展。 + +2. 元类的本质 +元类则类似于“工厂模式”,它是从配方上直接定制,决定整个类的创建过程。在房屋比喻中, +元类相当于建造房子的工厂,可以规定房子的基础结构甚至材料,因此适用于那些需要全局一致性和复杂控制的场景。 +它在框架和底层架构中更为常见。 + +4. 总结 +当需要可组合的扩展时,优先使用装饰器;当需要控制类创建过程时,使用元类。 +装饰器更适合业务逻辑的灵活扩展,而元类更适合框架和底层架构的统一控制。 +两者配合使用,可以在不同层次上实现代码的复用和规范。 +""" + import random random.seed(1234) From df28bfc97c95a336b01d41097794a1426af40fae Mon Sep 17 00:00:00 2001 From: "Tony.xu" <191284969@qq.com> Date: Mon, 4 Nov 2024 10:08:17 +0800 Subject: [PATCH 59/59] modify item_52-53.py --- example_code/item_52.py | 60 ++++++++++++++++++++++++++++++++++++----- example_code/item_53.py | 36 +++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/example_code/item_52.py b/example_code/item_52.py index 186edac..ec7842a 100755 --- a/example_code/item_52.py +++ b/example_code/item_52.py @@ -13,8 +13,29 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - # Reproduce book environment + +# 军规 52 : Use subprocess to Manage Child Processes +# 军规 52 : subprocess 模块:Python 的 subprocess 模块用于创建和管理子进程, +# 允许我们在 Python 程序中执行其他系统命令或脚本,并与它们进行交互。 +# 子进程是由当前 Python 程序派生出来的独立任务,subprocess +# 提供了控制子进程的创建、执行、输入输出管理等功能,使得在 Python 中执行外部命令更为灵活。 + +""" +解读: +(1)在某些场景下,我们需要调用系统命令或执行外部脚本。 +例如,在处理自动化任务、批量文件管理或系统监控时,使用 subprocess 可以简化操作流程。 +(2)subprocess 不仅支持创建子进程,还能通过 stdin、stdout 和 stderr 管理标准输入输出, +实现与子进程的数据交互。 +(3)相比于早期的 os.system 或 os.popen,subprocess 模块更强大、灵活,提供了更安全的接口, +避免了 shell 注入等安全风险。 + +总结: +subprocess 模块提供了执行系统命令、创建子进程并与之交互的灵活方式。它提供了多种方法,包括 subprocess.run() 和 subprocess.Popen(),可以满足不同的任务需求。 +对于简单的系统命令执行,subprocess.run() 是最佳选择。而 subprocess.Popen 更适合需要交互或异步管理的复杂任务。 +使用 subprocess 时尽量避免 shell=True,以防止 shell 注入漏洞。 +""" + import random random.seed(1234) @@ -49,6 +70,37 @@ def close_open_files(): atexit.register(close_open_files) +import subprocess +# GPT --- Example +print(f"\n{'GPT---Example':*^50}") +# 使用 subprocess 运行系统命令 +result = subprocess.run( + ['echo', 'Hello, Shadow Master!'], + capture_output=True, # 捕获输出 + text=True # 以字符串方式返回输出 +) +# 读取子进程的标准输出 +print("Output:", result.stdout) +# 读取子进程的标准错误输出 +print("Error:", result.stderr) +# 返回值,0 表示成功 +print("Return code:", result.returncode) + +# 解释:subprocess.Popen 提供了更多细粒度控制, +# 通过 communicate() 发送数据到子进程的标准输入, +# 并读取输出和错误信息。 +process = subprocess.Popen( + ['python3', '-c', 'print(input("Enter your name: "))'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + text=True +) +# 发送输入到子进程 +output, error = process.communicate(input="Shadow Master\n") +print("Output:", output) +print("Error:", error) + +# ============================================================================ # Example 1 # 目的:使用 subprocess 模块运行子进程 @@ -127,7 +179,6 @@ def run_encrypt(data): proc.stdin.flush() # Ensure that the child gets input return proc - # Example 6 # 目的:运行多个加密子进程 # 解释:使用 run_encrypt 函数运行多个加密子进程。 @@ -139,7 +190,6 @@ def run_encrypt(data): proc = run_encrypt(data) procs.append(proc) - # Example 7 # 目的:等待所有加密子进程完成 # 解释:使用 subprocess.Popen.communicate 方法等待所有加密子进程完成。 @@ -149,7 +199,6 @@ def run_encrypt(data): out, _ = proc.communicate() print(out[-10:]) - # Example 8 # 目的:定义 run_hash 函数 # 解释:定义 run_hash 函数,使用 subprocess.Popen 方法运行哈希子进程。 @@ -162,7 +211,6 @@ def run_hash(input_stdin): stdout=subprocess.PIPE ) - # Example 9 # 目的:运行多个加密和哈希子进程 # 解释:使用 run_encrypt 和 run_hash 函数运行多个加密和哈希子进程。 @@ -182,7 +230,6 @@ def run_hash(input_stdin): encrypt_proc.stdout.close() encrypt_proc.stdout = None - # Example 10 # 目的:等待所有加密和哈希子进程完成 # 解释:使用 subprocess.Popen.communicate 方法等待所有加密和哈希子进程完成。 @@ -197,7 +244,6 @@ def run_hash(input_stdin): print(out[-10:]) assert proc.returncode == 0 - # Example 11 # 目的:处理子进程超时 # 解释:使用 subprocess.Popen.communicate 方法处理子进程超时。 diff --git a/example_code/item_53.py b/example_code/item_53.py index 6fda03e..7af95e2 100755 --- a/example_code/item_53.py +++ b/example_code/item_53.py @@ -15,6 +15,42 @@ # limitations under the License. # Reproduce book environment + +# 军规 53 : Use Threads for Blocking I/O, Avoid for Parallelism +# 军规 53 : 使用线程进行阻塞 I/O 操作,避免用于并行处理 + +""" +翻译: +"对阻塞 I/O 使用线程,避免用于并行计算"意味着: +线程适合处理 I/O 等待操作(如文件读写、网络请求、数据库操作)。 +线程不适合用于 CPU 密集型任务,因为 GIL 限制了 Python 多线程在计算密集型任务上的性能提升。 +这是一个重要的性能准则,Python 程序员在并发设计时需要牢记。 + + +解读: +关键原理: +全局解释器锁 (GIL):Python 的 GIL 机制使得同一时刻只能有一个线程执行 Python 字节码, +主要原因在于保证 Python 解释器的线程安全。 +GIL 与 I/O 释放:在 I/O 操作(如文件读写、网络请求)中,线程会释放 GIL,使其他线程可以继续运行, +这使得线程适合处理 I/O 密集型任务。 +GIL 与 CPU 密集型任务:计算密集型任务会一直占用 GIL,不会释放给其他线程, +因此使用线程不会提升并行计算的性能。 + + +适用场景: +适合用线程的情况: +文件读写操作,网络请求(API、HTTP 请求),数据库读写等 I/O 操作。 +不适合用线程的情况: +数值计算,图像处理,大规模矩阵或数据处理等 CPU 密集型任务。 + + +总结: +线程适合 I/O 密集型任务:在 I/O 操作中,线程能够在等待时释放 GIL,实现高效并发。 +避免使用线程处理计算密集型任务:对于计算密集型任务,线程无法绕过 GIL,无法提升并行计算性能,推荐使用多进程。 +理解 GIL 限制很重要:GIL 是 Python 多线程在计算任务中面临的核心瓶颈,尤其在高性能场景中, +进程和分布式计算更为合适。 +""" + import random random.seed(1234)