在学习python的过程中,相信大家都有踩过不少的坑,有些坑可能踩了不止一次,感觉就像是在坑与坑之间反复横跳。那么如何避免这些坑呢?看完这篇文章,你就知道了。我们来谈谈我们学习python的过程中,最常见的七大坑: 1. 缩进,符号和空格不正确 写代码时大家会使用缩进、对齐、空格等,这些是为了提高代码的可读性 在python语言中,缩进是十分重要的 比如在创建一个新类时,该类中的所有内容都在声明下缩进,决策、循环还有其它结构语句也会出现类似的情况, 代码执行时如果存在问题,可以先查看一下缩进是否正确。 这边有个例子,在使用IF语句时,请确保使用正确且合适的冒号和缩进,因为它们会导致语法和缩进错误。 val = 500 if val > 100 print("value is grater then 100") File "", line 2 if val > 100 ^ SyntaxError: invalid syntax 在上面的代码当中,出现了两处错误:if语句后面的:缺失;下一行没有进行正确的缩进,执行代码出错。 val = 500 if val > 100: print("value is grater then 100") value is grater then 100 更正这两个问题之后,代码可以很好的运行 2. 变量使用不正确 class A(object):x = 1 class B(A):pass class C(A):pass print( A.x, B.x, C.x) 1 1 1 这里输出的值都是1,然后我们试着来改变一下A.x和B.x的值看看有什么变化。 B.x = 2 print (A.x, B.x, C.x) A.x = 3 print (A.x, B.x, C.x) 1 2 1 3 2 3 我们只改变了A.x,为什么C.x改变呢? 这里需要了解到python的命名空间。 python中,命名空间是名字到对象映射的结合,不同命名空间中的名字是没有关联的,这种映射的实现有点类似于python中的字典。 当你名字访问一个对象的属性时,先从对象的命名空间寻找。如果找到了这个属性,就返回这个属性的值;如果没有找到的话,则从类的命名空间中寻找,找到了就返回这个属性的值,找不到则抛出异常。 在Python中,类变量在内部作为字典处理,并遵循通常称为方法解析顺序(MRO)的方法。 MRO:Method Resolution Order方法解析顺序,Python支持多继承,该方法用于解决父类存在同名函数的时存在的二义性问题。 因此在上面的代码中,由于x在对象的命名空间中找不到该属性C,因此将在类中查找它。换句话说,C没有自己的x属性,独立于A。因此,引用C.x实际上是指A.x。 3.不了解python范围规则 倘若不知道python的范围规则,那么犯错误的可能性会大大增加,这是因为Python使用一种独有的范围规则来确定变量范围。 python范围解析是基于LEGB规则,以下是Python范围规则的概述: ·L -代表Local。它包含在函数内指定的(标识符/变量)名称(使用def或lambda),而不是使用global关键字声明。 ·E -代表Enclosing function locals。它包含来自任何/所有封闭函数的本地范围的名称(例如,使用def或lambda)。 ·G -指全球实体。它包括在模块文件的顶层运行或使用global关键字定义的名称。 ·B -指内置插件。它跨越预先指定为内置名称的名称,如打印,输入,打开等。 LEGB规则指定名称空间的以下顺序,用于搜索名称: Local - > Enclosed - > Global - > Built-in 考虑以下的例子: x = 10 def foo(): x += 1 print(x) foo() UnboundLocalError Traceback (most recent call last): in in foo() UnboundLocalError: local variable xreferenced before assignment 发生上述错误的原因是,对作用域中的变量进行赋值时,Python会自动将该变量视为该作用域的本地变量,并在外部作用域中隐藏任何类似命名的变量。 因此,许多人在代码提示出错并显示需要在函数中添加赋值语句而感到不解。 考虑一个在使用列表时遇到的例子: lst = [1, 2, 3] def foo1(): lst.append(5) foo1() lst [1, 2, 3, 5] lst = [1, 2, 3] def foo2(): lst += [5] foo2() UnboundLocalError Traceback (most recent call last): in in foo2() UnboundLocalError: local variable lstreferenced before assignment 为什么foo2出错了但是foo1运行良好? 答案在前面就已经有所提示,在这个例子当中foo1()做一个分配到lst,而在foo2()当中lst += [5]其实只是lst = lst + [5]的简写,我们希望分配一个值给lst,但是分配的值lst是基于lst自身,但其尚未定义。 4. python闭包变量绑定 python的闭包变量问题也是新手们容易混淆的一个点,来看看下面的例子: def create_multipliers(): return [lambda x : i * x for i in range(5)] for multiplier in create_multipliers(): print (multiplier(2)) 8 8 8 8 8 为什么结果是88888,和我所想的02468不一样呢? 这是由于Python的迟绑定(late binding)机制,闭包中内部函数的值只有在被调用时才会进行查询。 因此create_multipliers函数返回的lambda函数被调用时,会在附近的作用域中查询变量i的值,而在create_multipliers生成返回数组之后,整数i的值是4,不会再改变,因此返回数组中每个匿名函数实际上都是:lambda x: 4x。、 解决办法是将临时值也保存在匿名函数的作用域内,在声明匿名函数时就查询变量的值。 了解原理之后,让我们来改一改代码,surprise! def create_multipliers(): return [lambda x, i=i : i * x for i in range(5)] for multiplier in create_multipliers(): print (multiplier(2)) 0 2 4 6 8 *5. 名称与Python标准库模块发生冲突** Python拥有大量的库模块,开箱即用。但是,如果您遇到一个模块的名称与Python附带的标准库中具有相同名称的模块之间的名称冲突,则可能会出现问题。 例如导入另一个库,而这个库又会尝试导入模块的Python标准库版本,但由于你有一个同名的模块,另一个包会错误地导入你的版本而不是Python标准库。 因此,应该注意避免使用与Python标准库模块中相同的名称,并且更改包中的模块名称比提交Python Enhancement Proposal(PEP)以请求名称更改更容易。 6.is和==/=和== Python中有很多运算符,例如is,=,==这三个,许多刚刚入门的新手会误解这三个运算符的意义和用法,以致于代码出错。 在 Python 中会用到对象之间比较,可以用 ==,也可以用 is,但对对象比较判断的内容并不相同,区别在哪里? ·is 比较两个对象的 id 值是否相等,是否指向同一个内存地址,== 比较的是两个对象的内容是否相等,值是否相等; a = ["Python"] b = a b is a True id(a) 2222222 id(b) 2222222 b == a True 可以发现上面的例子当中b和a的内存地址是相同的,它们指向同一块内存,因而 is 和 == 的结果都为True,这是因为直接赋值都是赋值的引用。如果新建对象之后,b 和 a 指向了不同的内存,那么 b is a 的结果为False,而 b==a的结果为True。 ·小整数对象[-5,256]在全局解释器范围内被放入缓存供重复使用,例如: a = 1 b = 1 a is b True a == b True a = 257 b = 257 a is b False Python仅仅对比较小的整数对象进行缓存(范围为范围[-5, 256])缓存起来,而并非是所有整数对象。需要注意的是,这仅仅是在命令行中执行,而在Pycharm或者保存为文件执行,结果是不一样的,这是因为解释器做了一部分优化。 =和==的含义不同: =代表的含义是赋值,将某一数值赋给某个变量,比如a=3,将3这个数值赋予给a。 ==是判断是否相等,返回True或False,比如1==1。他们是相等的,那么就返回true。1==2,他们是不相等的,那么就返回false。 例子: a = [1,2] b = [1,2] c = a a is b False a is c true a == b true 7. 滥用init init方法在Python中用作构造函数,当Python将内存分配给新的类对象时,它会自动被调用。 首先,init并不相当于C#中的构造函数,在执行它的时候,实例已经构造出来。 class A(object): def init(self,name): self.name=name def getName(self): return A+self.name 执行代码: a=A(hello) 可以理解为: a=object.new(A) A.init(a,hello) 即init作用是初始化已实例化后的对象。 其次,子类可以不重写init,实例化子类时,会自动调用超类中已定义的init。 class B(A): def getName(self): return B+self.name if name==main: b=B(hello) print (b.getName()) 但如果重写了init,实例化子类时,则不会隐式的再去调用超类中已定义的init。 class C(A): def init(self): pass def getName(self): return C+self.name if name==main: c=C() print (c.getName()) 此时执行代码则会报"AttributeError: C object has noattribute name ”错误,所以如果重写了init,为了能使用或扩展超类中的行为,最好显式的调用超类的init方法。 class C(A): def init(self,name): super(C,self).init(name) def getName(self): return C+self.name if name==main: c=C(hello) print (c.getName()) 老天爷都给我点赞了,你还不点?觉得这篇文章对你有所帮助的话可以来个一键三连呀!!若有指正的话可以评论区见或者私我呀。
这些常见的坑,90%的程序猿都踩过,来看看里面有没有你的脚印?
点赞
收藏