学习Python时犯的4个编码错误

我在学习Python时犯的4个编码错误

几年前,我开始了学习Python的冒险,我已经知道了一些其他的编程语言,如PHP(让我接触到网络开发的第一种语言)、JavaScript(我已经很擅长了,正在写一个UI库)和C#,这是我当时收入的来源。

我通过自己开发一个应用程序来学习Python,因此我在代码中加入了许多JavaScript和C#的做事方式,这很糟糕,虽然有时会成功。我花了一些时间,阅读其他人的代码,并与其他人一起工作,才真正在语言上变得更好。今天我想和大家一起探讨一下我在学习Python时犯的一些错误(代码方面)。


#1 对Python作用域的误解

Python的范围解析是基于所谓的LEGB规则,它是LocalEnclosingGlobalBuiltin的简写。尽管看起来非常简单,但当时对我来说还是有点困惑,例如,考虑下面的例子。

x = 5
def foo():
    x += 1
    print(x)

foo()

-----------
Output
-----------
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
复制代码

对于上面的代码,我本以为它可以工作,改变全局变量x ,最后打印6 。但是,它可以变得更奇怪,让我们看一下下面的改动后的代码。

y = 5
def foo_y():
    print(y)

foo_y()

-----------
Output
-----------
5
复制代码

这到底是怎么回事呢?在一个代码段中,全局变量X 给出了一个UnboundLocalError ,然而,当我们只是试图打印这个变量时,它却能工作。其原因与范围有关。当你在一个作用域(如函数作用域)中对一个变量进行赋值时,该变量就会成为该作用域的局部变量,并对外部作用域中的任何类似命名的变量产生阴影。这就是在第一种情况下发生的事情,当我们做x += 1

如果我们的意图是访问全局变量x ,就像我们的函数foo() ,我们可以做这样的事情。

x = 5
def foo():
    global x
    x += 1
    print(x)

foo()

-----------
Output
-----------
6
复制代码

通过使用关键字global ,允许内部作用域访问全局作用域中声明的变量,也就是说,没有在任何函数中定义的变量。类似地,我们可以使用nonlocal 来产生类似的效果。

def foo():
    x = 5
    def bar():
        nonlocal x
        x += 1
        print(x)
    bar()

foo()

-----------
Output
-----------
6
复制代码

nonlocal 因为 允许你从外部作用域访问变量,然而,在 的情况下,你可以绑定到父作用域或全局作用域上的一个对象。global nonlocal


#2 在迭代列表的同时修改它

尽管这个错误不仅在 Python 中常见,但我发现这个错误在新的 Python 开发者中相当普遍,甚至对一些有经验的开发者也是如此。虽然有时看起来不是那么明显,但在某些场合下,我们最终修改了我们目前正在迭代的数组,导致了不恰当的行为,或者如果我们幸运的话,我们得到了一个错误并很容易注意到它。

但让我举个例子来说明我的意思,假设给定一个数组,你需要将该数组减少到只包含偶数元素,你可能会尝试做这样的事情。

def odd(x): return bool(x % 2)
numbers = [n for n in range(10)]
for i in range(len(numbers)):
    if odd(numbers[i]):
        del numbers[i]

-----------
Output
-----------
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
IndexError: list index out of range
复制代码

在所描述的情况下,当在迭代时删除一个列表或数组的元素时,我们会得到一个错误,因为我们试图访问一个已经不存在的项目。这是一种不好的做法,应该避免,在 python 中有更好的方法来实现类似的事情,其中就有列表理解法。

def odd(x): return bool(x % 2)
numbers = [n for n in range(10)]
numbers[:] = [n for n in numbers if not odd(n)]
print(numbers)

-----------
Output
-----------
[0, 2, 4, 6, 8]
复制代码

你也可以使用filter 函数来实现同样的目的,虽然它可以工作,但有些人认为这不是Pythonic的方法,我有点同意,但我不想陷入这种讨论中。我宁愿给你选择,你可以研究和决定。

def even(x): return not bool(x % 2)
numbers = [n for n in range(10)]
numbers = list(filter(even, numbers))
numbers

-----------
Output
-----------
[0, 2, 4, 6, 8]
复制代码

#3 闭包中的变量绑定

我想从我在twitter(@bajcmartinez)上发布的一个测验开始,我问人们他们认为下面这个片段的结果会是什么。

def create_multipliers():
    return [lambda x : i * x for i in range(5)]

for multiplier in create_multipliers():
    print(multiplier(2))

-----------
Output
-----------
8
8
8
8
8
复制代码

对于很多人来说,包括我自己,第一次遇到这个问题时,我们认为结果会是。

0
2
4
6
8
复制代码

然而,代码的结果实际上完全不同,我们非常不解为什么。实际发生的情况是,Python 会做一个迟到的绑定行为,根据这个行为,在闭包中使用的变量的值是在调用内部函数的时候查询的。所以在我们的例子中,只要有任何一个返回的函数被调用,i 的值就会在调用时在周围的范围内被查找。

解决这个问题的方法看起来有点黑,但它实际上是可行的

def create_multipliers():
    return [lambda x, i=i : i * x for i in range(5)]

for multiplier in create_multipliers():
    print(multiplier(2))

-----------
Output
-----------
0
2
4
6
8
复制代码

通过使用lambda函数的默认参数来传递i 的值,我们可以生成函数来完成所需的行为。我对这个解决方案非常不解,而且我仍然认为它不是很优雅,然而,有些人喜欢它。如果你知道这个问题的另一个可能的解决方案,请在评论中告诉我,我很想读到它。


#4 与Python标准库模块的名称冲突

这个问题在我刚开始的时候其实很常见,即使是现在,有时我也会犯这种错误。这个问题是由于你的一个模块的名字与Python标准库中的一个模块的名字相同而产生的。(例如,你的代码中可能有一个名为email.py的模块,这将与标准库中的同名模块发生冲突)。

也许名称冲突本身不会对你的代码产生任何问题,但有时我们会覆盖 Python 标准库中的一个函数或模块,该函数或模块后来被用于已安装的库中,它要么通过抛出错误,要么通过错误行为而发生冲突。无论如何,这都是一种不好的情况。

一个典型的错误是下面这个。

a = list()
print(a)

list = [1, 2, 3] # This is where we break it
a = list()

-----------
Output
-----------
[]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not callable
复制代码

通过简单地创建一个名为list 的变量,我们破坏了对list 函数的访问。而且,即使有其他的访问方式(例如:__builtins__.list() ),我们也应该避免这种名称。


总结

这篇文章并没有涵盖开发者在用Python编码时的所有常见错误,而是那些我最纠结的事情。如果你想知道更多关于如何写好Python代码和避免其他一些错误的信息,我推荐你阅读。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值