当前位置: 首页 > 科技观察

学习用Python编程时要避免的3个错误

时间:2023-03-14 17:21:27 科技观察

这些错误可能会产生需要数小时才能解决的棘手问题。当你做错事时承认错误并不容易,但犯错误是任何学习过程的一部分,无论是学习走路还是学习一门新的编程语言,比如学习Python。为了避免新接触Python的程序员犯同样的错误,这里列出了我在学习Python时犯的三个错误。这些要么是我在很长一段时间内经常犯的错误,要么是造成了需要数小时才能解决的问题。年轻的程序员注意了,这些错误可能会浪费一个下午!1.可变数据类型作为函数定义中的默认参数这似乎是正确的?例如,您编写一个小函数来搜索上的当前页面链接,并可选择将其附加到另一个提供的列表。defsearch_for_links(page,add_to=[]):new_links=page.search_for_links()add_to.extend(new_links)returnadd_to这表面上看是很正常的Python代码,实际上是,而且可以运行。但是,这里有一个问题。如果我们为add_to参数提供一个列表,它将按我们预期的那样工作。但是,如果我们让它使用默认值,就会发生神奇的事情。试试这个代码:deffn(var1,var2=[]):var2.append(var1)printvar2fn(3)fn(4)fn(5)也许你认为我们会看到:[3][4][5]但是在事实上,我们看到的是:[3][3,4][3,4,5]为什么?如您所见,每次都使用相同的列表,为什么输出是这样的?在Python中,当我们编写这样一个函数时,这个列表被实例化为函数定义的一部分。函数运行时,并不是每次都实例化。这意味着,该函数将始终使用完全相同的列表对象,除非我们提供一个新的列表对象:fn(3,[4])[4,3]答案正是我们想要的。获得此结果的正确方法是:deffn(var1,var2=None):ifnotvar2:var2=[]var2.append(var1)或在第一个示例中:defsearch_for_links(page,add_to=None):ifnotadd_to:add_to=[]new_links=page.search_for_links()add_to.extend(new_links)returnadd_to这将在加载模块时删除实例化,以便每次运行函数时都会发生列表实例化。请注意,对于不可变数据类型,例如元组、字符串和整数,不需要考虑这种情况。这意味着像下面这样的代码是非常可行的:classURLCatcher(object):urls=[]defadd_url(self,url):self.urls.append(url)2.可变数据类型作为类变量这个和上面提到的最后一个错误非常相似。考虑以下代码:a=URLCatcher()a.add_url('http://www.google.com')b=URLCatcher()b.add_url('http://www.bbc.co.hk??')代码看起来非常好。我们有一个存储URL的对象。当我们调用add_url方法时,它会将给定的URL添加到存储中。看起来很正确,对吧?让我们看看它实际上是什么样子:a=URLCatcher()a.add_url('http://www.google.com')b=URLCatcher()b.add_url('http://www.bbc.co.hk')b.urls:['http://www.google.com','http://www.bbc.co.uk']a.urls:['http://www.google.com','http://www.bbc.co.uk']等等,这是怎么回事?!我们不这么认为。我们实例化两个单独的对象a和b。一个URL给a,另一个给b。这两个对象怎么会有这两个URL呢?这是与第一个错误示例相同的问题。创建类定义时会实例化URL列表。此类的所有实例都使用相同的列表。有时这很有用,但大多数时候您并不想这样做。您希望每个对象都有一个单独的存储区。为此,我们将代码修改为:classURLCatcher(object):def__init__(self):self.urls=[]defadd_url(self,url):self.urls.append(url)URL列表是实例化的变化。当我们实例化两个单独的对象时,它们将各自使用两个单独的列表。3.变量分配错误这个问题困扰了我一段时间。让我们进行一些更改并使用另一种可变数据类型——字典。a={'1':"one",'2':'two'}现在,假设我们想在别处使用这个字典并保持其原始数据不变。b=ab['3']='三'容易吗?现在,让我们看看我们不想更改的原始字典a:{'1':"one",'2':'two','3':'three'}等等,让我们看看b?{'1':"one",'2':'two','3':'three'}等等,什么?有点乱……让我们退后一步,看看在这种情况下其他不可变类型会发生什么,例如元组:c=(2,3)d=cd=(4,5)现在c是(2,3),d为(4,5)。这个函数的结果和我们预期的一样。那么,前面的例子到底发生了什么?当使用可变类型时,它的行为有点像C中的指针。在上面的代码中,我们设置b=a,我们真正的意思是:b成为对a的引用。它们都指向Python内存中的同一个对象。听起来有点熟?那是因为这个问题和上一个问题很相似。实际上,这篇文章应该被称为“TheTroubleWithMutables”。列表也会发生同样的事情吗?是的。那么我们该如何解决呢?这必须非常小心地完成。如果我们确实需要复制一个列表进行处理,我们可以这样做:b=a[:]这将遍历并复制对列表中每个对象的引用,并将其放入新列表中。但要注意:如果列表中的每个对象都是可变的,我们将再次获得对它们的引用,而不是完整的副本。假设你在一张纸上列了一张清单。在最初的例子中,A和B正在看同一张纸。如果一个人修改了列表,那么两个人都会看到相同的更改。当我们复制引文时,每个人现在都有自己的列表。但是,我们假设此列表包括寻找食物的地方。如果“冰箱”是列表中的第一个,则两个列表中的条目都指向同一台冰箱,即使它是重复的。所以,如果冰箱被A改装,吃了里面的大蛋糕,B也会看到这块蛋糕的消失。这里没有简单的方法来修复它。只要您牢记这一点并以不会导致此问题的方式编写代码。字典的工作方式相同,您可以通过执行以下操作创建昂贵的副本:b=a.copy()同样,这只是创建一个新字典,指向最初存在的相同条目。因此,如果我们有两个相同的列表,并且我们修改了字典a的键指向的可变对象,那么这些更改也会在字典b中看到。可变数据类型的问题也是它们的优势。以上都不是真正的问题;他们是需要注意的问题。在99%的情况下,在第三个项目中使用昂贵的复制操作作为解决方案是不必要的。您的程序可能应该更改,以便在第一个示例中甚至不需要这些副本。编程愉快!欢迎在评论中提问。关于作者:PeteSavage-Peter是一位热情的开源爱好者,在过去十年中一直在推广和使用开源产品。他从Ubuntu社区开始,自愿在许多不同领域从事音频制作领域的研究工作。在职业经历方面,他最初担任公司的系统管理员,主要负责管理和设置数据中心,然后在RedHat担任CloudForms产品的首席测试工程师。