我从 Python 程序员开始看到的一个常见问题是,“如何让我的代码更 Pythonic?”像“Pythonic”这样的词的问题在于它的含义是模糊的:它对不同的人意味着不同的东西。意思也不是一成不变的。代码是否是 Pythonic 取决于您使用的 Python 版本,编写 Pythonic 代码的最佳实践可能会随着时间而改变。
在本文中,我将通过查看一些具体示例来分享我对 Python 化代码的看法。我还将为您提供一些精心挑选的资源,这些资源将帮助您建立一个心智模型,以决定代码何时是 Pythonic 或不是 Pythonic。
但首先,让我们至少就 Pythonic 这个词的某种定义达成一致。
“Pythonic”是什么意思?
Python 语言已有 30 多年的历史。在那段时间里,Python 程序员集体获得了大量使用该语言用于各种目的的经验。随着时间的推移,这种集体经验已被分享并提炼成最佳实践——通常称为Pythonic 方式。
The Zen of Python由 Tim Peters 编写,可通过在 REPL 中键入import this
来访问任何 Python 安装,传统上体现了 Pythonic 思维方式:
>>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
Python之禅之美,也是Python初学者最讨厌的特性。 Zen优雅地捕捉了 Pythonic 的精神,而没有给出任何明确的建议。例如,第一条原则:“美胜于丑”。是当然!但是我如何把丑陋的代码变得漂亮呢?什么是漂亮的代码?
Python 之禅的模棱两可,无论多么令人沮丧,但它现在与蒂姆·彼得斯在 1999 年写它时一样重要。它作为一组指导原则,使您能够区分 Python 代码和非 Python 代码并为自己做出决定的心理框架奠定了基础。
那么,这让我们对“Pythonic”这个词的实际定义有什么影响呢?我发现的最佳定义来自2014 年 StackOverflow 对“Pythonic 是什么意思?”问题的回答。将 Pythonic 代码描述为:
[C] ode 不仅语法正确,而且遵循 Python 社区的约定,并以预期的方式使用该语言。
这里有两个关键要点:
- 形容词 Pythonic 更多地与风格有关,而不是语法,尽管 Pythonic 习语通常具有超越纯粹风格选择的含义,包括更好的性能。
- 作为 Pythonic 传递的东西是由 Python 社区驱动的。
因此,现在我们至少对 Python 程序员将代码称为 Pythonic 的含义有了一些了解,让我们看看现在可以编写更多 Pythonic 代码的三种具体方法。
提示 #1:熟悉 PEP8
PEP8是 Python 的官方风格指南。 PEP代表Python增强建议。 PEP 是提出新 Python 特性的文档,并在 Python 社区讨论其接受或拒绝时作为该特性的官方文档。遵循 PEP8 不会让你的代码达到 Python 风格的完美,但它确实有助于让你的代码看起来对许多 Python 程序员来说很熟悉。
PEP8 处理诸如如何处理代码中的空格之类的事情,例如使用四个空格而不是制表符进行缩进,或者最大行长应该是多少,根据 PEP8,它是 79 个字符——尽管这可能是最广泛忽视的 PEP8 建议。
如果您是 Python 编程的新手,我建议从 PEP8 内部化的第一件事是命名约定的建议。例如,您应该以lowercase_with_underscores
样式编写函数和变量名称:
# Correct seconds_per_hour = 3600 # Incorrect secondsperhour = 3600 secondsPerHour = 3600
类名应使用CapitalizedWords
样式:
# Correct class SomeThing: pass # Incorrect class something: pass class some_thing: pass
以UPPER_CASE_WITH_UNDERSCORES
样式编写常量:
# Correct PLANCK_CONSTANT = 6.62607015e-34 # Incorrect planck_constant = 6.6260715e-34 planckConstant = 6.6260715e-34
PEP8 中提出的空白建议包括如何在运算符周围、函数参数名称和参数周围使用空格,以及如何打破长行。虽然多年的阅读和编写符合 PEP8 的 Python 代码的练习将帮助您内化这些建议,但仍然需要记住很多。
如果您无法记住所有 PEP8 的约定,请不要担心。你不需要!像flake8
这样的工具可以帮助你找到并修复代码中的 PEP8 问题。您可以使用pip
安装flake8
:
# Linux/macOS $ python3 -m pip install flake8 # Windows $ python -m pip install flake8
flake8
可用作命令行应用程序来扫描 Python 文件中的样式违规。例如,假设我有一个名为myscript.py
的文本文件,其中包含以下代码:
def add( x, y ): return x+y num1=1 num2=2 print( add(num1,num2) )
针对此代码运行flake8
告诉您存在哪些违规行为以及它们的确切位置:
$ flake8 myscript.jl myscript.jl:1:9: E201 whitespace after '(' myscript.jl:1:11: E231 missing whitespace after ',' myscript.jl:1:13: E202 whitespace before ')' myscript.jl:4:1: E305 expected 2 blank lines after class or function definition, found 1 myscript.jl:4:5: E225 missing whitespace around operator myscript.jl:5:5: E225 missing whitespace around operator myscript.jl:6:7: E201 whitespace after '(' myscript.jl:6:16: E231 missing whitespace after ',' myscript.jl:6:22: E202 whitespace before ')'
flake8
的每一行输出都会告诉您问题在哪个文件中,问题在哪一行,错误从哪一行开始,错误号(如果您愿意,可以使用这些代码配置flake8
以忽略特定错误) ,以及错误描述:
如何读取 flake8 输出
您甚至可以设置 VS Code 之类的编辑器,在编写代码时使用flake8
对代码进行lint ,以不断检查代码是否违反 PEP8。当flake8
发现问题时,您的代码有问题的部分下方会出现一条弯曲的红线,您可以在内置终端的“问题”选项卡中查看已检测到哪些错误:
在 Visual Studio Code 中使用 flake8
flake8
是在代码中查找 PEP8 错误的绝佳工具,但您仍然需要手动修复所有这些错误。这可能是很多工作。幸运的是,有一种方法可以使整个过程自动化。
Python 的black
自动格式化程序是一种用于自动格式化代码以符合 PEP8 的工具。当然,PEP8 建议为风格选择留下了很大的回旋余地, black
为您做出了很多决定。您可能同意也可能不同意这些决定。 black
是最小可配置的,所以你可能想在使用它之前先玩弄它。
您可以使用pip
安装black
:
# Linux/macOS $ python3 -m pip install black # Windows $ python -m pip install black
安装后,您可以在 shell 中使用black --check
命令来查看black
是否会对文件进行任何更改:
$ black --check myscript.py would reformat myscript.py Oh no! ? ? ? 1 file would be reformatted.
您可以使用--diff
标志来查看black
会产生什么变化的差异:
$ black --diff myscript.py --- myscript.py 2022-03-15 21:27:20.674809 +0000 +++ myscript.py 2022-03-15 21:28:27.357107 +0000 @@ -1,6 +1,7 @@ -def add( x, y ): - return x+y +def add(x, y): + return x + y -num1=1 -num2=2 -print( add(num1,num2) ) + +num1 = 1 +num2 = 2 +print(add(num1, num2)) would reformat myscript.py All done! ✨ ? ✨ 1 file would be reformatted.
要自动格式化您的文件,请将文件名传递给black
命令:
$ black myscript.py reformatted myscript.py All done! ✨ ? ✨ 1 file reformatted. # Show the formatted file $ cat myscript.py def add(x, y): return x + y num1 = 1 num2 = 2 print(add(num1, num2))
要检查您的文件现在是否符合 PEP8,请再次对其运行flake8
并查看是否有任何错误:
# No output from flake8 so everything is good! $ flake8 myscript.py
使用black
时要记住的一件事是,默认情况下, black
将最大行长设置为 88 列。这与 PEP8 对 79 列行的建议有所不同,因此即使在使用black
时,您也可能会看到flake8
报告行长度错误。您可以将black
配置为使用 79 列,或者将flake8
配置为接受更长的行长度。许多 Python 开发人员使用 88 列而不是 79 列,有些甚至设置black
和flake8
以使用更长的行长。
重要的是要记住 PEP8 只是一组建议,尽管许多 Python 程序员认真对待这些建议。但是 Python 中没有任何东西可以强制执行 PEP8 样式指南。如果您强烈反对 PEP8 中的某些内容,那么请务必忽略它!但是,如果您确实想严格遵守 PEP8,那么flake8
和black
等工具可以让您的生活更轻松。
提示 #2:避免 C 风格的循环
在像 C 或 C++ 这样的语言中,在遍历数组时跟踪索引变量是很常见的。例如,当被要求打印列表的元素时,来自 C 或 C++ 的新 Python 程序员编写类似以下内容的情况并不少见:
>>> names = ["JL", "Raffi", "Agnes", "Rios", "Elnor"] >>> # Using a `while` loop >>> i = 0 >>> while i < len(names): ... print(names[i]) ... i += 1 JL Raffi Agnes Rios Elnor >>> # Using a `for` loop >>> for i in range(len(names)): ... print(names[i]) JL Raffi Agnes Rios Elnor
但是,您可以直接迭代列表中的项目,而不是迭代索引:
>>> for name in names: ... print(name) JL Raffi Agnes Rios Elnor
然而,避免 C 风格的循环比直接迭代列表中的项目要深入得多。利用 Python 习语,例如列表推导式、内置函数(例如min()
、 max()
和sum()
以及使用对象方法可以帮助您的 Python 代码更上一层楼。
更喜欢列表理解而不是简单for
循环
一个常见的编程任务是处理一个数组中的元素并将结果存储在一个新数组中。例如,假设您有一个数字列表,并希望将其转换为这些数字的平方列表。你知道你应该避免 C 风格的循环,所以你最终可能会写这样的东西:
>>> nums = [1, 2, 3, 4, 5] >>> squares = [] >>> for num in nums: ... squares.append(num ** 2) ... >>> squares [1, 4, 9, 16, 25]
一种更 Pythonic 的方法是使用列表推导:
>>> squares = [num ** 2 for num in nums] # <-- List comprehension >>> squares [1, 4, 9, 16, 25]
列表推导一开始可能很难理解。但是,如果您熟悉在数学中编写集合的集合构建符号,那么列表推导式可能已经很熟悉了。
以下是我通常对列表推导的看法:
- 首先创建一个空列表文字:
[]
- 如果您使用
for
循环构建列表,则列表推导中的第一件事是通常放在.append()
方法中的任何内容:
[ num ** 2 ]
- 最后,将
for
循环的标题放在列表的末尾:
[num ** 2 for num in nums ]
列表推导式是编写 Pythonic 代码时需要掌握的一个重要概念。但它们可能会被过度使用。它们也不是 Python 中唯一的一种理解。在接下来的部分中,您将了解其他推导,例如生成器表达式和字典推导,并查看一个示例,说明何时可以避免使用列表推导。
使用内置函数,如min()
、 max()
和sum()
另一个常见的编程任务是在数字数组中找到最小值或最大值。使用for
循环,您可以在列表中找到最小数字,如下所示:
>>> nums = [10, 21, 7, -2, -5, 13] >>> min_value = nums[0] >>> for num in nums[1:]: ... if num < min_value: ... min_value = num ... >>> min_value -5
一种更 Pythonic 的方法是使用min()
内置函数:
>>> min(nums) -5
同样,无需编写循环来查找列表中的最大值。您可以使用max()
内置函数:
>>> max(nums) 21
要查找列表中数字的总和,您可以编写一个for
循环。但更 Pythonic 的方法是使用sum()
函数:
>>> # Not Pythonic: Use a `for` loop >>> sum_of_nums = 0 >>> for num in nums: ... sum_of_nums += num ... >>> sum_of_nums 44 >>> # Pythonic: Use `sum()` >>> sum(nums) 44
sum()
的另一个 Pythonic 用途是计算满足某些条件的列表元素的数量。例如,下面是一个for
循环,它计算列表中以字母 A 开头的字符串的数量:
>>> capitals = ["Atlanta", "Houston", "Denver", "Augusta"] >>> count_a_capitals = 0 >>> for capital in capitals: ... if capital.startswith("A"): ... count_a_capitals += 1 ... >>> count_a_capitals 2
将sum()
与列表推导相结合,将for
循环减少为一行代码:
>>> sum([capital.startswith("A") for capital in capitals]) 2
尽管很可爱,但您可以通过删除列表周围的括号将列表推导替换为生成器表达式,从而使其更加 Pythonic:
>>> sum(capital.startswith("A") for capital in capitals) 2
这究竟是如何工作的?列表推导和生成器表达式都返回一个包含True
和False
值的迭代,对应于capitals
列表中的字符串是否以字母"A"
开头:
>>> [capital.startswith("A") for capital in capitals] [True, False, False, True]
在 Python 中, True
和False
是变相的整数。 True
等于1
, False
等于0
:
>>> isinstance(True, int) True >>> True == 1 True >>> isinstance(False, int) True >>> False == 0 True
当您将列表推导式或生成器表达式传递给sum()
时, True
和False
值分别被视为1
和0
。由于有两个True
值和两个False
值,因此总和等于2
。
使用sum()
来计算有多少列表元素满足一个条件,这突出了 Pythonic 代码概念的一个重要点。就个人而言,我发现sum()
的这种用法非常符合 Python 风格。毕竟,它利用了几个 Python 语言特性来创建在我看来,简洁易读的代码。然而,并不是每个 Python 开发者都同意我的观点。
有人可能会争辩说,这个例子违反了 Python 之禅的原则之一:“显式优于隐式”。毕竟, True
和False
是整数并不明显, sum()
甚至应该与True
和False
值的列表一起使用。理解sum()
的这种用法需要深入理解 Python 的内置类型。
True
和False
的更多信息,以及关于 Python 中数字的其他令人惊讶的事实,请查看我的文章3 Things You Might Not Know About Numbers in Python 。没有一套严格的规则可以告诉您代码是否是 Pythonic。总有一个灰色地带。当遇到一个感觉可能在这个灰色区域的代码示例时,请使用您的最佳判断。总是在可读性方面犯错,不要害怕与同事联系或使用社交媒体寻求帮助。
提示 #3:使用正确的数据结构
编写干净的 Pythonic 代码的很大一部分归结为为手头的任务选择合适的数据结构。 Python 以“包含电池”的语言而闻名。 Python 中包含的一些电池是高效且随时可用的数据结构。
使用字典进行快速查找
假设您有一个名为clients.csv
的 CSV 文件,其中包含一家企业的客户数据,如下所示:
first_name,last_name,email,phone Manuel,Wilson,[email protected],757-942-0588 Stephanie,Gonzales,[email protected],385-474-4769 Cory,Ali,[email protected],810-361-3885 Adam,Soto,[email protected],724-603-5463
假设您的任务是编写一个程序,该程序将电子邮件地址作为输入,如果存在这样的客户,则使用该电子邮件输出客户的电话号码。你会怎么做呢?
您可以使用csv
模块中的DictReader
对象将此文件的每一行作为字典读取:
>>> import csv >>> with open("clients.csv", "r") as csvfile: ... clients = list(csv.DictReader(csvfile)) ... >>> clients [{'first_name': 'Manuel', 'last_name': 'Wilson', 'email': '[email protected]', 'phone': '757-942-0588'}, {'first_name': 'Stephanie', 'last_name': 'Gonzales', 'email': '[email protected]', 'phone': '385-474-4769'}, {'first_name': 'Cory', 'last_name': 'Ali', 'email': '[email protected]', 'phone': '810-361-3885'}, {'first_name': 'Adam', 'last_name': 'Soto', 'email': '[email protected]', 'phone': '724-603-5463'}]
clients
是一个字典列表,因此要找到具有给定电子邮件的客户,例如[email protected]
,您需要遍历列表并将每个客户的电子邮件与目标电子邮件进行比较,直到找到正确的客户:
>>> target = "[email protected]" >>> phone = None >>> for client in clients: ... if client["email"] == target: ... phone = client["phone"] ... break ... >>> print(phone) 385-474-4769
这段代码的问题在于循环客户端列表效率低下。如果 CSV 文件中有大量客户,您的程序可能会花费大量时间扫描列表以查找具有匹配电子邮件的客户。如果您需要经常进行此检查,则可能会浪费大量时间。
更 Pythonic 的方法是忘记将客户端存储在列表中,并使用字典将客户端映射到他们的电子邮件地址。一个很好的方法是使用字典理解:
>>> with open("clients.csv", "r") as csvfile: ... # Use a `dict` comprehension instead of a `list` ... clients = {row["email"]: row["phone"] for row in csv.DictReader(csvfile)} ... >>> clients {'[email protected]': '757-942-0588', '[email protected]': '385-474-4769', '[email protected]': '810-361-3885', '[email protected]': '724-603-5463'}
字典推导很像列表推导:
- 首先创建一个空字典:
{}
- 然后放入以冒号分隔的键值对:
{ row[“email”]: row[“phone”] }
- 最后,编写一个循环遍历 CSV 文件中每一行的
for
表达式:
{row[“email”]: row[“phone”] for row in csv.DictReader(csvfile) }
翻译成for
循环,这个字典理解看起来像这样:
>>> clients = {} >>> with open("clients.csv", "r") as csvfile: ... for row in csv.DictReader(csvfile): ... clients[row["email"]] = row["phone"]
制作clients
字典后,您可以使用他们的电子邮件地址找到客户的电话号码,而无需编写更多循环:
>>> target = "[email protected]" >>> clients[target] 385-474-4769
这段代码不仅比在列表上循环更短,而且效率更高。 Python 可以直接跳转到字典中的正确值,而无需任何循环。但是,有一个问题。如果客户端中没有客户clients
有匹配的电子邮件,则会引发KeyError
:
>>> clients["[email protected]"] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: '[email protected]'
处理此问题的一种方法是捕获KeyError
并在未找到客户端时打印默认值:
>>> target = "[email protected]" >>> try: ... phone = clients[target] ... except KeyError: ... phone = None ... >>> print(phone) None
不过,使用字典的.get()
方法,还有一种更 Pythonic 的方法可以做到这一点。 .get()
如果键存在则返回键的对应值,否则返回None
:
>>> clients.get("[email protected]") '385-474-4769'
让我们并排比较这两种解决方案:
import csv target = "[email protected]" phone = None # Un-Pythonic: loop over a list with open("clients.csv", "r") as csvfile: clients = list(csv.DictReader(csvfile)) for client in clients: if client["email"] == target: phone = client["phone"] break print(phone) # Pythonic: lookup in a dictionary with open("clients.csv", "r") as csvfile: clients = {row["email"]: row["phone"] for row in csv.DictReader(csvfile)} phone = clients.get(target) print(phone)
Pythonic 解决方案在不牺牲可读性的情况下更加简洁和高效。
利用集合操作
集合是 Python 中被低估的数据结构。结果,即使是中级 Python 开发人员也倾向于忽略集合,并错过利用它们发挥优势的机会。
也许 Python 中集合最著名的用例是从列表中删除重复项:
>>> nums = [1, 3, 2, 3, 1, 2, 3, 1, 2] >>> unique_nums = list(set(nums)) >>> unique_nums [1, 2, 3]
但是你可以用套装做更多的事情。我在代码中经常使用的一个用例是使用集合来有效地过滤可迭代的值。当您还需要唯一值时,这最有效。
这是一个人为但并非不切实际的例子。假设店主有一个客户的 CSV 文件,其中包含他们的电子邮件地址。我们将重用上一节中的clients.csv
文件。店主还有另一个 CSV 文件,其中包含上个月的订单,其中还包含电子邮件地址。也许这个 CSV 文件被称为orders.csv
,看起来像这样:
date,email,items_ordered 2022/03/01,[email protected],2 2022/03/04,[email protected],3 2022/03/07,[email protected],1
店主想通过电子邮件向过去一个月内没有订购任何商品的每位客户发送折扣券。一种方法是从clients.csv
和orders.csv
文件中读取电子邮件,并使用列表推导来过滤客户的电子邮件:
>>> import csv >>> # Create a list of all client emails >>> with open("clients.csv", "r") as clients_csv: ... client_emails = [row["email"] for row in csv.DictReader(clients_csv)] ... >>> # Create a list of emails from orders >>> with open("orders.csv") as orders_csv: ... order_emails = [row["email"] for row in csv.DictReader(orders_csv)] ... >>> # Use a list comprehension to filter the clients emails >>> coupon_emails = [email for email in clients_emails if email not in order_emails] >>> coupon_emails ["[email protected]", "[email protected]"]
上面的代码运行良好,而且看起来像Pythonic。但是假设店主每个月有数百万的客户和订单。 (他们显然非常成功!)过滤电子邮件以确定向哪些客户发送优惠券需要遍历整个client_emails
列表。如果client.csv
和orders.csv
文件中有重复行怎么办?事故发生了,你知道的。
更 Pythonic 的方法是读入客户端并将电子邮件排序为集合,并使用集合差异运算符过滤客户端电子邮件集:
>>> import csv >>> # Create a set of all client emails using a set comprehension >>> with open("clients.csv", "r") as clients_csv: ... client_emails = {row["email"] for row in csv.DictReader(clients_csv)} ... >>> # Create a set of emails frp, orders using a set comprehension >>> with open("orders.csv", "r") as orders_csv: ... order_emails = {row["email"] for row in csv.DictReader(orders_csv)} ... >>> # Filter the client emails using set difference >>> coupon_emails = client_emails - order_emails >>> coupon_emails {"[email protected]", "[email protected]"}
这种方法比前一种方法效率更高,因为它只循环客户端电子邮件一次,而不是两次。它还具有从两个 CSV 文件中自然删除任何重复电子邮件的优点。
学习编写 Pythonic 代码的三本书
你不可能在一夜之间学会编写干净的 Pythonic 代码。你需要学习大量的代码示例,练习编写自己的代码,并咨询其他 Python 开发人员。为了在您的旅程中为您提供帮助,我整理了三本书的清单,我发现这些书对了解 Pythonic 方式非常有帮助。
免责声明:以下部分包含附属链接。如果您决定通过我的链接购买其中一本书,我将免费获得一小笔佣金。
Dan Bader 的Python 技巧
Dan Bader 的短小精悍的书Python Tricks: A Buffet of Awesome Python Features是初学者到中级 Python 程序员了解更多关于编写 Pythonic 代码的绝佳起点。
Python Tricks将教你编写干净、惯用的 Python 的模式、编写函数的最佳实践、如何有效地使用 Python 的面向对象编程特性等等。
Brett Slatkin 的有效 Python
Brett Slatkin 的《 Effective Python 》是我在学习 Python 语法后阅读的第一本书,让我看到了惯用的 Pythonic 代码的力量。
正如本书的副标题所述, Effective Python涵盖了编写更好 Python 的 90 种特定方法。仅第一章,标题为Python Thinking ,就是一个技巧和窍门的金矿,即使是初学者 Python 程序员也会觉得很有帮助,尽管初学者可能会发现本书的其余部分难以理解。
流利的 Python由 Luciano Ramalho
如果我只能拥有一本关于 Python 的书,那么 Luciano Ramalho 的Fluent Python就是其中之一。
Fluent Python充满了清晰的说明支持的实际示例,对于希望学习如何编写 Pythonic 代码的任何人来说,它都是一个极好的指南。但是,请记住, Fluent Python不适合 Python 初学者。正如书中前言所说:
如果你只是在学习 Python,那么这本书将很难理解。不仅如此,如果您在 Python 之旅中过早阅读它,它可能会给您一种印象,即每个 Python 脚本都应该利用特殊方法和元编程技巧。过早的抽象与过早的优化一样糟糕。
然而,经验丰富的 Python 程序员将从本书中受益匪浅。
Ramalho 最近更新了他的现代 Python 书籍。目前,第二版只接受预购。我强烈建议预订第二版,因为第一版现已过时。
下一步
这篇文章涵盖了很多内容。你学到了:
- PEP8 风格指南如何帮助您编写标准化的 Python 代码
- 如何通过直接迭代和利用 Python 的一些内置函数来避免 C 风格的循环
- 为什么选择正确的数据结构可以让您编写更短且更高效的代码
这些技巧将帮助您编写更多 Pythonic 代码,但这只是一个开始。掌握 Python 需要数年时间。在您努力掌握 Python 的过程中,Pythonic 代码的公认规范可能会发生变化,因此与当前最佳实践保持同步至关重要。 r/learnpython subreddit 是提问和获得帮助的好地方。我也很乐意在Twitter 上回答问题。
但第一步是弄脏你的手并练习你学到的东西。正如Python 之禅所说:“现在总比没有好。”
你有兴趣学习新的东西吗?我为 Python 编程和技术写作提供一对一的私人辅导。点击这里了解更多信息。