使用Python和Rich创建一个Wordle Clone
我们将使用这个教程为终端创建一个Wordle克隆。自从Josh Wardle于2021年10月首次发布Wordle以来,数百万人已经使用过Wordle。虽然原始游戏可以在线玩,但我们将创建一个命令行应用程序,并利用Rich库为它赋予良好的外观。
通过完成这个逐步项目,您将获得设置基本原型游戏并将其逐步发展成可靠应用程序的经验。
什么是Wordle?
Wordle给你六次机会找到一个被埋藏的五个字母的单词。在每次猜测之后,你将被告知哪些字母已经被正确插入,哪些字母被放错了位置,哪些字母根本不在单词中。
第1步:猜单词游戏
游戏看起来不好,而且很难解释玩家的反馈。但是,你的Wordle克隆的基础已经就位。在这一步中,你将建立一个非常简单的猜单词游戏。
您的用户可以对单词进行合理的猜测,并学习他们正确放置的字母,他们正确放置的字母以及哪些字母甚至不在单词中。
在这个阶段,你将使用input()来读取玩家提供的单词,使用for循环给他们多次猜测,并使用set跟踪他们在这个阶段正确识别的字母。
通过input()获取用户数据
通过用户输入,您可以从其中获得信息。这个内置功能是在命令行上提供简单交互的好方法。
启动你的REPL并尝试一下。你应该写:
>>> guess = input("Guess a word: ")
Guess a word: python
>>> guess
'python'
您可以使用可选提示()请求输入。用户在输入任何数据之前将看到这个提示。上面示例中的突出显示的行显示了用户输入和提示。用户会被提示猜单词。用户键入”python”并单击”Enter”键。
调用input()返回用户的文本输入()。前面提到的情况,即“python”被选为猜测,说明了这一点。开始开发游戏从来都不是一个坏主意。使用编辑器创建wyrdl.py文件,并用以下细节填充它:
guess = input("Guess a word: ")
if guess == "PYTHON":
print("Correct")
else:
print("Wrong")
在读取用户的猜测后,您确定它是否与隐藏单词”python”匹配。您评估他们的直觉并告诉他们准确性或不准确性。
您可能认为这里没有太多的游戏性。如果你认为这是一个游戏,那它无疑是最沉闷和最令人沮丧的游戏之一。秘密单词每次都是相同的。因此,重玩价值不大。此外,用户无法从纠正中学到任何东西,因为他们无法采取任何行动。
很快您将改变游戏,使它更有趣。这个子部分将通过解决一个小的可用性问题来完成。考虑以下游戏:
Guess a word: python
Wrong
在这种情况下,你正确推断出秘密单词是python。但是,游戏会告诉你它是不正确的,因为它将你的猜测与大写字符串”PYTHON”进行对比。这个游戏的目的是猜单词,而不是识别大写或小写字符。两个单词如何比较,无论大小写如何?
可能将猜测显式更改为大写是最简单的解决方案。之后,用户输入单词时无论他们如何输入都没有关系:
guess = input("Guess a word: ").upper()
if guess == "PYTHON":
print("Correct")
else:
print("Wrong")
您加入了.upper(),让用户的猜测变成了全部大写。 这个修改立即提高了游戏的易用性。 即使您用小写字母拼写Python,Python也被报告为正确。 尽管如此,你只允许用户做出好的猜测。 在这之后的部分,你会添加更多的猜测来完善你的游戏。
为了防止重复的代码,要使用循环结构。
玩Wordle时,您最多有六次尝试选择理想单词。为了获得相同的结果,您可以复制之前创建的代码,并将其在游戏中重复六次。 由于多种原因,这是不明智的。 最重要的是,它难以维护且效率低下。
相反,您将使用循环结构来实现重复的行为。 Python提供了for和while循环结构。 在执行明确的迭代时,通常使用for,因为您知道要循环多少次。 另一方面,当您不确定需要重复多少次任务时,while循环结构是完美的无限迭代结构。
在这种情况下,您将使用for循环结构询问用户六次以猜测单词
for guess_num in range(1, 7):
guess = input(f"\nGuess {guess_num}: ").upper()
if guess == "PYTHON":
print("Correct")
break
print("Wrong")
您还通过迭代范围计算猜测次数,并向用户显示该数字。
一旦用户发现了解决方案,就没有必要让他们继续猜测了。 如果用户选择正确的单词,则使用break语句提前退出循环。 Break的另一个好处是消除明确else的要求。 只有猜测正确时你的代码才会继续运行。
为了使游戏可玩,现在是时候加入一些相关的用户反馈了。 在以下子节中,您将看到如何计算用户准确猜测的字母数。
使用集合检查字母
迄今为止,用户仅被告知他们是否正确猜出了术语。 您将评论特定的字母,以提供一些用户可以使用来猜测隐藏单词的线索。 您将每个字母分为三类:
- 正确的字母在隐藏单词和猜测中都在相同的位置上。
- 隐秘的单词包含一个错误的字母,但是位置不同。
- 隐藏单词不包含错误的字母。
您如何确定哪些字母属于不同类别? 首先确定哪些字母是正确顺序。 在比较两个序列的元素时,Python的zip()方法是很棒的选择。 在这种情况下,您正在比较两个字符串中的字母:
>>> for python_letter, program_letter in zip("PYTHON", "PROGRAM"):
... if python_letter == program_letter:
... print(python_letter)
p
通过一次比较PYTHON和PROGRAM中的一个字母,您可以在此示例代码中识别两个位置不正确的字符。 尽管O在两个术语中都存在,但因其位置不同而未报告。
现在,您需要收集字母而不是打印它们。 Python的理解是将一个或多个序列转换为另一个序列的强大构建块。 在这种情况下,您将应用预先确定的理解来获取正确的字母:
>>> word = "PYTHON"
>>> guess = "PROGRAM"
>>> {letter for letter, correct in zip(guess, word) if letter == correct}
虽然它生成一个集合而不是一个列表,但是集合解析与列表解析类似。 在此情况下,由于正确字母的顺序无关紧要,因此它在此情况下运作得很好。
Python具有强大的集合操作,这是其优点之一。 若要轻松查找至少一个集合中存在的元素、两个集合都存在的元素或仅在一个集合中存在的元素,请使用两个集合之间的联合、交集和差异。
您可以使用集合交集运算符(&)查找两个字符串中都出现的所有字母,例如,如果您有两个字符串:
print(set("PYTHON") & set("PROGRAM"))
{'P',' O'}
您可以从交集推断出P和O都出现在PYTHON和PROGRAM中。集合差也可用于识别出现在一个集合中但不出现在另一个集合中的字母:
print(set("PYTHON") - set("PROGRAM"))
{'N',' Y',' H',' T'}
在PYTHON中缺少PROGRAM中的字母N、Y、H和T。
现在,您可以使用集合来提高游戏水平。但在开始实现之前,您需要进行一个调整。您目前已将秘密单词硬编码到if测试中。在分类字母时,您将使用该术语。请使用以下常量来引用它:
WORD = "PYTHON"
for guess_num in range(1, 7):
guess = input(f"\nGuess {guess_num}: ").upper()
if guess == WORD:
print("Correct")
break
print("Wrong")
当WORD被引入时,更改秘密单词更简单。如果您提供一个单词列表,让您可以从中选择单词,那么游戏将更加有趣。
现在,您已经学习了集合的知识,您可以计算并显示正确、错误和误放置的字符。您应该更新您的代码,包括如下内容:
WORD = "PYTHON"
for guess_num in range(1, 7):
guess = input(f"\nGuess {guess_num}: ").upper()
if guess == WORD:
print("Correct")
break
correct_letters = {
letter for letter, correct in zip(guess, WORD) if letter == correct
}
misplaced_letters = set(guess) & set(WORD) - correct_letters
wrong_letters = set(guess) - set(WORD)
print("Correct letters:", ", ".join(sorted(correct_letters)))
print("Misplaced letters:", ", ".join(sorted(misplaced_letters)))
print("Wrong letters:", ", ".join(sorted(wrong_letters)))
您目前只列出了类别和字母如下:
Guess 1: program
Correct letters: P
Misplaced letters: O
Wrong letters: A, G, M, R
Guess 2: coding
Correct letters:
Misplaced letters: N, O
Wrong letters: C, D, G, I
Guess 3: python
Correct
尽管信息已经存在,但不一定简单。但下一步是使用单词列表添加一些变化。然后,您将改进游戏的用户界面,以提高视觉吸引力和玩家体验。
第2步:使用单词列表
尽管游戏的外观没有改变,但您必须尝试在每次玩游戏时识别不同的单词。在本阶段,您不会改变游戏的功能。但是,如果提供单词列表,它将更具娱乐性和可重复性。到目前为止,秘密单词一直是恒定的。这很快就会改变。
在此阶段,您将手动组装一小段单词列表并将其包含在游戏中。然后,将介绍如何从任何文本中创建单词列表。
手动制作单词列表
一个包含单词列表的纯文本文件将每行一个短语。这继续了Unix系统上的一个长期传统,例如拼写检查器和类似应用程序使用一个名为words的文件。
创建一个名为wordlist.txt的新文件,并用以下信息进行填充以开始:
Apple
Bread
Cloud
Dance
Early
Adder
Grape
您对前面的阶段可能研究过的可能的猜测单词已被包含在内。请随时添加到列表中。请不要花费太多精力,稍后将自动生成单词列表。
在制作更好的列表之前,您需要首先考虑如何将这个单词列表读入您的软件中。Python的pathlib模块非常适合处理各种文件并将它们读入内存。您可以试一下。
import string
filename = "mytext.txt"
with open(filename, 'r') as f:
words = []
for line in f:
for word in line.split():
clean_word = word.translate(str.maketrans('', '', string.punctuation)).upper()
words.append(clean_word)
word_list = list(set(words))
word_list.sort()
with open("wordlist.txt", 'w') as f:
f.write('\n'.join(word_list))
这个脚本将一个文本文件转换为适合在Wordle clone中使用的单词列表。在该脚本中,你为文本文件制作了一个单词列表。将这个单词列表放入一个名为wordlist.txt
的文件中。这个文件可以被Wordle clone读取并使用。
使用了字符串模块中的字符集和maketrans()
函数来清理文件的标点符号,然后将所有单词转换为大写。最终,将清洗后的单词添加到一个列表中。一个非常强大的Python技巧是使用set()
函数来消除列表中的重复值。在最后,将单词列表排序并写入到一个名为wordlist.txt
的文件中。详见如下代码段:
clean_word = word.translate(str.maketrans('', '', string.punctuation)).upper()
words.append(clean_word)
word_list = list(set(words))
word_list.sort()
with open("wordlist.txt", 'w') as f:
f.write('\n'.join(word_list))
单词列表中的单词可以从wordlist.txt
文件中读取并使用。
为了在Wordle clone中使用单词列表,只需导入该列表并选择一个随机单词:
import pathlib
import random
WORDLIST = pathlib.Path("wordlist.txt")
words = [
word.upper()
for word in WORDLIST.read_text(encoding="utf-8").strip().split("\n")
]
word = random.choice(words)
现在,单词列表具有可变性和可扩展性。
import pathlib
import sys
from string import ascii_letters
in_path = pathlib.Path(sys.argv[1])
out_path = pathlib.Path(sys.argv[2])
words = sorted(
{
word.lower()
for word in in_path.read_text(encoding="utf-8").split()
if all(letter in ascii_letters for letter in word)
},
key=lambda word: (len(word), word),
)
out_path.write_text("\n".join(words))
说明: 这个脚本使用 sys.argv 从命令行读取数据。第1个和第2个命令行选项被转换为路径,并在第7和第8行中赋予路径名称。您必须指定新单词列表文件的位置和现有文本文件的路径。
下面是使用脚本将当前的wyrdl.py复制成单词列表的示例:
$ python create_wordlist.py wyrdl.py wordlist.txt
在读取 wyrdl.py 时找到单词,然后将其保存在当前目录中的 wordlist.txt 文件中。请记住,这将替换手动创建的 wordlist.txt 文件。查看更新后的单词列表:
Abate
Blend
Caper
Dwell
Eager
Flair
Gloom
Haste
Icily
Jolly
Knead
Lurks
Melon
说明: 您的代码中有几个熟悉的单词。但是请注意,并非所有术语都包含在单词列表中。返回 create_wordlist.py,特别注意第14行。这一行将通过防止包含非ASCII字符的单词被读取来过滤您的集合推导。实际上,它仅限于字母A到Z。
您应该知道长度超过或少于五个字母的术语没有被过滤。也许您应该开发一个 Wordle 变体,测试用户对七个字母单词的掌握。您可以在构建单词列表时执行此操作。但是,通过将该任务转移到您的 wyrdl.py 代码,您可以获得一些灵活性。这使得游戏的单词长度和传统单词列表可以更改。
单词列表也被排序了。即使没有这个,列表还是可以手动浏览的,但是这并不一定是必须的。要更改排序顺序,您在第16行上指定了键。
通过 key=lambda word: (len(word), word),您根据单词长度对单词进行排序,然后根据其实际长度对单词进行排序。因此,您的单词列表从一个字母的单词开始,然后转向两个字母的单词,依此类推。每个相同长度的单词组按字母表顺序排列。
现在,您可以创建您的单词列表。运行 create_wordlist.py 脚本,该脚本可以在任何您能找到的纯文本文件上运行。例如,您可以下载整个莎士比亚作品以制作传统的单词列表,或者下载《爱丽丝漫游奇境记》的简化版本以制作更简单的单词列表。
import pathlib
import random
from string import ascii_letters
# ...
words = [
word.upper()
for word in WORDLIST.read_text(encoding="utf-8").split("\n")
if len(word) == 5 and all(letter in ascii_letters for letter in word)
]
说明:因为它们已经被 if 测试过滤掉,所以您还可以省略空单词。strip()。
为了使您的代码在未来更容易使用,首先必须重新排列代码。现在,秘密单词是从单词列表中随机选择的,这使得您的游戏更有趣。您稍后将努力通过更合理的反馈来改善用户体验。
输出:
第3步:使用函数组织代码
到目前为止,您已经为游戏创建了一个脚本。它由一系列依次执行的命令列表组成。即使它们非常适合快速入门和测试您的游戏的基本原型,这种程序的可扩展性很差。随着您的程序变得更加复杂,您应将代码组织为可重复使用的函数。
完成此步骤后,游戏对用户来说仍然是相同的。 但是,以后开发的基本代码将更加简单。
在以下组织中,代码如下:
import pathlib
import random
from string import ascii_letters
def main():
# 预处理
word = get_random_word()
# Process (main loop)
for guess_num in range(1, 7):
guess = input(f"\nGuess {guess_num}: ").upper()
show_guess(guess, word)
if guess == word:
break
# 后处理
else:
game_over(word)
def get_random_word():
wordlist = pathlib.Path(__file__).parent / "wordlist.txt"
words = [
word.upper()
for word in wordlist.read_text(encoding="utf-8").split("\n")
if len(word) == 5 and all(letter in ascii_letters for letter in word)
]
return random.choice(words)
def show_guess(guess, word):
correct_letters = {
letter for letter, correct in zip(guess, word) if letter == correct
}
misplaced_letters = set(guess) & set(word) - correct_letters
wrong_letters = set(guess) - set(word)
print("Correct letters:", ", ".join(sorted(correct_letters)))
print("Misplaced letters:", ", ".join(sorted(misplaced_letters)))
print("Wrong letters:", ", ".join(sorted(wrong_letters)))
def game_over(word):
print(f"The word was {word}")
if __name__ == "__main__":
main()
在进行所有这些更改后,您的游戏现在应该可以玩了。 执行您的程序以检查游戏是否按预期执行。
第4步:使用Rich进行游戏风格设计
如果您玩过在线的Wordle,您将认识猜测表和彩色字母,指示字母是正确的,错误的还是不正确的。
了解Rich控制台打印机
使用第三方库Rich来使用它。 在安装Rich之前,创建一个虚拟环境来安装项目要求。 从下面的列表中选择您的平台,然后键入以下命令:
PS> python -m venv venv
PS> venv\Scripts\Activate
(venv) PS>
启动虚拟环境后,可以使用pip安装Rich。
python -m pip install rich
安装后,您可以测试Rich。 重写print()函数是一种简单的方法开始使用Rich:
>>> from rich import print
>>> print("Hello, [bold red]Rich[/] :snake:")
Hello, Rich ?
Rich使用独特的标记语法,该语法是基于公告牌代码模拟的。 Rich将以粗体红色字体颜色呈现单词”Rich”,即使在此代码块中看不到它。 样式说明添加在方括号中,如上面示例中使用的[bold red]。 样式有效,直到您使用[/]结束它。
您可以使用Rich来通过在猜测之间清除屏幕来提高游戏的视觉吸引力。 为此,您要使用console. clear()。 将以下函数添加到代码中:
def refresh_page(headline):
console.clear()
console.rule(f"[bold blue]:leafy_green: {headline} :leafy_green:[/]\n")
使用rule(),您可以添加一个修饰性的水平线以更强调您的打印文本。 使用console.clear()清除屏幕。 然后,将在屏幕顶部使用console.rule()打印标题。
记录早期预测并为其着色
您将使用一个列表来跟踪所有您的猜测。 列表可以以“______”,即五个下划线开头,作为可能的猜测占位符。 随着用户进行猜测,占位符将被替换为有根据的猜测。
请按以下方式更新main()以开始:
def main():
# 预处理
words_path = pathlib.Path(__file__).parent / "wordlist.txt"
word = get_random_word(words_path.read_text(encoding="utf-8").split("\n"))
guesses = ["_" * 5] * 6
# 处理(主循环)
for idx in range(6):
guesses[idx] = input(f"\n猜测{idx + 1}: ").upper()
show_guess(guesses[idx], word)
if guesses[idx] == word:
break
# 后处理
else:
game_over(word)
说明: 接下来的演示将反映用户的新预测。所有预测都将使用新函数打印到屏幕上,该函数使用Rich进行优美的颜色和布局。在选择每个字母的正确颜色时,将循环遍历每个猜测字母。
为了简化操作,您从旧的基于集合的推理切换到了每个字母的新分类系统。在接下来的代码中,display_guesses()代替了show guess():
def show_guesses(guesses, word):
for guess in guesses:
styled_guess = []
for letter, correct in zip(guess, word):
if letter == correct:
style = "bold white on green"
elif letter in word:
style = "bold white on yellow"
elif letter in ascii_letters:
style = "white on #666666"
else:
style = "dim"
styled_guess.append(f"[{style}]{letter}[/]")
console.print("".join(styled_guess), justify="center")
您使用绿色背景样式化正确的字母。如果字母没有出现在秘密单词中,但是出现在猜测中,您将添加黄色背景。如果字母不正确,则显示在以十六进制值#666666表示的灰色背景上。最后,您将昏暗的替代符号显示出来。
def main():
# 预处理
words_path = pathlib.Path(__file__).parent / "wordlist.txt"
word = get_random_word(words_path.read_text(encoding="utf-8").split("\n"))
guesses = ["_" * 5] * 6
# 处理(主循环)
for idx in range(6):
refresh_page(headline=f"猜测{idx + 1}")
show_guesses(guesses, word)
guesses[idx] = input("\n猜测单词: ").upper()
if guesses[idx] == word:
break
# 后处理
else:
game_over(word)
使用当前的game_over()实现游戏结束时,最后一个猜测没有进行更新。您在input()之前使用show_guesses()来完成这项工作。
import pathlib
import random
from string import ascii_letters
from rich.console import Console
from rich.theme import Theme
# 创建console对象。width是输出的最大宽度,theme是控制台的主题
console = Console(width=40, theme=Theme({"warning": "red on yellow"}))
def main():
# 预处理
words_path = pathlib.Path(__file__).parent / "wordlist.txt"
word = get_random_word(words_path.read_text(encoding="utf-8").split("\n"))
# 初始化guesses列表,每个元素都是“_ _ _ _ _”,表示一个未知的5个字母的单词
guesses = ["_" * 5] * 6
# 处理(主循环)
for idx in range(6):
# 刷新页面
refresh_page(headline=f"Guess {idx + 1}")
# 显示当前状态
show_guesses(guesses, word)
# 输入猜测单词
guesses[idx] = input("\nGuess word: ").upper()
# 如果猜对了,退出循环
if guesses[idx] == word:
break
# 后处理
# 显示游戏结果界面
game_over(guesses, word, guessed_correctly=guesses[idx] == word)
def refresh_page(headline):
# 清空控制台
console.clear()
# 输出标题
console.rule(f"[bold blue]:leafy_green: {headline} :leafy_green:[/]\n")
def get_random_word(word_list):
# 从单词列表word_list中,找出所有长度为5的仅由字母组成的单词。将这些单词首字母改为大写,并保存在words列表中
words = [
word.upper()
for word in word_list
if len(word) == 5 and all(letter in ascii_letters for letter in word)
]
# 随机地返回一个单词
return random.choice(words)
def show_guesses(guesses, word):
for guess in guesses:
# styled_guess是一个由样式文字与猜测单词的每个单词组成的列表
styled_guess = []
for letter, correct in zip(guess, word):
# 如果猜测的字母恰好与单词的字母相同,那么样式是:粗体白字,绿底
if letter == correct:
style = "bold white on green"
# 如果猜测的字母与单词的字母相同,但位置不同,那么样式是:粗体白字,黄底
elif letter in word:
style = "bold white on yellow"
# 如果猜测的字母不在单词中,但是它是字母(不是空格、数字、标点符号等),那么样式是:白字,深灰底
elif letter in ascii_letters:
style = "white on #666666"
# 否则,就是一个空格、数字、标点符号等,那么样式是:暗淡
else:
style = "dim"
styled_guess.append(f"[{style}]{letter}[/]")
# 在控制台输出styled_guess中的内容
console.print("".join(styled_guess), justify="center")
def game_over(guesses, word, guessed_correctly):
# 显示"Game over"界面
refresh_page(headline="Game Over")
# 显示最终结果
show_guesses(guesses, word)
if guessed_correctly:
# 猜测成功
console.print(f"\n[bold white on green]Correct, the word is {word}[/]")
else:
# 猜测不成功
console.print(f"\n[bold white on red]Sorry, the word was {word}[/]")
if __name__ == "__main__":
main()
输出:
步骤5:包括验证和用户评论
在此阶段,您将添加工具,以帮助用户在采取戏剧性行动时穿越游戏。
基本游戏玩法仍然不变,但是您的程序现在更可靠,如果用户犯了任何错误,它会提供帮助。
# wyrdl.py
import pathlib
import random
from string import ascii_letters
From rich.console import Console
from rich.theme import Theme
console = Console(width=40, theme=Theme({"warning": "red on yellow"}))
def main():
# 预处理
words_path = pathlib.Path(__file__).parent / "wordlist.txt"
word = get_random_word(words_path.read_text(encoding="utf-8").split("\n"))
guesses = ["_" * 5] * 6
# 处理(主循环)
for idx in range(6):
refresh_page(headline=f"猜测 {idx + 1}")
show_guesses(guesses, word)
guesses[idx] = guess_word(previous_guesses=guesses[:idx])
if guesses[idx] == word:
break
# 后处理
game_over(guesses, word, guessed_correctly=guesses[idx] == word)
def refresh_page(headline):
console.clear()
console.rule(f"[bold blue]:leafy_green: {headline} :leafy_green:[/]\n")
def get_random_word(word_list):
if words := [
word.upper()
for word in word_list
if len(word) == 5 and all(letter in ascii_letters for letter in word)
]:
return random.choice(words)
else:
console.print("单词列表中没有长度为5的单词", style="warning")
raise SystemExit()
def show_guesses(guesses, word):
for guess in guesses:
styled_guess = []
for letter, correct in zip(guess, word):
if letter == correct:
style = "bold white on green"
elif letter in word:
style = "bold white on yellow"
elif letter in ascii_letters:
style = "white on #666666"
else:
style = "dim"
styled_guess.append(f"[{style}]{letter}[/]")
console.print("".join(styled_guess), justify="center")
def guess_word(previous_guesses):
guess = console.input("\n猜测单词:").upper()
if guess in previous_guesses:
console.print(f"您已经猜测了 {guess}.", style="warning")
return guess_word(previous_guesses)
if len(guess) != 5:
console.print("您的猜测必须为5个字母。", style="warning")
return guess_word(previous_guesses)
if any((invalid := letter) not in ascii_letters for letter in guess):
console.print(
f"非法字母:'{invalid}'。请使用英文字母.",
style="warning",
)
return guess_word(previous_guesses)
return guess
def game_over(guesses, word, guessed_correctly):
refresh_page(headline="游戏结束")
show_guesses(guesses, word)
if guessed_correctly:
console.print(f"\n[bold white on green]正确,单词是{word}[/]")
else:
console.print(f"\n[bold white on red]对不起,单词是{word}[/]")
if __name__ == "__main__":
main()
输出结果:
恭喜!您创建了一个功能丰富的 Wordle 克隆版,您可以将其与所有朋友分享,至少那些熟悉使用终端执行 Python 程序的朋友。