Python 新手常犯的 3 个错误

2025-05-27

Python 新手常犯的 3 个错误

上周末,我开始在exercism.io上指导Python 方向的同学。我之前并不确定会有什么收获,但过去一周,我已经指导了大约 50 个人,帮助他们的解决方案从“测试通过”提升到“测试通过、可读性强、Python风格”。我完全被迷住了。这真是太棒了。我打算专门写一篇关于那段经历的文章,但不是这篇。这篇文章主要想谈谈过去一周我看到的三个最常见的错误,以及一些可能更好的替代方案!那就让我们开始倒计时吧!

1. If 语句或循环的深度嵌套

# Calculating whether or not 'year' is a leap year

if year % 4 == 0:
    if year % 100 == 0:
        if year % 400 == 0:
            return True
        else:
            return False
    else:
        return True
else:
    return False
Enter fullscreen mode Exit fullscreen mode

很多时候,我会从《Python 之禅》中引用一句话,作为对“受训者”(不要与“manitee”混淆)的反馈。每当我遇到这个问题时,我总是会这样开头:

扁平比嵌套更好。

如果你用不集中的目光看代码,只看形状而不看文字,你会看到一堆箭头出去又回来:

\
 \
  \
   \
    \
    /
   /
  /
  \
   \
    \
     \
     /
    /
   /
  /
 /
/
Enter fullscreen mode Exit fullscreen mode

这绝对不是一件坏事,但它是一种“代码异味”,或者是一种蜘蛛感应,感觉某些东西可能被重构。

那么,除了嵌套,你还能做什么呢?有几件事可以尝试。首先,反转你的逻辑,使用“提前返回”来逐个剥离解空间中的小块。

if year % 400 == 0:
    return True
if year % 100 == 0:
    return False
if year % 4 == 0:
    return True
return False
Enter fullscreen mode Exit fullscreen mode

如果该数字能被 400 整除,我们就立即返回 true。否则,对于我们代码的其余部分,我们可以知道该年份肯定不能被 400 整除。因此,此时,任何其他能被 100 整除的年份都不是闰年。因此,我们通过返回 False 来剥开洋葱的这层外皮。

之后,我们可以知道这肯定不是 400year100的倍数,并且其余代码遵循相同的模式。

避免嵌套的另一种方法是使用“布尔运算符:” and, or, and not。我们可以组合if语句,从而省去一层嵌套!

if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
    return True
else:
    return False
Enter fullscreen mode Exit fullscreen mode

当然,这引出了我们的第二件事……

2. 从 If 语句返回布尔值

我们从上面的最后一个例子开始:

if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
    return True
else:
    return False
Enter fullscreen mode Exit fullscreen mode

每当你发现自己在写:

if something:
    return True
else:
    return False
Enter fullscreen mode Exit fullscreen mode

您应该记住,语句的子句if本身就是布尔值!

>>> year = 2000
>>> year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
True
Enter fullscreen mode Exit fullscreen mode

那么,为什么不少输入一点并直接返回布尔运算的结果呢?

return (year % 4 == 0 and (year % 100 != 0 or year % 400 == 0))
Enter fullscreen mode Exit fullscreen mode

当然,此时,代码行可能会变得有点长,但是代码现在冗余度减少了一点!

3. 清单就像锤子——并非所有东西都是钉子

出现这种情况的可能方式有两种:

some_numbers = [1, 2, 5, 7, 8, ...]
other_numbers = [1, 3, 6, 7, 9, ...]
# Let's try to combine these two without duplicates
for number in other_numbers:
    if number not in some_numbers:
        some_numbers.append(number)
Enter fullscreen mode Exit fullscreen mode

或者:

data = [["apple", 4], ["banana", 2], ["grape", 14]]
# What fruits do we have?
for item in data:
    print(item[0])
# => "apple" "banana" "grape"
# How many grapes do we have?
for item in data:
    if item[0] == "grape":
        print(item[1])
# => 14
Enter fullscreen mode Exit fullscreen mode

在第一种情况下,您需要跟踪几组项目,并希望将它们组合在一起而不重复。这时,使用就非常理想了set。集合本身会跟踪其项目(尽管不会跟踪顺序,因此如果顺序很重要,请不要使用集合)。您可以使用内置set()函数或花括号 ( {}) 来声明它们。

some_numbers = {1, 2, 5, 7, 8}
other_numbers = {1, 3, 6, 7, 9}
# Sets use the 'binary or' operator to do "unions"
# which is where they take all of the unique elements
some_numbers | other_numbers
# => {1, 2, 3, 5, 6, 7, 8, 9}

# You can even add single items in!
some_numbers.add(10)
# => {1, 2, 5, 7, 8, 10}

# But adding a duplicate doesn't change anything
some_numbers.add(1)
# => {1, 2, 5, 7, 8, 10}
Enter fullscreen mode Exit fullscreen mode

在第二种情况下,顺序可能同样不重要。您希望通过“标签”或其他方式跟踪一些数据,但能够将它们放在一起并在必要时列出。这次,您可能需要一个。您可以使用内置函数或花括号 ( )dict创建它们。不过,这次,您需要用冒号分隔标签(键)和值。dict(){}

fruits = {
    "apples": 4,
    "bananas": 2,
    "grapes": 14,
}
Enter fullscreen mode Exit fullscreen mode

您可以列出所有键(或值!)。

list(fruits.keys())
# => ["apples", "bananas", "grapes"]
list(fruits.values())
# => [4, 2, 14]

# Or both!
list(fruits.items())
# => [("apples", 4), ("bananas", 2), ("grapes", 14)]
Enter fullscreen mode Exit fullscreen mode

您还可以向它询问特定键(或赋予它新值)。

# How many grapes are there?
fruits["grapes"]
# => 14

# Not anymore.  I ate some.
fruits["grapes"] = 0

fruits["grapes"]
# => 0
Enter fullscreen mode Exit fullscreen mode

使用列表,你的算法会循环遍历每个元素以找到正确的元素。dict列表的查找速度非常快,所以,即使你的dict列表有无数个水果,查找速度grapes仍然非常快——而且输入起来很容易!没有循环!

行动呼吁

Exercism 需要导师!如果你认为自己可以成为一名优秀的导师(或者仅仅是一些简单练习的优秀导师),那就去他们的导师注册页面注册吧。目前,Rust、Golang 和 Elixir 的学习进度非常紧张,需要你的帮助!

文章来源:https://dev.to/rpalo/3-common-mistakes-that-python-newbies-make-4da7
PREV
清洁架构:代码背后的概念
NEXT
我如何将我的 Raspberry Pi 变成私有云服务器