今天给你们总结一些易犯的小错误,让你轻松进行不踩坑的python学习。python
1. 缩进,符号和空格不正确数组
写代码时你们会使用缩进、对齐、空格等,其目的是为了提升代码的可读性。缓存
但在python语言中,许多功能都依赖于缩进。bash
好比在建立一个新类时,该类中的全部内容都在声明下缩进,决策、循环还有其它结构语句也会出现相似的状况,闭包
若是你在代码执行时发现问题,能够查看一下是否使用了正确的缩进。app
来看看下面的例子,在使用IF语句时,请确保使用正确且合适的冒号和缩进,由于它们会致使语法和缩进错误。函数
val = 500if val > 100print("value is grater then 100")File "<ipython-input-1-a271e37c300f>", line 2 if val > 100 ^SyntaxError: invalid syntax复制代码
在上面的代码当中,出现了两处错误:if语句后面的:缺失;下一行没有进行正确的缩进,执行代码出错。
学习
val = 500if val > 100:print("value is grater then 100")value is grater then 100复制代码
当你更正上述代码中的两个问题后,你会发现整段代码可以很好的运行。优化
2. 错误使用类变量ui
class A(object):x = 1class B(A):passclass C(A):passprint( A.x, B.x, C.x)1 1 1复制代码
这里输出的值都是1,而后咱们试着来改变一下A.x和B.x的值看看有什么变化。
B.x = 2print (A.x, B.x, C.x)A.x = 3print (A.x, B.x, C.x)1 2 13 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 = 10def foo(): x += 1print(x)foo()UnboundLocalError Traceback (most recent call last):<ipython-input-26-234e54482865> in <module><ipython-input-26-234e54482865> in foo()UnboundLocalError: local variable x referenced 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):<ipython-input-30-579469eed71a> in <module> <ipython-input-30-579469eed71a> in foo2()UnboundLocalError: local variable lst referenced 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))88888复制代码
为何结果是88888,和我所想的02468不同呢?
这是因为Python的迟绑定(late binding)机制,闭包中内部函数的值只有在被调用时才会进行查询。
所以create_multipliers函数返回的lambda函数被调用时,会在附近的做用域中查询变量i的值,而在create_multipliers生成返回数组以后,整数i的值是4,不会再改变,所以返回数组中每一个匿名函数实际上都是:lambda x: 4*x。、
解决办法是将临时值也保存在匿名函数的做用域内,在声明匿名函数时就查询变量的值。
了解原理以后,让咱们来改一改代码,surprise!
def create_multipliers(): return [lambda x, i=i : i * x for i in range(5)]for multiplier in create_multipliers(): print (multiplier(2))02468复制代码
5. 名称与Python标准库模块发生冲突
Python拥有大量的库模块,开箱即用。可是,若是您遇到一个模块的名称与Python附带的标准库中具备相同名称的模块之间的名称冲突,则可能会出现问题。
例如导入另外一个库,而这个库又会尝试导入模块的Python标准库版本,但因为你有一个同名的模块,另外一个包会错误地导入你的版本而不是Python标准库。
所以,应该注意避免使用与Python标准库模块中相同的名称,而且更改包中的模块名称比提交Python Enhancement Proposal(PEP)以请求名称更改更容易。
6. is和==/=和==
Python中有不少运算符,例如is,=,==这三个,许多刚刚入门的新手会误解这三个运算符的意义和用法,以至于代码出错。
在 Python 中会用到对象之间比较,能够用 ==,也能够用 is,但对对象比较判断的内容并不相同,区别在哪里?
·is 比较两个对象的 id 值是否相等,是否指向同一个内存地址,== 比较的是两个对象的内容是否相等,值是否相等;
a = ["Python"]b = ab is aTrue复制代码
id(a)2222222id(b)2222222b == aTrue复制代码
能够发现上面的例子当中b和a的内存地址是相同的,它们指向同一块内存,于是 is 和 == 的结果都为True,这是由于直接赋值都是赋值的引用。若是新建对象以后,b 和 a 指向了不一样的内存,那么 b is a 的结果为False,而 b==a的结果为True。
·小整数对象[-5,256]在全局解释器范围内被放入缓存供重复使用,例如:
a = 1b = 1a is bTruea == bTrue复制代码
a = 257b = 257a is bFalse复制代码
Python仅仅对比较小的整数对象进行缓存(范围为范围[-5, 256])缓存起来,而并不是是全部整数对象。须要注意的是,这仅仅是在命令行中执行,而在Pycharm或者保存为文件执行,结果是不同的,这是由于解释器作了一部分优化。
=和==的含义不一样:
=表明的含义是赋值,将某一数值赋给某个变量,好比a=3,将3这个数值赋予给a。
==是判断是否相等,返回True或False,好比1==1。他们是相等的,那么就返回true。1==2,他们是不相等的,那么就返回false。
例子:
a = [1,2]b = [1,2]c = aa is bFalsea is ctruea == btrue复制代码
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.nameif __name__== __main__ : b=B( hello ) print (b.getName())复制代码
但若是重写了__init__,实例化子类时,则不会隐式的再去调用超类中已定义的__init__。
class C(A): def __init__(self): pass def getName(self): return C +self.nameif __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.nameif __name__== __main__ : c=C( hello ) print (c.getName())复制代码
源 / DataCastle数据城堡