网站性能检测评分
注:本网站页面html检测工具扫描网站中存在的基本问题,仅供参考。
python中用法
一步一步带你理解 Python 中的浅复制与深复制 营销视频课程
Python 中的赋值语句不会对对象进行拷贝,仅仅是将变量名指向对象。对于不可修改的对象来说,这种机制不会影响我们日常的使用。但是,对于可修改的对象,你偶尔可能需要对该对象做一个真正的复制。何为真正的复制?就是修改拷贝来的对象不会影响原来的对象。
Python 中内置的可修改的集合类对象,比如列表、字典、集合等,可以直接使用对应的工厂方法进行拷贝。
需要注意的是,对于复合类型的对象,比如列表、字典、集合等,复制有浅复制与深复制两种类型。
浅复制意味着新建一个对象,但是其子元素仍然指向的对应原对象的子对象。也就是说,这只会对原对象进行一层的拷贝,而不会递归的对子对象也进行拷贝。
深复制则会递归的对子对象进行拷贝。
如果上面的看不懂,没关系,我们通过一个个例子来搞清楚。
浅复制
现在下面这个列表,我们使用 list() 方法对它进行复制
这意味着 xs 和 ys 是两个不同的对象。我们可以看看它们的值
为了证明 xs 与 ys 确实不同,我们可以做个小实验。添加一个子列表到 xs 中,然后看会不会影响 ys 。
与预期相同,如果只是修改原对象这一层,确实不对对 ys 造成影响。
但是由于是浅复制,ys 中的子元素并非是 xs 子元素的拷贝,仅仅是 xs 子对象的引用。因此,如果你修改 xs 中的子元素,ys 中的子元素同样会被修改。
看起来我们只是修改了 xs 中的子元素,实则 ys 中的子元素也被修改了,这是由于浅复制的缘故。
现在我们大概清楚了浅复制与深复制的区别,但是还有两个问题待解决:
一是,对于内置的集合类对象,我们怎么进行深复制?
二是,对于任意的类,又该如何进行浅复制与深复制?
答案是标准库中的 copy 模块,其提供了简单的方法对对象进行浅复制或深复制。
深复制
这次我们仍然使用上面的例子,不同的是,我们使用 copy 模块中的 deepcopy() 方法来进行复制。
还是先看看各自的值
这次,因为 zs 对 xs 进行了深复制,即不仅拷贝了 xs 本身,还对它的子对象都进行了递归的拷贝,所以,如果我们再次修改 xs 的子元素时,并不会对 zs 造成影响。我们来试一试
果然,与预期一致。
顺便说下,使用 copy 模块中的 copy() 方法可以对对象进行浅复制。平时开发需要浅复制的时候,你可以使用该方法,但如果碰到内置的集合类对象,比如列表、字典、集合等的时候,使用对应的工厂方法如 list() ,dict() ,set() 等更 Pythonic 一些。
对任意对象进行复制
现在有一个点类 Point ,如下
其中,__repr__ 方法可以让我们更方便的查看对象的信息。需要注意的是,f'...' 这种格式化字符串的方式在 Python 3.6 才支持,如果是 Python 2 或者 3.6 以前的版本可以使用 '...' % (v1[, v2, ...]) 的方式。
现在我们创建一个点对象并进行浅复制
查看两个对象的信息
和预期一致。
需要注意的是,因为类的两个成员 x 和 y 都是简单类型,这里是整型,所以这里浅复制与深复制没有任何差别。后面我会扩展这个例子。
现在我要定义一个矩形类,其类成员将会使用到上面的点类。
首先我们先试一下浅复制
看下各自的信息
记得我们上面修改列表的浅复制与深复制的例子吗?这里,我要类似的实验。我们修改 rect 的成员,不出意外的话,srect 也会改变。
果然。
下面,我会进行深复制,然后再类似的修改
这次,深复制出来的 drect 对象与 srect 才能说是“真正不同”的对象。
copy 模块中还有其它很多用法,比如定义对象的 __copy__() 和 __deepcopy__() 方法可以自定义对象的浅复制与深复制行为等。这不是本文的重心,有兴趣的可以去看相应的文档 https://docs.python.org/3/library/copy.html 。
小结
* 浅复制不会克隆子对象,所以,复制出来的对象和原对象并非完全不想关。
* 深复制会递归的克隆子对象,所以,复制出来的对象和原对象完全不相关,但是深复制比浅复制会慢一些。
* 使用 copy 模块你可以复制任何类,不管是浅复制还是深复制。
巩固
从网上找了组图片(侵删),让大家加深下认识
首先是赋值
然后是浅复制
最后是深复制
Python程序员最常犯的10个错误,你中招了吗? 公司视频课程
大数据文摘作品
编译:什锦甜、Gao Ning、小鱼
Python简介
Python是一种具有动态语义的、面向对象的解释型高级编程语言。因其内置了高级数据结构,并支持动态类型和动态绑定,使用Python进行快速应用程序开发十分便利。同时作为一门脚本语言,它兼容部分现有的组件和服务。Python还支持模块和各种库的扩展,有助于实现模块化编程和提高代码复用率。
关于本文
刚接触这门语言的新手可能会对Python简洁灵活的语法有些不适应,或是低估了Python强大的性能。鉴于此,本文列出了Python开发人员常犯的10个小错误,资深程序猿也难免会中招哦。
本文供Python高级开发人员参考,Python小白可以参考下面这篇文章:
http://onlamp/pub/a/python/2004/02/05/learn_python.html
常见错误1:滥用表达式作为函数参数的默认值
Python允许开发者指定函数参数的默认值,这也是Python的一大特色,但当默认值可变时,可能会给开发者带来一些困扰。例如下面定义的函数:
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified... bar.append("baz") # but this line could be problematic, as we'll see...... return bar
看出bug了吗?那就是在每次调用函数前没有对可变参数进行赋值,而认为该参数就是默认值。比如上面的代码,有人可能期望在反复调用foo()时返回'baz',以为每次调用foo()时,bar的值都为[],即一个空列表。
但是,让我们来看看代码运行结果:
>>> foo()["baz"]>>> foo()["baz", "baz"]>>> foo()["baz", "baz", "baz"]
嗯?为什么每次调用foo()后会不断把"baz"添加到已有的列表,而不是新建一个新列表呢?答案就是,函数参数的默认值仅在定义函数时执行一次。因此,仅在第一次定义foo()时,bar初始化为默认值(即空列表),此后,每次调用foo()函数时,参数bar都是第一次初始化时生成的列表。
常见的解决方案:
>>> def foo(bar=None):... if bar is None: # or if not bar:... bar = []... bar.append("baz")... return bar...>>> foo()["baz"]>>> foo()["baz"]>>>foo()["baz"]
常见错误2:错误地使用类变量
代码示例:
>>> class A(object):... x = 1...>>> class B(A):... pass...>>> class C(A):... pass...>>> print A.x, B.x, C.x1 1 1
运行结果没问题。
>>> B.x = 2>>> print A.x, B.x, C.x1 2 1
结果也正确。
>>> A.x = 3>>> print A.x, B.x, C.x3 2 3
什么鬼?我们只改变了A.x.,为什么C.x 也变了?
在Python中,类变量是以字典形式进行内部处理,遵循方法解析顺序(Method Resolution Order ,MRO)。因此,在上述代码中,因为在类C中没有找到属性x,它就会从父类中查找x的值(尽管Python支持多重继承,但上述代码只存在一个父类A)。换句话说,C没有独立于类A的属于自己的x。因此,C.x实际上指的是A.x。除非处理得当,否则就会导致Python出现错误。
如果想更深入了解Python的类特性,请戳:
https://toptal/python/python-class-attributes-an-overly-thorough-guide
常见错误3:错误指定异常代码块的参数
假设你有如下代码:
>>> try:... l = ["a", "b"]... int(l[2])... except ValueError, IndexError: # To catch both exceptions, right?... pass...Traceback (most recent call last):File "
这里的问题是except语句不接受以这种方式指定的异常列表。在Python2.x中,except Exception语句中变量e可用来把异常信息绑定到第二个可选参数上,以便进一步查看异常的情况。因此,在上述代码中,except语句并没有捕捉到IndexError异常;而是将出现的异常绑定到了参数IndexError中。
想在一个except语句同时捕捉到多个异常的正确方式是,将第一个参数指定为元组,并将要捕捉的异常类型都写入该元组中。为了方便起见,可以使用as关键字,Python 2 和Python 3都支持这种语法格式:
>>> try:... l = ["a", "b"]... int(l[2])... except (ValueError, IndexError) as e: ... pass...>>>
常见错误4:错误理解Python中变量的作用域
Python变量作用域遵循LEGB规则,LEGB是Local,Enclosing,Global,Builtin的缩写,分别代表本地作用域、封闭作用域、全局作用域和内置作用域,这个规则看起来一目了然。事实上,Python的这种工作方式较为独特,会导致一些编程错误,例如:
>>> x = 10>>> def foo():... x += 1... print x...>>> foo()Traceback (most recent call last):File "
问题出在哪?
上面的错误是因为在作用域内对变量赋值时,Python自动将该变量视为该作用域的本地变量,并对外部定义的同名变量进行了屏蔽。因此,原本正确的代码,在某个函数内部添加了一个赋值语句后,却意外收到了UnboundLocalError的报错信息。
关于UnboundLocalError更多内容请戳:
https://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
在使用列表时,Python程序员更容易掉入此类陷阱,例如:
>>> lst = [1, 2, 3]>>> def foo1():... lst.append(5) # This works ok......>>> foo1()>>> lst[1, 2, 3, 5]>>> lst = [1, 2, 3]>>> def foo2():... lst += [5] # ... but this bombs!...>>> foo2()Traceback (most recent call last):File "
奇怪,为什么foo1正常运行,而foo2崩溃了呢?
原因和上一个案例中出现的问题相似,但这里的错误更加细微。函数foo1没有对变量lst进行赋值操作,而函数foo2有赋值操作。
首先, lst += [5]是lst = lst + [5]的缩写形式,在函数foo2中试图对变量lst进行赋值操作(Python将变量lst默认为本地作用域的变量)。但是,lst += [5]语句是对lst变量自身进行的赋值操作(此时变量lst的作用域是函数foo2),但是在函数foo2中还未声明该变量,所以就报错啦!
常见错误5:在遍历列表时修改列表
下面代码中的错误很明显:
>>> odd = lambda x : bool(x % 2)>>> numbers = [n for n in range(10)]>>> for i in range(len(numbers)):... if odd(numbers[i]):... del numbers[i] # BAD: Deleting item from a list while iterating over it...Traceback (most recent call last):File "
有经验的程序员都知道,在Python中遍历列表或数组时不应该删除该列表(数组)中的元素。虽然上面代码的错误很明显,但是在编写复杂代码时,资深程序员也难免会犯此类错误。
幸好Python集成了大量经典的编程范式,如果运用得当,可以大大简化代码并提高编程效率。简单的代码会降低出现上述bug的几率。列表解析式(list comprehensions)就是利器之一,它将完美避开上述bug,解决方案如下:
>>> odd = lambda x : bool(x % 2)>>> numbers = [n for n in range(10)]>>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all>>> numbers[0, 2, 4, 6, 8]
更多有关列表解析式的详细内容,请戳:https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps
常见错误6:不理解Python闭包中的变量绑定
代码示例:
>>> def create_multipliers():... return [lambda x : i * x for i in range(5)]>>> for multiplier in create_multipliers():... print multiplier(2)...
你以为运行结果会是:
02468
但实际输出结果是:8
8888
惊不惊喜!
这种情况是由于Python延迟绑定(late binding)机制造成的,也就是说只有在内部函数被调用时才会搜索闭包中变量的值。所以在上述代码中,每次调用create_multipliers()函数中的return函数时,会在附近作用域中查询变量i的值。(此时,return中循环已结束,所以i值为4)。
常见解决方案:
>>> def create_multipliers():... return [lambda x, i=i : i * x for i in range(5)]...>>> for multiplier in create_multipliers():... print multiplier(2)...02468
没错!我们利用了匿名函数lambda的默认参数来生成结果序列。有人觉得这种用法很简洁,有人会说它很巧妙,还有人会觉得晦涩难懂。如果你是Python开发人员,那么深刻理解上述语法对你而言非常重要。
常见错误7:模块之间出现循环依赖
假设你有两个文件,分别是a.py和b.py,两者相互导入,如下所示:
a.py模块中的代码:
import bdef f():return b.xprint f()
b.py模块中的代码:
import ax = 1def g():print a.f()
首先,我们尝试导入a.py:
>>> import a1
运行结果正确!这似乎有点出人意料,因为我们在这里进行循环导入,应该会报错呀!
答案是,在Python中如果仅存在一个循环导入,程序不会报错。如果一个模块已经被导入,Python会自动识别而不会再次导入。但是如果每个模块试图访问其他模块不同位置的函数或变量时,那么Error又双叒叕出现了。
回到上面的示例中,当导入a.py模块时,程序可以正常导入b.py模块,因为此时b.py模块未访问a.py中定义任何的变量或函数。b.py模块仅引用了a.py模中的a.f()函数。调用的a.f()函数隶属于g()函数,而a.py或b.py模块中并没有调用g()函数。所以程序没有报错。
但是,如果我们在未导入a.py模块之前先导入b.py模块,结果会怎样?
>>> import bTraceback (most recent call last):File "
报错了!问题在于,在导入b.py的过程中,它试图导入a.py模块,而a.py模块会调用f()函数,f()函数又试图访问b.x变量。但此时,还未对变量b.x进行定义,所以出现了AttributeError异常。
稍微修改下b.py,即在g()函数内部导入a.py就可以解决上述问题。
修改后的b.py:
x = 1def g():
import a # This will be evaluated only when g() is calledprint a.f()
现在我们再导入b.py模块,就不会报错啦!
>>> import b>>> b.g()1 # Printed a first time since module 'a' calls 'print f()' at the end1 # Printed a second time, this one is our call to 'g'
常见错误8:文件命名与Python标准库模块的名称冲突
Python的优势之一就是其集成了丰富的标准库。正因为如此,稍不留神就会在为自己的文件命名时与Python自带标准库模块重名。例如,如果你的代码中有一个名为email.py的模块,恰好就和Python标准库中email.py模块重名了。)
上述问题比较复杂。举个例子,在导入模块A的时候,假如该模块A试图导入Python标准库中的模块B,但你已经定义了一个同名模块B,模块A会错误导入你自定义的模块B,而不是Python标准库中的模块B。这种错误很糟糕,因为程序员很难察觉到是因为命名冲突而导致的。
因此,Python程序员要注意避免与Python标准库模块的命名冲突。毕竟,修改自己模块的名称比修改标准库的名称要容易的多!当然你也可以写一份Python改善建议书(Python Enhancement Proposal,PEP)提议修改标准库的名称。
常见错误9:不熟悉Python2和Python3之间的差异
先来看看foo.py文件中的代码:
import sysdef bar(i):if i == 1: raise KeyError(1) if i == 2: raise ValueError(2)def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e)bad()
在Python 2中,上述代码运行正常
$ python foo.py 1key error1$ python foo.py 2value error2
但是在Python 3中运行时:
$ python3 foo.py 1key errorTraceback (most recent call last):File "foo.py", line 19, in
什么情况?原来,在Python 3中,在except代码块作用域外无法访问异常对象。(原因是,Python 3会将内存堆栈中的循环引用进行保留,直到垃圾回收...
Python迭代器的经典用法 营销视频课程
迭达是Python中最强有力的特性之一,这里介绍一些迭代的常用用法,希望对大家编程有帮助。
一.迭代列表的排列
使用itertools.permutations()函数
迭代所有排列
迭代部分排列
二、迭代列表的组合
使用itertoolsbinations()函数
三、以索引-值的形式迭代列表
使用enumerate()函数
四、同时迭代多个序列
使用zip()函数实现
zip()函数还可以用于组合字典
五、将不同的列表和集合组合起来一起迭代
使用itertools.chain()函数,将列表和集合组装想来,看上去就像是在一个容器中操作
六、合并多个有序序列,再对整个有序序列进行迭代
使用heapq.merge()函数,该函数对所有提供的序列不会一次性读取,可以处理非常长的序列,开销却很小.
请注意,heapq.meage()会自动排序,这对我们做程序开发非常有用。
heapq.merge()用来处理有序文件重排序是非常理想的:
与大家探讨一下python中turtle(海龟)模块的简单用法 营销视频课程
在执行turtle模块之前。要确保自己的python中已经安装了Tkinter模块。
turtle模块可以用来学习计算机是如何在屏幕上画图的。
提供了画向量图的方法
#!/usr/bin/python
#coding: UTF-8
import turtle
import time
# 调用turtle中的Pen函数创建画布
t = turtle.Pen()
# 画矩形
for i in range(0, 4):
# 往前画一条直线
t.forward(100)
# 左转弯90度
t.left(90)
time.sleep(1)
#time.sleep(3)
# 清空画布并把海龟放在起始位置
t.reset()
# 画两条相互平行的直线
# 往后画一条直线
t.backward(100)
# 拿起画笔,不再作画,只有遇见down函数的时候才可以继续作画
t.up()
# 右转90度
t.right(90)
# 往前移动20个像素
t.forward(20)
# 左转90度,指向和上一条线平行的方向
t.left(90)
# 放下画笔,开始作画
t.down()
# 画另一条平行线
t.forward(100)
time.sleep(3)
t.reset()
#画等边三角形
t.forward(100)
t.left(120)
t.forward(100)
t.right(60)
t.backward(100)
time.sleep(3)
# 只清空画布,海龟仍然停留在当前的位置
#注意此时箭头所在的位置,注意与reset执行时的区别
t.clear()
下图为执行上述代码以后的结果
有不同的理解或者方法,望不吝赐教,感激不尽!!!!
欢迎大家相互交流讨论
Python程序员最常犯的10个错误,你中招了吗? 营销视频课程
大数据文摘作品
编译:什锦甜、Gao Ning、小鱼
Python简介
Python是一种具有动态语义的、面向对象的解释型高级编程语言。因其内置了高级数据结构,并支持动态类型和动态绑定,使用Python进行快速应用程序开发十分便利。同时作为一门脚本语言,它兼容部分现有的组件和服务。Python还支持模块和各种库的扩展,有助于实现模块化编程和提高代码复用率。
关于本文
刚接触这门语言的新手可能会对Python简洁灵活的语法有些不适应,或是低估了Python强大的性能。鉴于此,本文列出了Python开发人员常犯的10个小错误,资深程序猿也难免会中招哦。
本文供Python高级开发人员参考,Python小白可以参考下面这篇文章:
http://onlamp/pub/a/python/2004/02/05/learn_python.html
常见错误1:滥用表达式作为函数参数的默认值
Python允许开发者指定函数参数的默认值,这也是Python的一大特色,但当默认值可变时,可能会给开发者带来一些困扰。例如下面定义的函数:
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified... bar.append("baz") # but this line could be problematic, as we'll see...... return bar
看出bug了吗?那就是在每次调用函数前没有对可变参数进行赋值,而认为该参数就是默认值。比如上面的代码,有人可能期望在反复调用foo()时返回'baz',以为每次调用foo()时,bar的值都为[],即一个空列表。
但是,让我们来看看代码运行结果:
>>> foo()["baz"]>>> foo()["baz", "baz"]>>> foo()["baz", "baz", "baz"]
嗯?为什么每次调用foo()后会不断把"baz"添加到已有的列表,而不是新建一个新列表呢?答案就是,函数参数的默认值仅在定义函数时执行一次。因此,仅在第一次定义foo()时,bar初始化为默认值(即空列表),此后,每次调用foo()函数时,参数bar都是第一次初始化时生成的列表。
常见的解决方案:
>>> def foo(bar=None):... if bar is None: # or if not bar:... bar = []... bar.append("baz")... return bar...>>> foo()["baz"]>>> foo()["baz"]>>>foo()["baz"]
常见错误2:错误地使用类变量
代码示例:
>>> class A(object):... x = 1...>>> class B(A):... pass...>>> class C(A):... pass...>>> print A.x, B.x, C.x1 1 1
运行结果没问题。
>>> B.x = 2>>> print A.x, B.x, C.x1 2 1
结果也正确。
>>> A.x = 3>>> print A.x, B.x, C.x3 2 3
什么鬼?我们只改变了A.x.,为什么C.x 也变了?
在Python中,类变量是以字典形式进行内部处理,遵循方法解析顺序(Method Resolution Order ,MRO)。因此,在上述代码中,因为在类C中没有找到属性x,它就会从父类中查找x的值(尽管Python支持多重继承,但上述代码只存在一个父类A)。换句话说,C没有独立于类A的属于自己的x。因此,C.x实际上指的是A.x。除非处理得当,否则就会导致Python出现错误。
如果想更深入了解Python的类特性,请戳:
https://toptal/python/python-class-attributes-an-overly-thorough-guide
常见错误3:错误指定异常代码块的参数
假设你有如下代码:
>>> try:... l = ["a", "b"]... int(l[2])... except ValueError, IndexError: # To catch both exceptions, right?... pass...Traceback (most recent call last):File "
这里的问题是except语句不接受以这种方式指定的异常列表。在Python2.x中,except Exception语句中变量e可用来把异常信息绑定到第二个可选参数上,以便进一步查看异常的情况。因此,在上述代码中,except语句并没有捕捉到IndexError异常;而是将出现的异常绑定到了参数IndexError中。
想在一个except语句同时捕捉到多个异常的正确方式是,将第一个参数指定为元组,并将要捕捉的异常类型都写入该元组中。为了方便起见,可以使用as关键字,Python 2 和Python 3都支持这种语法格式:
>>> try:... l = ["a", "b"]... int(l[2])... except (ValueError, IndexError) as e: ... pass...>>>
常见错误4:错误理解Python中变量的作用域
Python变量作用域遵循LEGB规则,LEGB是Local,Enclosing,Global,Builtin的缩写,分别代表本地作用域、封闭作用域、全局作用域和内置作用域,这个规则看起来一目了然。事实上,Python的这种工作方式较为独特,会导致一些编程错误,例如:
>>> x = 10>>> def foo():... x += 1... print x...>>> foo()Traceback (most recent call last):File "
问题出在哪?
上面的错误是因为在作用域内对变量赋值时,Python自动将该变量视为该作用域的本地变量,并对外部定义的同名变量进行了屏蔽。因此,原本正确的代码,在某个函数内部添加了一个赋值语句后,却意外收到了UnboundLocalError的报错信息。
关于UnboundLocalError更多内容请戳:
https://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
在使用列表时,Python程序员更容易掉入此类陷阱,例如:
>>> lst = [1, 2, 3]>>> def foo1():... lst.append(5) # This works ok......>>> foo1()>>> lst[1, 2, 3, 5]>>> lst = [1, 2, 3]>>> def foo2():... lst += [5] # ... but this bombs!...>>> foo2()Traceback (most recent call last):File "
奇怪,为什么foo1正常运行,而foo2崩溃了呢?
原因和上一个案例中出现的问题相似,但这里的错误更加细微。函数foo1没有对变量lst进行赋值操作,而函数foo2有赋值操作。
首先, lst += [5]是lst = lst + [5]的缩写形式,在函数foo2中试图对变量lst进行赋值操作(Python将变量lst默认为本地作用域的变量)。但是,lst += [5]语句是对lst变量自身进行的赋值操作(此时变量lst的作用域是函数foo2),但是在函数foo2中还未声明该变量,所以就报错啦!
常见错误5:在遍历列表时修改列表
下面代码中的错误很明显:
>>> odd = lambda x : bool(x % 2)>>> numbers = [n for n in range(10)]>>> for i in range(len(numbers)):... if odd(numbers[i]):... del numbers[i] # BAD: Deleting item from a list while iterating over it...Traceback (most recent call last):File "
有经验的程序员都知道,在Python中遍历列表或数组时不应该删除该列表(数组)中的元素。虽然上面代码的错误很明显,但是在编写复杂代码时,资深程序员也难免会犯此类错误。
幸好Python集成了大量经典的编程范式,如果运用得当,可以大大简化代码并提高编程效率。简单的代码会降低出现上述bug的几率。列表解析式(list comprehensions)就是利器之一,它将完美避开上述bug,解决方案如下:
>>> odd = lambda x : bool(x % 2)>>> numbers = [n for n in range(10)]>>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all>>> numbers[0, 2, 4, 6, 8]
更多有关列表解析式的详细内容,请戳:https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps
常见错误6:不理解Python闭包中的变量绑定
代码示例:
>>> def create_multipliers():... return [lambda x : i * x for i in range(5)]>>> for multiplier in create_multipliers():... print multiplier(2)...
你以为运行结果会是:
02468
但实际输出结果是:8
8888
惊不惊喜!
这种情况是由于Python延迟绑定(late binding)机制造成的,也就是说只有在内部函数被调用时才会搜索闭包中变量的值。所以在上述代码中,每次调用create_multipliers()函数中的return函数时,会在附近作用域中查询变量i的值。(此时,return中循环已结束,所以i值为4)。
常见解决方案:
>>> def create_multipliers():... return [lambda x, i=i : i * x for i in range(5)]...>>> for multiplier in create_multipliers():... print multiplier(2)...02468
没错!我们利用了匿名函数lambda的默认参数来生成结果序列。有人觉得这种用法很简洁,有人会说它很巧妙,还有人会觉得晦涩难懂。如果你是Python开发人员,那么深刻理解上述语法对你而言非常重要。
常见错误7:模块之间出现循环依赖
假设你有两个文件,分别是a.py和b.py,两者相互导入,如下所示:
a.py模块中的代码:
import bdef f():return b.xprint f()
b.py模块中的代码:
import ax = 1def g():print a.f()
首先,我们尝试导入a.py:
>>> import a1
运行结果正确!这似乎有点出人意料,因为我们在这里进行循环导入,应该会报错呀!
答案是,在Python中如果仅存在一个循环导入,程序不会报错。如果一个模块已经被导入,Python会自动识别而不会再次导入。但是如果每个模块试图访问其他模块不同位置的函数或变量时,那么Error又双叒叕出现了。
回到上面的示例中,当导入a.py模块时,程序可以正常导入b.py模块,因为此时b.py模块未访问a.py中定义任何的变量或函数。b.py模块仅引用了a.py模中的a.f()函数。调用的a.f()函数隶属于g()函数,而a.py或b.py模块中并没有调用g()函数。所以程序没有报错。
但是,如果我们在未导入a.py模块之前先导入b.py模块,结果会怎样?
>>> import bTraceback (most recent call last):File "
报错了!问题在于,在导入b.py的过程中,它试图导入a.py模块,而a.py模块会调用f()函数,f()函数又试图访问b.x变量。但此时,还未对变量b.x进行定义,所以出现了AttributeError异常。
稍微修改下b.py,即在g()函数内部导入a.py就可以解决上述问题。
修改后的b.py:
x = 1def g():
import a # This will be evaluated only when g() is calledprint a.f()
现在我们再导入b.py模块,就不会报错啦!
>>> import b>>> b.g()1 # Printed a first time since module 'a' calls 'print f()' at the end1 # Printed a second time, this one is our call to 'g'
常见错误8:文件命名与Python标准库模块的名称冲突
Python的优势之一就是其集成了丰富的标准库。正因为如此,稍不留神就会在为自己的文件命名时与Python自带标准库模块重名。例如,如果你的代码中有一个名为email.py的模块,恰好就和Python标准库中email.py模块重名了。)
上述问题比较复杂。举个例子,在导入模块A的时候,假如该模块A试图导入Python标准库中的模块B,但你已经定义了一个同名模块B,模块A会错误导入你自定义的模块B,而不是Python标准库中的模块B。这种错误很糟糕,因为程序员很难察觉到是因为命名冲突而导致的。
因此,Python程序员要注意避免与Python标准库模块的命名冲突。毕竟,修改自己模块的名称比修改标准库的名称要容易的多!当然你也可以写一份Python改善建议书(Python Enhancement Proposal,PEP)提议修改标准库的名称。
常见错误9:不熟悉Python2和Python3之间的差异
先来看看foo.py文件中的代码:
import sysdef bar(i):if i == 1: raise KeyError(1) if i == 2: raise ValueError(2)def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e)bad()
在Python 2中,上述代码运行正常
$ python foo.py 1key error1$ python foo.py 2value error2
但是在Python 3中运行时:
$ python3 foo.py 1key errorTraceback (most recent call last):File "foo.py", line 19, in
什么情况?原来,在Python 3中,在except代码块作用域外无法访问异常对象。(原因是,Python 3会将内存堆栈中的循环引用进行保留,直到垃圾回收...