6.1 懒惰即美德
斐波那契数列:
>>> fabs = [0, 1]
>>> for i in range(8):
fabs.append(fabs[-1] + fabs[-2])
>>> fabs
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
6.2 抽象和结构
6.3 创建函数
使用def语句创建函数
>>> def fibs(num):
result = [0, 1]
for i in range(num - 2):
result.append(result[-1] + result[-2])
return result
>>> fibs(10)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
6.3.1 文档化函数
6.3.2 并非真正函数的函数
函数如果没有返回值的时候,默认函数None
6.4 参数魔法
6.4.1 值从哪里来
函数的形参
6.4.2 我能改变 参数吗
当在函数内部把参数重绑定的时候,函数外部的变量不受影响。
字符串(以及数字和元组)是不可变的。即无法修改(只能用新的值覆盖)。
可是,将可变的数据结构如列表当做参数时会发生什么?
>>> def change(n):
n[0] = 'Mr. Gumby'
>>> names = ['1','2']
>>> change(names)
>>> names
['Mr. Gumby', '2']
在上述列子中,列表发生变化,这是和前面的区别:当两个变量引用一个列表的时候,他们确实引用同一个列表。
如何避免上述情况呢?可以复制一个列表的副本:
>>> names = ['1','2','3']
>>> n = names[:] // 此时n和names是两个独立的列表,互相之间不影响。
>>> n == names
True
>>> n is names
False
(1)为什么要修改参数: 传递的参数是列表的话可以修改值。
(2)如果是元组,字符串或数字是不可以改变值的。可以采用返回值的方式来改变函数外面的实参的值。或者把实参改成元组。
6.4.3 关键字参数和默认值
>>> def hello_l(greeting, name):
print('%s, %s' % (greeting, name))
>>> hello_l("hello", 'world')
hello, world
>>> def hello_2(greeting = "hello", name = "world"): // 指定参数的默认值
print('%s %s' % (greeting, name))
>>> hello_2(name= "wrold", greeting="hello") // 调用的时候可以指定关键字,不怕顺序颠倒
hello wrold
6.4.4 收集参数
让用户提供任意数量的参数。
>>> def print_pa(*pa):
print(pa)
>>> print_pa(1,2,3,4)
(1, 2, 3, 4)
/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*
>>> def print_(title, *pa): // 星号的意思就是收集剩余的参数
print(title, pa)
>>> print_("fdsfsd", "a","b")
fdsfsd ('a', 'b') // 单个星号返回的是元组
>>> def print_2(**pa): // 两个星号表示可以提供关键字参数
print(pa)
>>> print_2(x = 1, y = 2, z = 3)
{'x': 1, 'y': 2, 'z': 3} // 双星号返回的是字典
>>> def print_3(x, y, z=3, *p1, **p2): // 混合使用各种参数类型
print(x, y, z)
print(p1)
print(p2)
>>> print_3(1,2,3,5,6,7,foo=1,bar=2)
1 2 3
(5, 6, 7)
{'foo': 1, 'bar': 2}
6.4.5 参数收集的逆过程
>>> def add(x,y):
return x + y
>>> params = (1,2)
>>> add(*params)
3
6.4.6 练习使用参数
有很多提供和接收参数的方法,要勤练习
总的来说,除去普通的normal args,python中比较特别的函数参数有:关键字参数、默认参数、非关键字可变长参数(元组)、关键字可变长参数(字典),下面将结合例子进行介绍。
关键字参数:调用时指定参数的名称,且与函数声明时的参数名称一致。使用关键字参数允许函数调用时参数的顺序与声明时不一致,仅根据参数的指定进行赋值。例:
# 函数定义 def foo(x, y): print 'x is %s' % x print 'y is %s' % y if __name__ == '__main__': # 标准调用 foo(1, 2) # 关键字调用 foo(y = 1, x = 2)
在标准调用中,x和y依次为1和2;在关键字调用中,x和y的值根据名字指定而与顺序无关。
# 标准调用输出 x is 1 y is 2 # 关键字调用输出 x is 2 y is 1
默认参数:在函数声明时,指定形参的默认值,调用时可不传入改参数(使用默认值)。例:
def tax(cost, rate = 0.17): print cost * (1 + rate) if __name__ == '__main__': # rate使用默认值0.17 tax(1000) # rate指定为0.05 tax(1000, 0.05)
使用默认值时,rate为0.17,结果为1170;在指定参数rate时,rate为0.05,结果为1050。
# tax(1000)的输出 1170.0 # tax(1000, 0.05)的输出 1050.0
非关键字可变长参数(元组):“非关键字”“可变长”顾名思义是允许在调用时传入多个“非关键字”参数,python会将这些多出来的参数放入一个元组中。例:
# 定义函数,其中*theRest为非关键字可变长参数 def tupleVarArgs(arg1, arg2='defaultB', *theRest): print 'formal arg 1: ', arg1 print 'formal arg 2: ', arg2 for eachXtrArg in theRest: print 'another arg: ', eachXtrArg
我们采用多种调用方式来查看结果,从而理解非关键字可变长参数的使用:
>>> tupleVarArgs('abc') formal arg 1: abc formal arg 2: defaultB
>>> tupleVarArgs('abc', 'def') formal arg 1: abc formal arg 2: def
>>> tupleVarArgs('abc', 'def', 'xyz', 123.4) formal arg 1: abc formal arg 2: def another arg: xyz another arg: 123.4
关键字可变长参数(字典):“关键字”“可变长”顾名思义是允许在调用时传入多个“关键字”参数,python会将这些多出来的<参数名, 参数值>放入一个字典中。需要注意的是,关键字变量参数应该为函数定义的最后一个参数,带**。例:
# 定义函数,其中**theRest为关键字可变长参数 def dictVarArgs(arg1, arg2='defaultB', **theRest): print 'formal arg 1: ', arg1 print 'formal arg 2: ', arg2 for eachXtrArg in theRest.keys(): print 'Xtra arg %s: %s' % (eachXtrArg, str(theRest[eachXtrArg]))
我们采用多种调用方式来查看结果,从而理解关键字可变长参数的使用:
>>> dictVarArgs('abc') formal arg 1: abc formal arg 2: defaultB >>> dictVarArgs('abc', 'def') formal arg 1: abc formal arg 2: def # a=1和b='aha'即为关键字可变参数,他们会被放入theRest字典 >>> dictVarArgs('abc', 'def', a=1, b='aha') formal arg 1: abc formal arg 2: def Xtra arg a: 1 Xtra arg b: aha # 全部采用关键字参数,但只有a和b是可变的,会被放入theRest >>> dictVarArgs(arg2='def', a=1, b='aha', arg1='put at the last') formal arg 1: put at the last formal arg 2: def Xtra arg a: 1 Xtra arg b: aha # 这次使用了arg2的默认值 >>> dictVarArgs('one', a=1, b='aha', name=('yuki', 'lau')) formal arg 1: one formal arg 2: defaultB Xtra arg a: 1 Xtra arg b: aha Xtra arg name: ('yuki', 'lau')
注意
当非关键字可变长参数和关键字可变长参数出现在同一个函数中时,他们应当遵守如下的顺序约定:
def newfoo(normal_arg1, normal_arg2, *non-keywords, ** keywords): pass
当然,你也可以直接向函数中传入元组和字典对象,如:
>>> newfoo(2, 4, *(6, 8), **{'foo': 10, 'bar': 12})
也可以来个“混搭”,孤立的可变长参数将会被放入对应的tuple或dict中,如:
>>> newfoo(2, 4, 3, x=4, y=5, *aTuple, **aDict)
在这个例子中,参数3会被放入aTuple,参数x=4和y=5会被放入aDict
当然关于Python函数的参数问题还有很多,本文仅仅介绍几种常用的情况,仅做个简单的了解和参考。
6.5 作用域
内建的vars()函数可以返回变量和所对应的值的这个字典,这个字典就是命名空间或者作用域
>>> x = 1
>>> y = 250
>>> vars()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'x': 1, 'y': 250}
>>> scope = vars()
>>> scope['x']
1
>>> def foo(): x = 42 // 改变的是局部变量,不会改变外部的X
>>> x = 1
>>> foo()
>>> x
1
>>> globals()['x'] // 获取全局变量的内建函数
1
下面看如何在函数内部重新绑定变量的值:方法是 将其声明为全局变量
>>> x = 1
>>> def change_g():
global x
x = x + 1
>>> change_g()
>>> x
2
6.6 递归
6.6.1 两个经典:阶乘和幂
>>> def factoria_(n):
result = n
for i in range(1, n):
result *= i
return result
>>> factoria_(3)
6
改成递归:
>>> def factorial(n):
if n == 1:
return 1;
else:
return n * factorial(n-1)
>>> factorial(4)
24
求x的n次幂:
>>> def power(x, n):
if n == 0:
return 1;
else:
return x*power(x, n-1)
>>> power(3, 2)
9
6.6.2 另外一个经典:二分法查找
def search(sequence, number, lower, upper):
print(lower, upper, seq)
if lower == upper:
assert number == sequence[upper]
return upper
else:
middle = (lower + upper) // 2
if number > sequence[middle]:
return search(sequence, number, middle + 1, upper)
else:
return search(sequence, number, lower, middle)