python编码错误
做错事绝非易事,但犯错是任何学习过程的一部分,从学习走路到学习新的编程语言(例如Python)。
这里列出了我在学习Python时出错的三件事,以使新的Python程序员可以避免犯同样的错误。 这些错误或者是我很久以前就解决的,或者是造成了需要花费数小时才能解决的重大问题的错误。
请注意年轻的编码员,其中一些错误是浪费下午的时间!
1.可变数据类型作为函数定义中的默认参数
有道理吧? 假设您有一个小功能,可以搜索当前页面上的链接并将其附加到另一个提供的列表中。
def search_for_links
( page
, add_to
=
[
]
) :
new_links
= page.
search_for_links
(
)
add_to.
extend
( new_links
)
return add_to
从表面上看,这看起来像是完全正常的Python,确实如此。 有用。 但是有问题。 如果我们提供了add_to参数的列表,它将按预期工作。 但是,如果我们使用默认值,则会发生一些有趣的事情。
尝试以下代码:
def fn
( var1
, var2
=
[
]
) :
var2.
append
( var1
)
print var2
fn
(
3
)
fn
(
4
)
fn
(
5
)
您可能希望我们会看到:
[3]
[4]
[5]
但是我们实际上看到了这一点:
[3]
[3,4]
[3,4,5]
为什么? 好了,您看到的是,每次都使用相同的列表。 在Python中,当我们以这种方式编写函数时,列表将作为函数定义的一部分实例化。 每次运行该函数时都不会实例化它。 这意味着该函数将一次又一次地使用完全相同的列表对象,除非我们当然提供另一个对象:
fn ( 3 , [ 4 ] )
[4,3]
符合预期。 实现所需结果的正确方法是:
def fn
( var1
, var2
=
None
) :
if
not var2:
var2
=
[
]
var2.
append
( var1
)
或者,在我们的第一个示例中:
def search_for_links
( page
, add_to
=
None
) :
if
not add_to:
add_to
=
[
]
new_links
= page.
search_for_links
(
)
add_to.
extend
( new_links
)
return add_to
这会将实例化从模块加载时间移开,以使其在每次函数运行时发生。 请注意,对于不可变的数据类型(例如元组 , 字符串或整数) ,这不是必需的。 这意味着可以执行以下操作:
def func
( message
=
"my message"
) :
print message
2.可变数据类型作为类变量
紧跟着最后一个错误的是一个非常相似的错误。 考虑以下:
class URLCatcher
( object
)
:
urls
=
[
]
def add_url
(
self
, url
)
:
self
. urls
. append
( url
)
这段代码看起来很正常。 我们有一个带有URL存储的对象。 当我们调用add_url方法时,它会将给定的URL添加到商店中。 完美吧? 让我们来看看它的作用:
a
= URLCatcher
(
)
a.
add_url
(
'http://www.google.'
)
b
= URLCatcher
(
)
b.
add_url
(
'http://www.bbc.co.'
)
b.urls
[' http ://www.google.com,'http://www.bbc.co.uk ']
网址
[' http ://www.google.com,'http://www.bbc.co.uk ']
等等,什么? 我们没想到。 我们实例化了两个单独的对象a和b 。 为A提供了一个URL,为b提供了另一个URL。 两个对象都有两个URL怎么办?
事实证明,这与第一个示例有点相同。 创建类定义时,将实例化URL列表。 该类的所有实例都使用相同的列表。 现在,在某些情况下这是有利的,但是大多数时候您不想这样做。 您希望每个对象都有一个单独的存储。 为此,我们将修改如下代码:
class URLCatcher
(
object
) :
def
__init__
(
self
) :
self .
urls
=
[
]
def add_url
(
self
, url
) :
self .
urls .
append
( url
)
现在,在创建对象时实例化URL列表。 当我们实例化两个单独的对象时,它们将使用两个单独的列表。
3.可变的分配错误
这使我困惑了一段时间。 让我们稍微改变一下齿轮,使用另一个可变的数据类型dict 。
a
=
{
'1' :
"one"
,
'2' :
'two'
}
现在让我们假设我们要采取字典和其他人使用它的某个地方,离开原来的不变。
b
= a
b
[
'3'
]
=
'three'
简单吗?
现在让我们看一下我们最初的字典a ,我们不想修改的字典:
{ '1' : "one" , '2' : 'two' , '3' : 'three' }
哇,等一下。 那么b是什么样子?
{ '1' : "one" , '2' : 'two' , '3' : 'three' }
等等什么 但是……让我们退后一步,看看其他不可变类型(例如元组)会发生什么:
c
=
(
2
,
3
)
d
= c
d
=
(
4
,
5
)
现在c是:
(2、3)
d是:
(4、5)
可以正常运行。 那么在我们的示例中发生了什么? 当使用可变类型,我们得到的东西,表现得有点更像C的指针。当我们说b在上面,我们真正的意思是代码= A:B现在也是一个参考。 它们都指向Python内存中的同一对象。 听起来有点熟? 那是因为它与先前的问题相似。 实际上,该帖子实际上应该被称为“可变因素的麻烦”。
列表也会发生同样的事情吗? 是。 那么我们如何解决呢? 好吧,我们必须非常小心。 如果我们确实需要复制列表进行处理,则可以执行以下操作:
b = a [ : ]
这将遍历并将引用复制到列表中的每个项目,并将其放置在新列表中。 但请注意:如果列表中的任何对象都是可变的,我们将再次获得对这些对象的引用,而不是完整的副本。
想象一下在一张纸上有一张清单。 在原始示例中,人员A和人员B看着同一张纸。 如果有人更改了该列表,则两个人都会看到相同的更改。 当我们复制引用时,每个人现在都有自己的列表。 但让我们假设此列表包含寻找食物的地方。 如果“冰箱”在列表中位于第一位,即使复制后,两个列表中的两个条目都指向同一台冰箱。 因此,如果人A修改了冰箱,比如说吃了一个大的奶油蛋糕,那么人物B也将看到奶油蛋糕丢失了。 解决这个问题没有简单的方法。 您只需要记住并以不会引起问题的方式进行编码即可。
Dicts的功能相同,您可以执行以下操作来创建此昂贵的副本:
b = a. copy ( )
同样,这只会创建一个新字典,指向与原始字典中相同的条目。 因此,如果我们有两个相同的列表,并且我们修改了字典'a'中的键所指向的可变对象,则字典'b'中存在的字典对象也将看到这些更改。
可变数据类型的麻烦在于它们功能强大。 以上都不是真正的问题; 他们是牢记防止问题的事情。 在第三项中作为解决方案提出的昂贵的复制操作在99%的时间中都是不必要的。 您的程序可以而且应该进行修改,这样一开始甚至不需要这些副本。
编码愉快! 并随时在评论中提问。
翻译自: https://opensource.com/article/17/6/3-things-i-did-wrong-learning-python
python编码错误