1. 调用函数
1.1 关键字参数和默认参数
关键字参数调用函数允许用户缺失参数或者不按顺序给定参数。默认参数则是声明了默认值的参数。如下例:
def power(a, b=2): print a ** b power(b=3,a=4)
1.2 实例 easyMath.py
#!/usr/bin/env python # -*- coding:utf-8 -*- from operator import add, sub from random import randint, choice ops = {'+': add, '-': sub} MAXTRIES = 2 def doprob(): op = choice('+-') nums = [randint(1, 10) for i in range(2)] nums.sort(reverse=True) ans = ops[op](*nums) pr = '%d %s %d=' %(nums[0], op, nums[1]) oops = 0 while True: try: if int(raw_input(pr)) == ans: print u'解对了' break if oops == MAXTRIES: print u'应该是:\n%s%d' % (pr, ans) else: print u'解错了。。。再拭一次' oops += 1 except (KeyboardInterrupt, EOFError, ValueError): print u'无效输入。。。再试一次' def main(): while True: doprob() try: opt = raw_input(u'继续? [Y]'.encode('gbk')).lower() if opt and opt[0] == 'n': break except (KeyboardInterrupt, EOFError): break if __name__ == '__main__': main()
2. 创建函数
2.1 函数可以具有属性,用句点表示
def foo(): pass foo.__doc__ = "This is foo's doc string" foo.version = 0.1
2.2 内嵌函数
Python支持在函数体内创建另外一个函数,这种函数叫内嵌函数。但要注意内嵌函数的作用域。
如下代码:
def foo(): def bar(): print "bar() called" print "foo() called" bar() foo() bar()
2.3 装饰器
2.3.1 装饰器相关介绍见https://www.zhihu.com/question/26930016
装饰器以@开头,这里有个语法糖,即
@deco2 @deco1 def func(arg1, arg2, ...):pass
和以下代码是等价的:
def func(arg1, arg2, ...):pass func = deco2(deco1(func))
2.3.2 装饰器实际上就是接受函数对象作为参数的函数,返回一个修改后的函数对象,并将其重新赋值给原函数标识符,永久失去对原函数对象的访问。
2.3.3 使用装饰器的例子
#!/usr/bin/env python from time import ctime, sleep def tsfunc(func): def wrappenFunc(): print '[%s] %s() called' %( ctime(), func.__name__) return func return wrappenFunc @tsfunc def foo(): pass foo() sleep(4) for i in range(2): sleep(1) foo()
3. 传递函数
3.0 python中,函数也是对象,也是可以被其他变量引用的:
>>> def foo(): ... print "in foo()" ... >>> bar = foo >>> bar() in foo() >>> bar <function foo at 0x00000000024FD978> >>> ber = foo() in foo() >>> type(bar) <type 'function'> >>> type(ber) <type 'NoneType'>
函数也可以作为参数传递给其他函数调用:
>>> def bar(argfunc): ... argfunc() ... >>> bar(foo) in foo()
3.1 关于函数传递的实例
#!usr/bin/env python def convert(func, seq): 'conv. sequence of numbers to same type' # 将序列中的数字转换为同一类型,类型由转换函数给出 return [func(eachNum) for eachNum in seq] myseq = (123, 45.67, -6.2e8, 999999999L) print convert(int, myseq) print convert(long, myseq) print convert(float, myseq)
4. 形式参数(形参)
4.1 Python函数的形参集合包括所有必要参数(以正确的定位顺序传入函数)、关键字参数(以顺序传入,或不以顺序传入,但指定了参数列表中曾定义的关键字)和所有在函数定义时指明了默认值的函数。下面将分别叙述。
4.2 位置参数
位置参数必须以在函数中定义的顺序来准确传递。另外,若没有设定默认参数值,则传入参数的数目必须和函数定义的数目精确一致。
>>> def foo(who): ... print 'Hello', who ... >>> foo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError:foo() takes exactly 1 argument (0 given) >>> >>> foo('World') Hello World >>> foo('Mr.', 'World') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError:foo() takes exactly 1 argument (2 given)
4.3 例子
#!/usr/bin/env python from urllib import urlretrieve def firstNonBlank(lines): for eachline in lines: if not eachline.strip(): continue else: return eachline def firstLast(webpage): f = open(webpage) lines=f.readlines() f.close() print firstNonBlank(lines) lines.reverse() print firstNonBlank(lines) def download(url='http://www.baidu.com', process=firstLast): try: retval = urlretrieve(url)[0] except IOError: retval = None if retval: process(retval) if __name__ == '__main__': download()
5.可变参数
5.1 可变长度的参数包括非关键字可变长参数即元组和关键字变量参数即字典,其中关键字变量参数必须在最后。
A. 非关键字可变长参数
def tupleVarArgs(arg1, arg2='defaultB', *theRest): 'display regular args and non-keyword variable args' print 'formal arg1:', arg1 print 'formal arg2:', arg2 for eachXtrArg in theRest: print 'another arg:', eachXtrArg
执行结果:
>>> tupleVarArgs('abc') formal arg1: abc formal arg2: defaultB >>> >>> tupleVarArgs(23, 4.56) formal arg1: 23 formal arg2: 4.56 >>> >>> tupleVarArgs('abc', 123, 'xyz', 456.789) formal arg1: abc formal arg2: 123 another arg: xyz another arg: 456.789
B.关键字变量参数
def dictVarArgs(arg1, arg2='defaultB', **theRest): 'display 2 regular args and keyword variable args' print 'formal arg1:', arg1 print 'formal arg2:', arg2 for eachXtrArg in theRest.keys(): print "Xtra arg %s: %s" % \ (eachXtrArg, str(theRest[eachXtrArg]))
执行结果:
>>> dictVarArgs(1220, 740.0, c='grail') formal arg1: 1220 formal arg2: 740.0 Xtra arg c: grail >>> >>> dictVarArgs(arg2='tales', c=123, d='poe', arg1='mystery') formal arg1: mystery formal arg2: tales Xtra arg c: 123 Xtra arg d: poe >>> >>> dictVarArgs('one', d=10, e='zoo', men=('freud', 'gaudi')) formal arg1: one formal arg2: defaultB Xtra arg men: ('freud', 'gaudi') Xtra arg e: zoo Xtra arg d: 10 >>>
C. 混合型
def newfoo(arg1, arg2, *nkw, **kw): 'display regular args and all variable args' print 'arg1 is:', arg1 print 'arg2 is:', arg2 for eachNKW in nkw: print 'additional non-keyword arg:', eachNKW for eachKW in kw: print "additional keyword arg '%s': %s" % \ (eachKW, kw[eachKW])
执行结果:
>>> newfoo('wolf', 3, 'projects', freud=90, gamble=96) arg1 is: wolf arg2 is: 3 additional non-keyword arg: projects additional keyword arg 'gamble': 96 additional keyword arg 'freud': 90 >>>
5.2 更多的调用可变参数的例子
A.经典风格
>>> newfoo(10, 20, 30, 40, foo=50, bar=60) arg1 is: 10 arg2 is: 20 additional non-keyword arg: 30 additional non-keyword arg: 40 additional keyword arg 'foo': 50 additional keyword arg 'bar': 60
B.可以非关键字参数放在元组中、关键字参数放在词典中调用,而不是逐个列出
>>> newfoo(2, 4, *(6, 8), **{'foo': 10, 'bar': 12}) arg1 is: 2 arg2 is: 4 additional non-keyword arg: 6 additional non-keyword arg: 8 additional keyword arg 'foo': 10 additional keyword arg 'bar': 12
C.先创建元组和字典,再给函数调用
>>> newfoo(1, 2, 3, x=4, y=5, *aTuple, **aDict) arg1 is: 1 arg2 is: 2 additional non-keyword arg: 3 additional non-keyword arg: 6 additional non-keyword arg: 7 additional non-keyword arg: 8 additional keyword arg 'y': 5 additional keyword arg 'x': 4 additional keyword arg 'z': 9
上例说明,在已定义的元组和列表之外的参数,也可以被函数调用
5.3 函数式编程例子
#!/usr/bin/env python def testit(func, *nkwargs, **kwargs): try: retval = func(*nkwargs, **kwargs) result = (True, retval) except Exception, diag: result = (False, str(diag)) return result def test(): funcs = (int, float, long) vals = (1234, 12.34, '1234', '12.34') for eachFunc in funcs: print '_' * 20 for eachVal in vals: retval = testit(eachFunc,eachVal) if retval[0]: print '%s(%s) = ' %(eachFunc.__name__, eachVal), retval[1] else: print '%s(%s) = FAILED' %(eachFunc.__name__, eachVal), retval[1] if __name__ == '__main__': test()
6. 函数式编程
6.1 匿名函数与lambda
通过 lambda 与标准函数对比来理解
>>> def true(): return True >>> lambda : True # 与前者等价 >>> def add(x, y): return x + y >>> lambda x, y: x + y >>> def usuallyAdd2(x, y=2): return x+y >>> lambda x, y=2: x+y >>> def showAllAsTuple(*z): return z >>> lambda *z: z # 以上每行 lambda 语句均等价于其上一行标准函数语句
运行时的演示:
>>> a = lambda x, y=2: x + y >>> a(3) 5 >>> a(3,5) 8 >>> a(0) 2 >>> a(0,9) 9 >>> >>> b = lambda *z: z >>> b(23, 'zyx') (23, 'zyx') >>> b(42) (42,)
6.2 内建函数apply() filter() map() reduce()
6.2.1 apply()已逐渐被淘汰
6.2.2 filter()函数为给定的序列对象每个元素调用给定的布尔函数,将返回非零值的元素添加到一个列表中。list列表解析可以代替filter。下面通过一段简单代码的重构来说明
A.原始代码
from random import randint def odd(n): return n % 2 allNums = [] for eachNum in range(9): allNums.append(randint(1,99)) print filter(odd, allNums)
B.
from random import randint allNums = [] for eachNum in range(9): allNums.append(randint(1,99)) print filter(lambda n: n%2, allNums) # 将简单的 odd() 函数替换为 lambda
C.
from random import randint allNums = [] for eachNum in range(9): allNums.append(randint(1,99)) print [n for n in allNums if n%2] # 用列表解析来代替 filter()
D .
from random import randint print [n for n in [randint(1,99) for i in range(9)] if n%2] # 继续用列表解析来替代中间变量
6.2.3 map()函数
A.简单的map()函数有一个函数和一个序列,将函数作用在序列的每个元素上,然后创建每个返回值组成的列表。
>>> map(lambda x: x+2, [0, 1, 2, 3, 4, 5]) [2, 3, 4, 5, 6, 7] >>> map(lambda x: x**2, range(6)) [0, 1, 4, 9, 16, 25]
B.一般的 map() 可以接受多个序列,这种情况下,map() 并行的迭代每个序列,即将每个序列的第一个元素捆绑到一个元组中,对元组调用给定的函数,并将函数返回值合并到一个序列上。
>>> map(lambda x, y: x + y, [1, 3, 5], [2, 4, 6]) [3, 7, 11] >>> map(lambda x, y: (x+y, x-y), [1, 3, 5], [2, 4, 6]) [(3, -1), (7, -1), (11, -1)] >>> >>> map(None, [1, 3, 5], [2, 4, 6]) # 等价于 zip() [(1, 2), (3, 4), (5, 6)]
6.2.4 reduce()函数
reduce() 函数使用一个二元函数、一个序列和一个可选的初始化器。它取出序列的头两个元素(未给定初始化器的情况下;若给定初始化器,则取出序列的第一个元素和初始化器组合)调用二元函数,获得单一的返回值继续和序列的下一个元素一起调用二元函数,直到遍历整个序列得出最后的值,返回这个值
例子:求一个列表所有值的和
def mySum(x, y): return x+y allNums = range(5) total = 0 for eachNum in allNums: total = mySum(total, eachNum) # 利用循环求得 print 'the total is:', total
输出
the total is 10
利用 reduce() 结合lambda
print reduce(lambda x, y: x+y, range(5))
输出
the total is 10
6.3 偏函数
6.3.1 简单例子
通过以下简单例子来理解 PFA(偏函数应用)
>>> from operator import add, mul >>> from functools import partial >>> add1 = partial(add, 1) # add1(x) == add(1, x) >>> mul100 = partial(mul, 100) # mul100(x) == mul(100, x) >>> >>> add1(12) 13 >>> mul100(100) 10000 >>> mul100(500) 50000 >>> baseThree = partial(int, base=3) # baseThree(str) == int(str, 3) >>> baseThree('121212') 455
6.3.2 注意关键字参数
因为固定参数总是放在运行参数的左边,所以若不注意关键字参数问题,很容易导致异常
>>> baseTwoBad = partial(int,2) # baseTwoBad(x) == int(2,x) >>> baseTwoBad('100100') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: an integer is required
6.3.3 GUI类例子
#!/usr/bin/env python from functools import partial import Tkinter root = Tkinter.Tk() MyButton = partial(Tkinter.Button, root, fg='white', bg='blue') b1 = MyButton(text='Button 1') b2 = MyButton(text='Button 2') qb = MyButton(text='QUIT', bg='red', command=root.quit) b1.pack() b2.pack() qb.pack(fill=Tkinter.X, expand=True) root.title('PFAs!') root.mainloop()
7.变量作用域
7.1 全局变量与局部变量
定义在函数内的变量有局部作用域,在一个模块中最高级别的变量有全局作用域
>>> global_str = 'foo' >>> def foo(): ... local_str = 'bar' ... return global_str + local_str ... >>> foo() 'foobar' >>> global_str 'foo' >>> local_str Traceback (most recent call list): File "<stdin>", line 1, in <module> NameError: name 'local_str' is not defined
上例中,global_str 是全局变量,local_str 是局部变量。foo() 函数可以访问全局变量和局部变量,代码主题部分则只能访问全局变量
7.2 global 语句
def foo(): print "\ncalling foo()..." bar = 200 print "in foo(), bar is", bar bar = 100 print "in __main__, bar is", bar foo() print "\nin __main__, bar is (still)", bar
代码输出:
in __main__, bar is 100 calling foo()... in foo(), bar is 200 in __main__, bar is (still) 100
上例表明,在函数体内的全局变量,会被函数体内定义的局部变量覆盖掉。为了明确的引用已命名的全局变量,就需要使用global 语句
>>> is_this_global = 'xyz' >>> def foo(): ... global is_this_global # global var1,var2... ... this_is_local = 'abc' ... is_this_global = 'def' ... print this_is_local + is_this_global ... >>> foo() abcdef >>> print is_this_global def