关于python函数形参字典中函数的形参问题: def func(i): i=1 print i 上面这个函数无论传递什么参数运行结果都是1

-Python之闭包和匿名函数普通函数形参可以设定默认值,在没有传入相应参数的时候,函数自动引用默认参数def test1(arg1='参数一', arg2='参数二'):
print('arg1:'+arg1)
print('arg2:'+arg2)
# arg1:参数一
arg2:参数二
# 默认情况下,参数值和参数名称是按函数声明中定义的的顺序匹配起来的
test1('ice', 'cream')
# arg1:ice
arg2:cream
test1(arg2='cream', arg1='ice')
# arg1:ice
arg2:cream
Python 不定长参数 *args, **dictargs 1. 加了星号(*)的变量名会存放所有未命名的变量参数,不能存放dict,否则报错。如:def multiple(arg, *args):
print "arg: ", arg
#打印不定长参数
for value in args:
print "other args:", value
if __name__ == '__main__':
multiple(1,'a',True)
输出: 2. 加了星号(**)的变量名会存放所有未命名的变量参数def multiple2(**args):
#打印不定长参数
for key in args:
print key + ":" + bytes(args[key])
if __name__ == '__main__':
multiple2(name='Amy', age=12, single=True)
输出 3. 有 *args 和 **dictargs: def multiple(arg, *args, **dictargs):
print "arg: ", arg
for value in args:
print "other args:", value
#打印dict类型的不定长参数 args
for key in dictargs:
print "dictargs:" + key + ":" + bytes(dictargs[key])
if __name__ == '__main__':
multiple(1,'a',True, name='Amy',age=12, )
闭包必须是一个嵌套的函数。闭包必须返回嵌套函数。嵌套函数必须引用一个外部的非全局的局部自由变量。# 嵌套函数但不是闭包
def nested():
def nst():
print('i am nested func %s' % nested.__name__) #没有返回嵌套函数,没有引用一个外部的非全局的局部只有变量
# 闭包函数
def closure():
var = 'hello world' # 非全局局部变量
def cloe():
print(var) # 引用var
return cloe # 返回内部函数
cl = closure()
闭包优点:避免使用全局变量:两个函数传递值的时候需要使用全局变量传值。可以提供部分数据的隐藏:函数内部的数据不可通过外部访问到。可以提供更优雅的面向对象实现:部分自定义少方法的类实现的时候,可以通过闭包实现。# 用类实现一个加法的类是这样
class _Add(object):
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
return self.a + self.b
# 用闭包实现
def _Add(a):
def add(b):
return a + b
return add
ad = _Add(1) # 是不是很像类的实例化 ,第一次赋值给ad的时候就已经把a的值传到add中
print(ad(1)) # out:2
print(ad(2)) # out:3
print(ad(3)) # out:4
Python 延迟绑定结合一个题目来说明:def multipliers():
return [lambda x : i*x for i in range(4)]
print [m(2) for m in multipliers()]
# [6, 6, 6, 6]
其实这个题目,可能目的是想输出:[0, 2, 4, 6],如何改进才能输出这个结果呢?def multipliers():
# 添加了一个默认参数i=i
return [lambda x, i=i: i*x for i in range(4)]
print [m(2) for m in multipliers()]
# [0, 2, 4, 6]
multipliers就是一个闭包函数了1.def multipliers():
return [lambda x : i*x for i in range(4)]
# multipliers内嵌套一个匿名函数
# 该匿名函数引用外部非全局变量 i
# 返回该嵌套函数
6.print [m(2) for m in multipliers()]
下面来解释为什么输出结果是[6,6,6,6]。拆解成普通函数:def multipliers(x):
return [lambda x,i=3: i*x, lambda x,i=3: i*x, lambda x,i=3: i*x, lambda x,i=3:i*x i=3]
print [m(2) for m in multipliers()]
运行代码,代码从第6行开始运行,解释器碰到了一个列表解析,循环取multipliers()函数中的值,而multipliers()函数返回的是一个列表对象,这个列表中有4个元素,每个元素都是一个匿名函数(实际上说是4个匿名函数也不完全准确,其实是4个匿名函数计算后的值,因为后面for i 的循环不光循环了4次,同时提还提供了i的变量引用,等待4次循环结束后,i指向一个值i=3,这个时候,匿名函数才开始引用i=3,计算结果。所以就会出现[6,6,6,6],因为匿名函数中的i并不是立即引用后面循环中的i值的,而是在运行嵌套函数的时候,才会查找i的值,这个特性也就是延迟绑定)# 为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码,并不是准确的):
def multipliers():
return [lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x, lambda x: 3 * x]
因为Python解释器,遇到lambda(类似于def),只是定义了一个匿名函数对象,并保存在内存中,只有等到调用这个匿名函数的时候,才会运行内部的表达式,而for i in range(4) 是另外一个表达式,需等待这个表达式运行结束后,才会开始运行lambda 函数,此时的i 指向3,x指向2那我们来看下,添加了一个i=i,到底发生了什么?def multipliers():
# 添加了一个默认参数i=i
return [lambda x, i=i: i*x for i in range(4)]
添加了一个i=i后,就给匿名函数,添加了一个默认参数,而python函数中的默认参数,是在python 解释器遇到def(i=i)或lambda 关键字时,就必须初始化默认参数,此时for i in range(4),每循环一次,匿名函数的默认参数i,就需要找一次i的引用,i=0时,第一个匿名函数的默认参数值就是0,i=1时,第二个匿名函数的默认参数值就是1,以此类推。# 为了便于理解,你可以想象下multipliers内部是这样的(这个是伪代码只是为了理解):
def multipliers():
return [lambda x,i=0: i*x, lambda x,i=1: i*x, lambda x,i=2: i*x, lambda x,i=3:i*x i=3]
# x的引用是2 所以output的结果就是:[0,2,4,6]
当然你的i=i,也可以改成a=i。def multipliers():
return [lambda x,a=i: a * x for i in range(4)]
Python的延迟绑定其实就是只有当运行嵌套函数的时候,才会引用外部变量i,不运行的时候,并不是会去找i的值,这个就是第一个函数,为什么输出的结果是[6,6,6,6]的原因。闭包中引用变量的LEGB法则实际上,每一个函数对象,都有一个指向了该函数定义时所在全局名称空间的globals属性:#show_filename inside wrapper
#show_filename.__globals__
'__builtins__': &module '__builtin__' (built-in)&,
#内建作用域环境
'__file__': 'enclosed.py',
'wrapper': &function wrapper at 0x7f&,
#直接外围环境
'__package__': None,
'__name__': '__main__',
'foo': &module 'foo' from '/home/chiyu/foo.pyc'&,
'__doc__': None
当代码执行到show_filename中的return “filename: %s” % filename语句时,解析器按照下面的顺序查找filename变量: 1.Local - 本地函数(show_filename)内部,通过任何方式赋值的,而且没有被global关键字声明为全局变量的filename变量; 2.Enclosing - 直接外围空间(上层函数wrapper)的本地作用域,查找filename变量(如果有多层嵌套,则由内而外逐层查找,直至最外层的函数); 3.Global - 全局空间(模块enclosed.py),在模块顶层赋值的filename变量; 4.Builtin - 内置模块(builtin)中预定义的变量名中查找filename变量;在任何一层先找到了符合要求的filename变量,则不再向更外层查找。如果直到Builtin层仍然没有找到符合要求的变量,则抛出NameError异常。这就是变量名解析的:LEGB法则。举例:#foo.py
filename = "foo.py"
def call_func(f):
return f()
#如前面介绍的,f引用一个函数对象,然后调用它
在另一个func.py模块中,写下了这样的代码:#func.py
import foo
#导入foo.py
filename = "func.py"
def show_filename():
return "filename: %s" % filename
if __name__ == "__main__":
print foo.call_func(show_filename)
#注意:实际发生调用的位置,是在foo.call_func函数中
当我们用python func.py命令执行func.py时输出结果为:chiyu@chiyu-PC:~$ python func.py
filename:func.py
很显然show_filename()函数使用的filename变量的值,是在与它相同环境(func.py模块)中定义的那个。尽管foo.py模块中也定义了同名的filename变量,而且实际调用show_filename的位置也是在foo.py的call_func内部。 而对于嵌套函数,这一机制则会表现的更加明显:闭包将会捕捉内层函数执行所需的整个环境:#enclosed.py
import foo
def wrapper():
filename = "enclosed.py"
def show_filename():
return "filename: %s" % filename
print foo.call_func(show_filename)
#输出:filename: enclosed.py
总结: 1.闭包最重要的使用价值在于:封存函数执行的上下文环境; 2.闭包在其捕捉的执行环境(def语句块所在上下文)中,也遵循LEGB规则逐层查找,直至找到符合要求的变量,或者抛出异常。匿名函数
不会像def functionname():这样定义匿名函数,匿名函数没有固定的可调用的变量名,它的基本形式是:
lambda args: expressionargs:用逗号隔开参数列表expression:用到args中各个参数的表达式lambda语句定义的代码必须是合法的表达式,不能出现多条件语句,可以使用if的三元表达式,不能出现其他的非表达式语句,如for和while等。lambda的首要用途是指定短小的回调函数lambda将返回一个函数而不是将函数赋值给某个变量lambda是一个表达式而非语句lambda是一个单个表达式,而不是一个代码块语句和表达式的区别:在程序设计语言中,语句指的是执行单元,通常以行为单位,表达式指的是可用于计算的式子,即可能产生一个值的式子。语句可以包含有表达式,表达式也可以单独形成一个语句范例:function=lambda x,y:x+y
function(3,4)
lambda的重要功能是函数的速写l3=[(lambda x:x*2),(lambda y:y*3)]
res=l3[0](3)
l3={'函数1':(lambda x:x*2),'函数2':(lambda y:y*3)}
res=l3['函数1'](2)
filter过滤器Python内建的filter()函数用于过滤序列。和map()类似,filter()也接收一个函数和一个序列。和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。例如,在一个list中,删掉偶数,只保留奇数,可以这么写:def is_odd(n):
return n % 2 == 1
print(list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])))
&&&[1, 5, 9, 15]
把一个序列中的空字符串删掉,可以这么写:def not_empty(s):
return s and s.strip()
filter(not_empty, ['A', '', 'B', None, 'C', '
print(list(filter(not_empty, ['A', '', 'B', None, 'C', '
&&&['A', 'B', 'C']
31 条评论分享收藏&nbsp>&nbsp
&nbsp>&nbsp
&nbsp>&nbsp
Python之函数练习题
摘要:一、简述普通参数、指定参数、默认参数、动态参数的区别普通参数:就是放入一个形参,当放入实参时,需要按照顺序给形参值。指定参数:放入实参时是指定的,不用按照顺序给形参,都能让形参获得相应的参数。默认参数:在形参内指定一个参数,需要放在形参的最后面。当实参没有给值时,就默认是形参的值。动态参数:格式:*args和**kwargs前面一个保存为元组,后面一个保存为字典。二、写函数,计算传入字符串中的【数字】、【字母】、【空格】和【其他】的个数#!/bin/bash/envpytho
一、简述普通参数、指定参数、默认参数、动态参数的区别
普通参数:就是放入一个形参,当放入实参时,需要按照顺序给形参值。
指定参数:放入实参时是指定的,不用按照顺序给形参,都能让形参获得相应的参数。
默认参数:在形参内指定一个参数,需要放在形参的最后面。当实参没有给值时,就默认是形参的值。
动态参数:格式:*args 和 **kwargs 前面一个保存为元组,后面一个保存为字典。
二、写函数,计算传入字符串中的【数字】、【字母】、【空格】和【其他】的个数 #!/bin/bash/env python# -*- coding:utf-8 -*-#function:写函数,计算传入字符串中的【数字】、【字母】、【空格】和【其他】的个数def func1(p): digit_number = 0 space_number = 0 alpha_number = 0 else_number = 0 for i in p: if i.isdigit(): #检查字符串是否只由数字组成 digit_number += 1 elif i.isspace(): #检查字符串是否只由空格组成 space_number += 1 elif i.isalpha(): #检查字符串是否只由字母组成 alpha_number += 1 else: else_number += 1 return (digit_number,space_number,alpha_number,else_number)r = func1(&qwer 123&)print(r)结果:(3, 2, 4, 0)
三、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5 #!/bin/bash/env python# -*- coding:utf-8 -*-#function:写函数,计算传入字符串中的【数字】、【字母】、【空格】和【其他】的个数def func1(p): i1 = len(p) print(i1) if i1 & 5: print('Yes,the length higher than 5') else: print('NO')r = func1((11,22,33))
四、写函数,检查用户传入的对象(字符串、列表、元组)的每一个元素是否含有空内容 #!/bin/bash/env python# -*- coding:utf-8 -*-#function:写函数,检查用户传入的对象(字符串、列表、元组)的每一个元素是否含有空内容def func1(p,q,i): if p == &&: print('字符串有') if q == []: print('列表有') if i == (): print(&元组有&)r = func1(&123&,[11,22],())结果:元组有
五、写函数,检查传入列表的长度,如果大于2,那么仅仅保留前两个长度的内容,并将新内容返回给调用者 #!/bin/bash/env python# -*- coding:utf-8 -*-#function:写函数,检查传入列表的长度,如果大于2,那么仅仅保留前两个长度的内容,并将新内容返回给调用者def func1(p): i1 = len(p) if i1 & 2: i2 = p[0:2] return i2r = func1([11,22,33,44,55])print(r)结果:[11, 22]
六、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新的列表返回给调用者& #!/bin/bash/env python# -*- coding:utf-8 -*-#function:写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新的列表返回给调用者def func1(p,q): result = [] for i1 in range(len(p)): if i1 % 2 == 1: result.append(p[i1]) for i2 in range(len(q)): if i2 % 2 == 1: result.append(p[i2]) print(result)r = func1([11,22,33],(11,22,33))结果:[22,22]
七、写函数,检查传入字典的每一个value的长度,如果大于2,那么仅仅保留前两个长度的内容,并将新内容返回给调用者
dic = {“k1”: &v1v1&,&k2&:[11,22,33}}
ps:字典中的value只能是字符串或列表 #!/bin/bash/env python# -*- coding:utf-8 -*-#function:检查传入字典的每一个value的长度,如果大于2,那么仅仅保留前两个长度的内容,并将新内容返回给调用者def func1(**p): for key,value in p.items(): if len(value) & 2: p[key] = value[0:2] return pr = func1(k1=&v1v1&,k2=[11,22,33,44,55])print(r)结果:{'k1': 'v1', 'k2': [11, 22]}
以上是的内容,更多
的内容,请您使用右上方搜索功能获取相关信息。
若你要投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内给你回复。
云服务器 ECS
可弹性伸缩、安全稳定、简单易用
&40.8元/月起
预测未发生的攻击
&24元/月起
为您提供0门槛上云实践机会
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供
Python之函数练习题相关信息,包括
的信息,所有Python之函数练习题相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
支持与服务
资源和社区
关注阿里云
International当前位置: >>
Python程序设计 第6 章 函数的设计和使用(第8次课)
计算机编程导论课程建设组 编著2014.10 第6 章函数的设计和使用1.问题的引入 2. 函数基础知识 6.1 问题的引入【问题6-1】计算三个圆的面积和周长,这三个圆的半 径分别为2,3,4。 分析:按照已学过的知识,解决该问题的程序如下: #Ques6_1.py a=2 area_a=3.14*a*a perimeter_a=3.14*2*a print '半径为2的圆的面积为:', area_a print '半径为2的圆的周长为:', perimeter_a b=3 area_b=3.14*b*b perimeter_b=3.14*2*b print '半径为3的圆的面积为:', area_b print '半径为3的圆的周长为:', perimeter_b c=4 area_c=3.14*c*c perimeter_c=3.14*2*c print '半径为4的圆的面积为:',area_c print '半径为4的圆的周长为:', perimeter_c 程序运行结果: 半径为2的圆的面积为: 12.56 半径为2的圆的周长为: 12.56 半径为3的圆的面积为: 28.26 半径为3的圆的周长为: 18.84 半径为4的圆的面积为: 50.24 半径为4的圆的周长为: 25.12 这三段基本相同的代码是否能够 只写一次呢? 对于这样的问题,我们可以使用 函数来解决,使计算圆面积和周长的 这段代码得以重用。 6.2 黑箱模型黑箱模型指所建立的模型只考虑输入与输出, 而与过程机理无关。 函数定义格式如下: def 函数名(参数): ……(函数体)6.3 函数基础知识 6.3.1 函数的定义 函数定义格式如下: def 函数名([参数]): ……(函数体) 【例6-1】 定义一个输出函数,打印出Hello World!。框图:开始程序: #Exp6_1.py def sayHello( ): #函数定义 print 'Hello World! ' #函数体 #主程序 sayHello( ) #函数调用结束调用函数sayHello()程序运行结果: Hello World! 你能区分主程序和函数吗? 6.3.2 形参和实参在定义函数时,函数名后面括号中的变量称为形参 在调用函数时,可以通过参数将一些值传递给函数处理, 这些在调用函数时提供给函数的值称为实参。 【例6-2】用函数调用的方法来解决6.1节中的问题,如图6-4 所示。 程序: # Exp6_2.py def circle(r): area=3.14*r*r perimeter=2*3.14*r print '半径为',r,'的圆面积为:', area print '半径为',r,'的圆周长为:', perimeter #主程序 circle(2) #函数调用 circle(3) #函数调用 circle(4) #函数调用 参数传递示意图小 路 大 路 【例6-3】 编写函数,实现比较两个数的大小,并输出其中 较大的数,并调用函数。 程序: #Exp6_3.py def printMax(a, b): if a&b: 程序运行结果: pirnt a, 'is the max' 4 is the max else: 7 is the max print b, 'is the max' #主程序 printMax(3, 4) #传递两个实数实参 x=5 y=7 printMax(x, y) #传递两个变量实参 请回答问题:printMax(x+x, y) 将输出什么? 6.3.3 return语句return语句用来从一个函数中返回,即跳出函数,也可 用return语句从函数中返回一个值。 【例6-4】 用return语句返回值。 # Exp6_4.py 程序运行结果: def maximum( x, y ): 3 if x&y: return x else: a=maximum(2,3) return y print a #主程序 print maximum( 2, 3 ) 6.4 变量的作用域当引入函数的概念之后,就出现了变量作用域 的问题。变量起作用的范围称为变量的作用域。一 个变量在函数外部定义和在函数内部定义,其作用 域是不同的。如果我们用特殊的关键字定义一个变 量,也会改变其作用域。 在函数内定义的变量只在该函数内起作用,称为局部变量。 【例6-5】 使用局部变量。 #Exp6_5.py def func( x ):6.4.1 局部变量print 'x is', xx=2 print 'changed local x to', x #主程序 x=50 func( x ) print 'x is still', x程序运行结果:x is 50 changed local x to 2 x is still 50注意:本例有三个不同的x,函数外定义的x、形参x、函数内定 义的x ,在内存中是三个不同的单元。 6.4.2 全局变量首先,在函数外定义的变量是全局变量,在其下方调用的 函数中都能获得其值。但在函数中对其赋值不会改变全局 变量的值而产生一个局部变量,如例6-5所示。 如果想要在函数内部给一个定义在函数外的变量赋值,那 么需要在函数内部用global来说明。global也能在函数内 部定义出全局变量。下面是global的用法: (1)一个变量已在函数外定义,如果在函数内需要为这个 变量赋值,并要将这个赋值结果反映到函数外,可以在函 数内用global声明这个变量,将其定义为全局变量。(2)在函数内部直接将一个变量声明为全局变量,在函数 外没有声明,在调用这个函数之后,将增加为新的全局变 量。 【例6-6】 在函数内使用外部定义 的全局变量。 # Exp6_6.py def func( ): global x print 'x is', x x=2 print 'changed local x to', x #主程序 x=50 func( ) print 'value of x is', x程序运行结果: x is 50changed local x to 2value of x is 2 【例6-7】 把函数内定义的变量声明为全局变量。# Exp6_7.py def func( ):global xx=2 print 'x=', x#主程序func( ) print 'x=', x程序运行结果: x=2 x=2思考: 主程序中的两行代码调顺序是否可行? 6.5 参数的类型6.5.1 默认参数 def 函数名(形参名=默认值,……) 【例6-8】 使用默认参数值。 程序: # Exp6_8.py 程序运行结果: def say( message, times =1 ): Hello print message * times WorldWorldWorldWorldWorld #主程序 say( 'Hello' ) say( 'World', 5 ) 6.5 参数的类型6.5.2 关键参数 指实参 【例6-9】 使用关键参数。 # Exp6_9.py def func( a, b=5, c=10 ): print 'a is', a, 'and b is', b, 'and c is', c #主程序 func( 3, 7 ) func( 25, c=24 ) func( c=50, a=100 )程序运行结果: a is 3 and b is 7 and c is 10a is 25 and b is 5 and c is 24a is 100 and b is 5 and c is 50 练习1:写一个函数,其功能是求1+2+…+n 练习2:使用maximum( x, y )函数求三个数 的最大值。 练习3:试验、理解本次课的所有例子。 第8次上机作业: 6.2 6.5 6.1
赞助商链接
Python语言程序设计(美-梁勇)第7章习题解答_计算机...7.6 构建一个对象的语法是什么?Python 在创建一个...Print a.i 答:需要传递一个实参来调用初始化函数...Python语言程序设计(美-梁勇)第4章选择习题解答_计算机软件及应用_IT/计算机_专业资料。第4章 选择 第4章 选择 4.1 列举六种比较运算符。 答:&,&,==,!=,...暂无评价|0人阅读|0次下载|举报文档Python语言程序设计(美-梁勇)第3章(英文)习题解答_计算机软件及应用_IT/计算机_专业资料。第3章数学函数、字符串和对象 Chapte...PYTHON 程序设计及运行结果 第 6 页 文档贡献者 一中无语无语 贡献于2017-04...《高级语言程序设计》课... 8页 1下载券
Python 语言程序设计1 暂无评价 7...《Python 程序设计实验》教学大纲课程名称(中文):Python 程序设计实验 课程名称(英文):Experiments of Programming in Python 课程编号:ZX36317 课程性质:独立设课 ...《Python 程序设计》题库一、填空题 第一章 基础...(globals()) 67、 可以使用内置函数___查看包含当前...([6, 7, 8, 9, 10]) 114、 表达式 {1, 2...Python语言程序设计(美-梁勇)第5章习题解答(英文)_计算机软件及应用_IT/计算机...The printout is 2, 4, 6, 8 on separate lines. 4. 5. max is 5 ...暂无评价|0人阅读|0次下载|举报文档Python语言程序设计(美-梁勇)第6章习题解答(英文)_计算机软件及应用_IT/计算机_专业资料。Python语言程序设计,第6章,函数, Ch...Python 程序设计基础 习题答案与分析 程昱 第 1 章 基础知识 1.1 简单说明...[start:end] 2.6 列表对象的 sort()方法用来对列表元素进行原地排序,该函数...Python语言程序设计(美-梁勇)第5章习题解答_计算机软件及应用_IT/计算机_专业...次,输出结果为 2\n 4\n 6\n 8(4 行) 5.4 指出下面代码的错误: 答:a...
All rights reserved Powered by
www.tceic.com
copyright &copyright 。文档资料库内容来自网络,如有侵犯请联系客服。Python程序员常犯的10个错误
原文链接:
BY&&- SENIOR SOFTWARE ENGINEER @&
About Python 关于Python
&is an interpreted, object-oriented, high-level programming language with dynamic semantics. Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for, as well as for use as a scripting or glue language to connect existing components or services. Python supports modules and packages, thereby encouraging program modularity and code reuse.
Python是一门解释型、面向对象、具有动态语义的高级编程语言。其高级的内置数据结构,结合动态类型与动态绑定,使得Python非常适于快速应用开发,也可作为一门脚本语言或者胶水语言来联结现有的组件或者服务。Python支持模块和包,因而鼓励程序模块化和代码重用。
About this article 关于本文
Python&s simple, easy-to-learn syntax can mislead Python developers & especially those who are newer to the language & into missing some of its subtleties and underestimating the power of the language.Python简单易学的语法可能会误导Python开发者&尤其是那些语言的初学者&忽略其精妙之处并且低估这门语言的能力。
With that in mind, this article presents a &top 10& list of somewhat subtle, harder-to-catch mistakes that can bite even the most advanced Python developer in the rear.考虑到这一点,本文列举了有些微妙,难以捕捉,甚至可能会咬到高级Python程序员屁股的10大错误清单。
Common Mistake #1: Misusing expressions as defaults for function arguments 函数参数默认值表达式的误用
Python allows you to specify that a function argument is&optional&by providing a&default value&for it. While this is a great feature of the language, it can lead to some confusion when the default value is&. For example, consider this Python function definition: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
A common mistake is to think that the optional argument will be set to the specified default expression&each time&the function is called without supplying a value for the optional argument. In the above code, for example, one might expect that calling&foo()&repeatedly (i.e., without specifying a&bar&argument) would always return&'baz', since the assumption would be that&each time&foo()&is called (without a&bar&argument specified)&baris set to&[]&(i.e., a new empty list).一个常见的错误是认为每当可选参数没有赋值调用函数的时候,可选参数都会被设为指定的缺省表达式。比如在上面的代码中,人们可能期望重复调用函数foo的时候(亦即,不指定bar参数的值)函数总是返回'baz',由于假设是每次foo被调用时(没有指定bar参数的值),都被设为[](亦即,一个新的空列表)
But let&s look at what actually happens when you do this:让我们看看你这么做的时候实际上会产生什么结果:
["baz", "baz"]
["baz", "baz", "baz"]
Huh? Why did it keep appending the default value of&"baz"&to an&existing&list each time&foo()&was called, rather than creating a&new&list each time?嗯?为什么每次foo被调用的时候都会在现有列表中追加缺省值"bz",而不是每次创建一个新列表呢?
The answer is that&the default value for a function argument is only evaluated once, at the time that the function is defined.&Thus, the&bar&argument is initialized to its default (i.e., an empty list) only when&foo()&is first defined, but then calls to&foo()&(i.e., without a&bar&argument specified) will continue to use the same list to which&bar&was originally initialized.答案是函数的缺省值只在函数定义时进行一次初始化。因此,bar参数只在foo()函数第一次定义时初始化为其缺省值(亦即,一个空列表),但是当接下来foo()函数被调用时(亦即,bar参数不赋值)会继续沿用bar最开始初始化得到的那个列表。
FYI, a common workaround for this is as follows:仅供参考,一个常见的解决方案如下所示:
&&& def foo(bar=None):
if bar is None:
# or if not bar:
bar.append("baz")
return bar
Common Mistake #2: Using class variables incorrectly 类变量的误用
Consider the following example:考虑下面的例子:
&&& class A(object):
&&& class B(A):
&&& class C(A):
&&& print A.x, B.x, C.x
这还说得通。
&&& B.x = 2
&&& print A.x, B.x, C.x
Yup, again as expected.没错,仍然符合预期。
&&& A.x = 3
&&& print A.x, B.x, C.x
What the&$%#!&?? We only changed&A.x. Why did&C.x&change too?这是闹哪样啊?我们只更改了A.x,为什么C.x的值也变了?
In Python, class variables are internally handled as dictionaries and follow what is often referred to as&. So in the above code, since the attribute&x&is not found in class&C, it will be looked up in its base classes (only&A&in the above example, although Python supports multiple inheritance). In other words,&C&doesn&t have its own&x&property, independent of&A. Thus, references to&C.x&are in fact references to&A.x.在Python里,类变量内部以字典形式进行处理,并且遵从方法解析顺序(MRO)。所以在上面的代码中,由于x属性在C类中没有找到,它会继续在基类中查找(虽然Python支持多重继承,但是上例中只有A类)。换言之,C没有独立于A的、它自己的x属性。因此,对C.x的引用实际上是对A.x的引用。
Common Mistake #3: Specifying parameters incorrectly for an exception block 异常块参数指定错误
Suppose you have the following code:假如你有一段这样的代码:
l = ["a", "b"]
... except ValueError, IndexError:
# To catch both exceptions, right?
Traceback (most recent call last):
File "", line 3, in
IndexError: list index out of range
The problem here is that the&except&statement does&not&take a list of exceptions specified in this manner. Rather, In Python 2.x, the syntax&except Exception, e&is used to bind the exception to the&optional&second parameter specified (in this case&e), in order to make it available for further inspection. As a result, in the above code, the&IndexError&exception is&not&being caught by the&except& rather, the exception instead ends up being bound to a parameter named&IndexError.这里的问题是异常声明没有通过这种方式获取一组异常。相反,在Python 2.x中,语法except Exception, e用来将异常绑定到指定的第二个可选参数上(此例中是e),以使其在将来的检查中可用。其结果是,在上面的代码中,IndexException异常没有通过异常声明被捕捉到,相反,这个异常以绑定至命名为IndexError的参数结束。
The proper way to catch multiple exceptions in an&except&statement is to specify the first parameter as a&&containing all exceptions to be caught. Also, for maximum portability, use the&as&keyword, since that syntax is supported by both Python 2 and Python 3:在异常声明里捕捉多重异常的正确方式是以包含所有待捕捉异常元组的形式指定第一个参数。并且,为保证最大限度的可移植性,使用as关键词,因为该语法在Python2和Python3中都支持:
l = ["a", "b"]
... except (ValueError, IndexError) as e:
Common Mistake #4: Misunderstanding Python scope rules Python作用域规则的误解
Python scope resolution is based on what is known as the&&rule, which is shorthand for&Local,&Enclosing,Global,&Built-in. Seems straightforward enough, right? Well, actually, there are some subtleties to the way this works in Python. Consider the following:Python作用域规则基于所谓的LEGB规则,它是Local(局部),Enclosing(封闭),Global(全局),Build-in(内置)的缩写。似乎很直观,是么?实际上,Python的这一规则中有一些玄机。考虑下面这个例子:
&&& x = 10
&&& def foo():
Traceback (most recent call last):
File "&stdin&", line 1, in &module&
File "&stdin&", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
What&s the problem?这是什么问题?
The above error occurs because, when you make an&assignment&to a variable in a scope,&that variable is automatically considered by Python to be local to that scope&and shadows any similarly named variable in any outer scope.上面报错是因为,当你在一个作用域内为变量赋值的时候,变量会自动被Python当做局部作用域并屏蔽所有外层作用域的同名参数。
Many are thereby surprised to get an&UnboundLocalError&in previously working code when it is modified by adding an assignment statement somewhere in the body of a function. (You can read more about this&.)因此许多人在前面的代码例子,函数体外添加了赋值语句,抛出UnboundLocalError异常时感到很惊讶。
It is particularly common for this to trip up developers when using&. Consider the following example:开发人员使用list被坑的情况尤其常见。考虑下面的例子:
&&& lst = [1, 2, 3]
&&& def foo1():
lst.append(5)
# This works ok...
&&& foo1()
[1, 2, 3, 5]
&&& lst = [1, 2, 3]
&&& def foo2():
lst += [5]
# ... but this bombs!
&&& foo2()
Traceback (most recent call last):
File "&stdin&", line 1, in &module&
File "&stdin&", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment
Huh? Why did&foo2&bomb while&foo1&ran fine?昂?为什么foo2挂了而foo1可以呢?
The answer is the same as in the prior example, but is admittedly more subtle.&foo1&is not making an&assignment&to&lst, whereas&foo2&is. Remembering that&lst += [5]&is really just shorthand for&lst = lst + [5], we see that we are attempting to&assign&a value to&lst&(therefore presumed by Python to be in the local scope). However, the value we are looking to assign to&lst&is based on&lst&itself (again, now presumed to be in the local scope), which has not yet been defined. Boom.答案与前例类似,但无可否认这个例子更加微妙。foo1没有给lst赋值,而foo2却赋值了。记住lst += [5]实际上是lst = lst + [5]的简写,我们看到我们试图给lst变量赋值(因此Python假设此变量位于局部作用域中)。然而,我们打算赋值的lst变量的值基于它本身(再次被假定在局部作用域之中),而它还没被定义过。Boom
Common Mistake #5: Modifying a list while iterating over it 遍历列表时修改列表的值
The problem with the following code should be fairly obvious:下面这段代码的问题应该十分明显:
&&& 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 "&stdin&", line 2, in &module&
IndexError: list index out of range
Deleting an item from a list or array while iterating over it is a faux pas well known to any experienced software developer. But while the example above may be fairly obvious, even advanced developers can be unintentionally bitten by this in code that is much more complex.在遍历时从列表或者数组中删除项目是任何有经验软件开发人员都知道的反例(faux pas,失礼)。虽然可能上面这个例子是显而易见的,但是即使高级开发者也可能会在复杂得多的代码中无意识地被这个错误咬一口。
Fortunately, Python incorporates a number of elegant programming paradigms which, when used properly, can result in significantly simplified and streamlined code. A side benefit of this is that simpler code is less likely to be bitten by the accidental-deletion-of-a-list-item-while-iterating-over-it bug. One such paradigm is that of&. Moreover, list comprehensions are particularly useful for avoiding this specific problem, as shown by this alternate implementation of the above code which works perfectly:幸运的是,Python集成了许多优雅的编程范式,当正确使用时,可以使代码显著地简化和精简。一个附带的好处是比较简单的代码不太可能会出现"遍历列表元素意外删除项目"的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]
Common Mistake #6: Confusing how Python binds variables in closures Python闭包绑定变量的误解
Considering the following example:考虑下面的例子:
&&& def create_multipliers():
return [lambda x : i * x for i in range(5)]
&&& for multiplier in create_multipliers():
print multiplier(2)
You might expect the following output:你可能会期望得到下面的输出:
But you actually get:但实际结果是:
Surprise!好惊讶!
This happens due to Python&s&late binding&behavior which says that the values of variables used in closures are looked up at the time the inner function is called. So in the above code, whenever any of the returned functions are called, the value of&i&is looked up&in the surrounding scope at the time it is called&(and by then, the loop has completed, so&i&has already been assigned its final value of 4).这是由于Python的延迟绑定行为导致的,意思是说闭包中使用变量的值在内部函数调用时才被查找。所以在上面的代码中,无论何时任何一个返回的函数被调用的时候,i的值在其被调用时在周围作用域中查找(到那时,循环已经完成,所以i已经被赋为最终值4)
The solution to this is a bit of a hack:解决方案有一点hack:
&&& def create_multipliers():
return [lambda x, i=i : i * x for i in range(5)]
&&& for multiplier in create_multipliers():
print multiplier(2)
Voil&! We are taking advantage of default arguments here to generate anonymous functions in order to achieve the desired behavior. Some would call this elegant. Some would call it subtle. Some hate it. But if you&re a Python developer, it&s important to understand in any case.在这里!我们在这里利用了缺省参数生成匿名函数来获得期望的行为。有人可能会称之为优雅。有人会称之为微妙。有人非常憎恨它。但如果你是Python开发者,无论如何理解是很重要的。
Common Mistake #7: Creating circular module dependencies 创建循环模块依赖
Let&s say you have two files,&a.py&and&b.py, each of which imports the other, as follows:比如说你有两个文件,a.py和b.py,每一个都引入了对方,如下所示:
return b.x
And in&b.py:
print a.f()
First, let&s try importing&a.py:&
&&& import a
Worked just fine. Perhaps that surprises you. After all, we do have a circular import here which presumably should be a problem, shouldn&t it?工作的很好。也许你会惊讶。毕竟,我们的确在这里做了循环导入,这应该是一个问题,不是吗?
The answer is that the mere presence of a circular import is not in and of itself a problem in Python. If a module has already been imported, Python is smart enough not to try to re-import it. However, depending on the point at which each module is attempting to access functions or variables defined in the other, you may indeed run into problems.答案是引用仅仅循环导入本身在Python中不是一个问题。如果一个模块已经被导入过了,Python非常聪明,就不再重复导入了。然而,每个模块彼此试图访问对方的函数或者变量时,你就真的会遇到麻烦了。
So returning to our example, when we imported a.py, it had no problem importing b.py, since b.py does not require anything from a.py to be defined at the time it is imported. The only reference in b.py to a is the call to a.f(). But that call is in g() and nothing in a.py or b.py invokes g(). So life is good.回到我们的例子,我们导入a.py时,导入b.py也没问题,因为b.py在导入a.py时不需要a.py中的任何东西。b.py中对a的唯一引用是a.f()。但是那个调用是在g()中并且a.py或者b.py都没有调用g()。所以生活是如此的美好。
But what happens if we attempt to import b.py (without having previously imported a.py, that is):但当我们试图导入b.py(预先没有导入a.py)会发生什么呢:
&&& import b
Traceback (most recent call last):
File "&stdin&", line 1, in &module&
File "b.py", line 1, in &module&
File "a.py", line 6, in &module&
File "a.py", line 4, in f
return b.x
AttributeError: 'module' object has no attribute 'x'
Uh-oh. That&s not good! The problem here is that, in the process of importing&b.py, it attempts to import&a.py, which in turn calls&f(), which attempts to access&b.x. But&b.x&has not yet been defined. Hence the&AttributeError&exception.哦哦。这不太好!这里的问题是,在导入b.py的过程中,试图导入a.py,随之会调用f(),试图访问b.x。但是b.x还没有被定义过。因此会导致AttributeError异常。
At least one solution to this is quite trivial. Simply modify&b.py&to import&a.py&within&g():此问题的一个解决方案十分的平凡。只需修改b.py,在g()中导入a.py:
import a # This will be evaluated only when g() is called
print a.f()
Now when we import it, everything is fine:现在我们引入时,一切正常:
&&& import b
1 # Printed a first time since module 'a' calls 'print f()' at the end
1 # Printed a second time, this one is our call to 'g'
Common Mistake #8: Name clashing with Python Standard Library modules 与Python标准库模块的命名冲突
One of the beauties of Python is the wealth of library modules that it comes with &out of the box&. But as a result, if you&re not consciously avoiding it, it&s not that difficult to run into a name clash between the name of one of your modules and a module with the same name in the standard library that ships with Python (for example, you might have a module named&email.py&in your code, which would be in conflict with the standard library module of the same name).Python美丽的地方之一是丰富且现成的库模块。但相应的,如果你不去有意识的规避,不难遇到你的模块与Python标准库模块冲突的情况(比如,你可能有一个命名为email.py的模块),这会与同名的标准库模块发生冲突。
This can lead to gnarly problems, such as importing another library which in turns tries to import the Python Standard Library version of a module but, since you have a module with the same name, the other package mistakenly imports your version instead of the one within the Python Standard Library. This is where bad stuff happens.这可能会带来一些麻烦的问题,例如导入其他库时,这个库尝试导入Python标准库版本的一个模块,由于你有一个同名的模块,这个库可能会错误滴导入你的版本而不是Python标准库的版本。这就是问题所在。
Care should therefore be exercised to avoid using the same names as those in the Python Standard Library modules. It&s way easier for you to change the name of a module within your package than it is to file a&&to request a name change upstream and to try and get that approved.因此需要引起注意来避免使用与Python标准库模块相同的模块名称。修改你自己模块的名称比修改Python增强提案(PEP)中的名称要容易,你需要向上流提交申请并且还需要获得批准。
Common Mistake #9: Failing to address differences between Python 2 and Python 3 忽略了Python2与Python3的区别
Consider the following file&foo.py:
import sys
def bar(i):
if i == 1:
raise KeyError(1)
if i == 2:
raise ValueError(2)
def bad():
bar(int(sys.argv[1]))
except KeyError as e:
print('key error')
except ValueError as e:
print('value error')
On Python 2, this runs fine:
$ python foo.py 1
$ python foo.py 2
value error
But now let&s give it a whirl (漩涡)on Python 3:
$ python3 foo.py 1
Traceback (most recent call last):
File "foo.py", line 19, in &module&
File "foo.py", line 17, in bad
UnboundLocalError: local variable 'e' referenced before assignment
What has just happened here? The &problem& is that, in Python 3, the exception object is not accessible beyond the scope of the&except&block. (The reason for this is that, otherwise, it would keep a reference cycle with the stack frame in memory until the garbage collector runs and purges the references from memory. More technical detail about this is available&).这儿发生了什么?&问题&是,在Python3中,异常对象无法在异常块作用域外访问。(原因是在垃圾收集器运行且从内存中清理引用之前会在内存栈帧中保存一个引用周期)
One way to avoid this issue is to maintain a reference to the exception object&outside&the scope of the&except&block so that it remains accessible. Here&s a version of the previous example that uses this technique, thereby yielding code that is both Python 2 and Python 3 friendly:解决此问题的方法之一是在异常块作用域外围护一个异常对象的引用,以使其可访问。这里是前例使用这一技术的一个版本,使得代码对Python2和Python3都友好:
import sys
def bar(i):
if i == 1:
raise KeyError(1)
if i == 2:
raise ValueError(2)
def good():
exception = None
bar(int(sys.argv[1]))
except KeyError as e:
exception = e
print('key error')
except ValueError as e:
exception = e
print('value error')
print(exception)
Running this on Py3k:
$ python3 foo.py 1
$ python3 foo.py 2
value error
Yippee! 好开心!
(Incidentally, our&&discusses a number of other important differences to be aware of when migrating code from Python 2 to Python 3.)
Common Mistake #10: Misusing the&__del__&method __del__方法的误用
Let&s say you had this in a file called&mod.py:假设你有一个叫做mod.py的文件:
import foo
class Bar(object):
def __del__(self):
foo.cleanup(self.myhandle)
And you then tried to do this from&another_mod.py:
import mod
mybar = mod.Bar()
You&d get an ugly&AttributeError&exception.你会得到一个丑陋的AttributeError异常。
Why? Because, as reported&, when the interpreter shuts down, the module&s global variables are all set to&None. As a result, in the above example, at the point that&&is invoked, the name&foo&has already been set to&None.为什么?因为,这里有写,当解释器关闭时,模块的全局变量全部会被设为None。结果是,在上例中,当__del__被调用时,foo这个名字已经被设为None了。
A solution would be to use&&instead. That way, when your program is finished executing (when exiting normally, that is), your registered handlers are kicked off&before&the interpreter is shut down.此问题的解决方法是使用atexit.register()作为替代。用这种方式,当你的程序完成执行(正常退出),你的注册处理函数会在解释器关闭之前被调用
With that understanding, a fix for the above&mod.py&code might then look something like this:理解了这个,上面mod.py代码的修正可能会是这样:
import foo
import atexit
def cleanup(handle):
foo.cleanup(handle)
class Bar(object):
def __init__(self):
atexit.register(cleanup, self.myhandle)
This implementation provides a clean and reliable way of calling any needed cleanup functionality upon normal program termination. Obviously, it&s up to&foo.cleanup&to decide what to do with the object bound to the name&self.myhandle, but you get the idea.这个实现提供了一个简洁而可依赖的方式在程序正常终止时调用任何所需的清理函数。显然,由foo.cleanup来决定应该对绑定到名字self.myhandle的对象做些什么,但你已经明白了。
Wrap-up 总结
Python is a powerful and flexible language with many mechanisms and paradigms that can greatly improve productivity. As with any software tool or language, though, having a limited understanding or appreciation of its capabilities can sometimes be more of an impediment than a benefit, leaving one in the proverbial state of &knowing enough to be dangerous&.Python是一门强大而灵活的语言,附带了许多极大地提高生产率的机制和范式。但是,像任何软件工具或者语言一样,理解的不足或者对其能力误解有时与其说是好处不如说是障碍,一句众所周知的谚语&一知半解是危险的&。
Familiarizing oneself with the key nuances of Python, such as (but by no means limited to) the issues raised in this article, will help optimize use of the language while avoiding some of its more common pitfalls.
You might also want to check out our&&for suggestions on interview questions that can help identify Python experts.
本文链接:请尊重作者的劳动成果,转载请注明出处!书影博客保留对文章的所有权利。
如果您喜欢这篇博文,欢迎您捐赠书影博客:
没有类似日志
Pingbacks已关闭。
欢迎来到的博客
周一周二周三周四周五周六周日
262728293031&}

我要回帖

更多关于 python def函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信