标签:python
第06章 抽象
------
懒惰即美德
假如要计算斐波那契数列(任何一个数是前两数之和的数字序列)
>>> fibs=[0,1] >>> for i in range(8): fibs.append(fibs[-2]+fibs[-1]) #fibs[-2]+fibs[-1]后两位数,append往后添加 #运行后,包含10个斐波那契数列的10个数字是 >>> fibs [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]如果允许用户输入,从而改变计算的值,要如何做呢?
>>> fibs = [0,1] >>> num = input('Enter number here:') Enter number here:10 >>> for i in range(num-2): fibs.append(fibs[-2]+fibs[-1]) >>> fibs [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]如果该程序要经常用到,就应该再抽象一些,如下面:fibs函数就会创建
num = input('How many numbers do you want? ') print fibs(num)
程序应该是非常抽象的,就像"下载页面、计算频率,打印单词频率"一样易懂.
事实上我们现在就能把这段描述翻译成Python
page = download_page() freqs = compute_frequencies(page) for word,freq in freqs: print word, freq
------
创建函数
函数可以调用,它执行某种行为并返回值。
一般来说,内建的callable函数可以判断函数是否可调用:
>>> import math >>> x = 1 >>> y = math.sqrt >>> callable(x) False >>> callable(y) TrueNote:callbale()在Python3.0里面会用hasattr(func.__call__)来代替
创建函数是组织程序的关键,那么如何定义函数呢? 下面就是个最简单的函数
>>> def hello(name): return 'Hello,' + name + '!' >>> hello('Jerry') 'Hello,Jerry!'
像上面的斐波那契要写成函数的话,就方便多了,可以传入任意数字
>>> def fibs(num): result = [0,1] for i in range(num-2): result.append(result[-2]+result[-1]) return result >>> fibs(10) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Note:
1. return 语句非常重要,是用来从函数中返回值的,如果没有的话,返回None
>>> def sum(x,y): result = x + y >>> print sum(1,2) None
2. return 相当于程序中的break.比如说下面的:
def test(): print 'This is 1 line.' return print 'This is 2 line' test() #输出结果 >>>This is 1 line.由此可见: 第二个打印没有显示
记录函数
如果想要函数被别人理解的话,可以用#注释,另外一个直接加上字符串
如果直接放def函数后面的话,会作为函数的一部分,称为文档字符串
>>> def square(x): 'Calculates the square of the number x' return x*x可以用内置的模块__doc__来查看文档
>>> square.__doc__ 'Calculates the square of the number x'最主要,也最常用的是help(),dir()来查看函数相关信息
>>> dir(square) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> help(square) Help on function square in module __main__: square(x) Calculates the square of the number x
------
参数魔法
形参:def 后面的变量就是形式参数,简称形参
实参:调用函数提供的值就是实际参数,简称实参,或叫参数,或叫值.
变量定义在函数体内,为局部变量,定义在函数体内,为全局变量
>>> def try_to_change(n): n = 'Mr, Gumby' >>> name = 'Mrs, Smith' >>> try_to_change(name) #将name作为实参传给try_to_change函数 >>> name #全局变量值不变 'Mrs, Smith'字符串及元祖是不可变的,因此无法被修改,如果是可变的数据结构如列表做为参数呢?
>>> def change(n): n[0] = 'Mr. Gumby' >>> name = ['Mrs. Smith','Mr. Jing'] >>> change(name) >>> name #值已经发生了变化 ['Mr. Gumby', 'Mr. Jing']
这是跟上面的区别所在,这里面的列表发生了改变:
>>> name = ['Mrs. Smith','Mr. Jing'] >>> n = name #模拟传参数 >>> n[0] = 'Mr. Gumby' #改变列表 >>> name ['Mr. Gumby', 'Mr. Jing']如果不想被修改,就得拷贝其副本 n = name[:]
>>> storage={} >>> storage['first']={} >>> storage['middle']={} >>> storage['last']={} >>> storage {'middle': {}, 'last': {}, 'first': {}}#storage这种字典的存储方式,有3个键‘first‘,‘middle‘,‘last‘.
>>> storage['first']['Magus']=[me] >>> storage['middle']['Lei']=[me] >>> storage['last']['Hetland']=[me] >>> storage { 'middle': {'Lei': ['Magnus Lei Hetland']}, 'last': {'Hetland': ['Magnus Lei Hetland']}, 'first': {'Magus': ['Magnus Lei Hetland']} }每个键下面都存储一个以人名的列表。本例中,列表只有我
>>> my_sister='Anne Lei Hetland' >>> storage['first'].setdefault('Anne',[]).append(my_sister) >>> storage['middle'].setdefault('Lei',[]).append(my_sister) >>> storage['last'].setdefault('Hetland',[]).append(my_sister) >>> storage['first']['Anne'] ['Anne Lei Hetland'] >>> storage['middle']['Lei'] ['Magnus Lei Hetland', 'Anne Lei Hetland']如果要写大程序来更新的话,更会显得臃肿不堪.
>>> def init(data): ... data['first']={} ... data['middle'] = {} ... data['last'] = {} ... >>> init(storage) >>> storage {'middle': {}, 'last': {}, 'first': {}}可以看到,函数包办初始化的工作,让程序更易读.
>>> def inc(x): return x+1 ... >>> i = 10 >>> i = inc(i) >>> i 11如果想改变参数的话,有个小技巧,放在列表中.
>>> def inc(x): ... x[0] = x[0] + 1 ... >>> foo = [10] >>> inc(foo) >>> foo [11]这样代码只会返回新值,比较清新。
>>> def hello_1(greeting,name): print "%s,%s!"%(greeting,name) ... >>> def hello_2(name,greeting): print "%s,%s!"%(name,greeting) ...两个代码要实现的功能完全一样,只是参数名字反过来了。
>>> hello_1('hello','world') hello,world! >>> hello_2('hello','world') hello,world!有的时候参数顺序是很难记的,为了让事情简单些,可以提供参数的名字.
>>> hello_1(greeting='Hi', name='Jerry') Hi,Jerry!参数名和值要对应
>>> hello_2(name='Jerry',greeting='Hi') Jerry,Hi!这类参数名提供的参数叫关键字参数。主要作用是明确每个参数的作用,
store('Mr. Smith',10,20,13,5) store(patient='Mr. Smith',hour=10,minutes=20,day=13,month=5)尽管多打了几个字,但一目了然,弄乱了参数的顺序,对程序不会有任何影响。
>>> def hello_3(greeting='Hello',name='World'): ... print '%s,%s!' % (greeting,name) ... >>> hello_3() #如果不加参数的话,就用默认值 Hello,World! >>> hello_3('greeting') #带参数的话,按参数顺序赋值 greeting,World! >>> hello_3('greeting','universe') #按提供的顺序 greeting,universe! # 如果只想提供name,而让greeting默认 >>> hello_3(name='Sherry') Hello,Sherry!位置参数和关键字参数可以联合使用,将位置参数放前面.
>>> def hello_4(name,greeting='Hello',punctuation='!'): ... print '%s, %s%s' % (greeting,name,punctuation) ... >>> hello_4('Jerry') Hello, Jerry! >>> hello_4('Jerry','Howdy') Howdy, Jerry! >>> hello_4('Jerry','Howdy','...') Howdy, Jerry... >>> hello_4('Jerry',punctuation='.') Hello, Jerry. >>> hello_4('Jerry',greeting='Top of the morning to ya') Top of the morning to ya, Jerry! >>> hello_4() Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: hello_4() takes at least 1 argument (0 given)
#如果最后一个name也用默认值的话,就不会产生上面的异常.
------
收集参数
有的时候提供多参数是很有必要的,那么如何做呢? 很简单
>>> def print_parms(*parms): ... print parms ...#1个参数的话,会作为元祖打印出来,里面还有逗号
>>> print_parms('Hello') ('Hello',) >>> print_parms(1,2,3) (1, 2, 3)#parms前面的*号,将所有的参数放到一个元祖里面,然后使用。
>>> def print_parms_2(title,*parms): ... print title ... print parms ... >>> print_parms_2('Parms:',1,2,3) Parms: (1, 2, 3)#在这里,*变成了收集其余的位置参数
>>> print_parms_2('Nothing:') Nothing: ()的确如此,非常有用,那么能不能处理关键字参数呢?
>>> print_parms_2('hmm...',someting=42) Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: print_parms_2() got an unexpected keyword argument 'someting'可以看出,应该不行,那么要如何实现呢? 就要用到"**"
>>> def print_parms_3(**parms): ... print parms ... >>> print_parms_3(x=1,y=2,z=3) {'y': 2, 'x': 1, 'z': 3}#返回的是字典而不是元祖
>>> def print_parms_4(x,y,z=3,*pospar,**keypar): ... print x,y,z ... print pospar ... print keypar ... >>> print_parms_4(1,2,3,5,6,7,foo=1,bar=2) 1 2 3 (5, 6, 7) {'foo': 1, 'bar': 2}
------
反转过程
那么要如何使用*,**呢?
看下面一个简单的例子:
>>> def add(x,y): return x+y ... >>> parms=(1,2) #下面这样会报错 >>> add(parms) Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: add() takes exactly 2 arguments (1 given) #必需这样,前面加个*号 >>> add(*parms) 3#字典方面的调用
>>> def hello_3(greeting='Hello',name='World'): ... print '%s,%s!' % (greeting,name) ... >>> params = {'name':'Sir Robin','greeting':'Well met'} >>> hello_3(**params) Well met,Sir Robin!#再看下面的,看加双*和没加**
>>> def with_star(**kwd): ... print kwd['name'],'is',kwd['age'],'years old!' ... >>> def without_star(kwd): ... print kwd['name'],'is',kwd['age'],'years old!' ... >>> args = {'name':'Mr. Gumby','age':35} >>> with_star(**args) Mr. Gumby is 35 years old! >>> without_star(args) Mr. Gumby is 35 years old!#可以看出两者情形一样,所以*只在定义函数(允许不定数目的参数)
和调用(分割字典或序列)才有用.
Note:
使用拼接(Splicing)操作符传递参数很有用,因为不用担心参数的个数
>>> def foo(x,y,z,m=0,n=0): ... print x,y,z,m,n ... >>> def call_foo(*arg,**kwds): ... print 'Calling foo' ... foo(*arg,**kwds)------
练习使用参数:
def story(**kwds): return 'Once upon a time. There was a ' '%(job)s called %(name)s.' % kwds def power(x,y,*others): if others: print 'Received redundant parameters:', others return pow(x,y) def interval(start,stop=None,step=1): 'Imitates range() for step>0' if stop is None: start,stop = 0,start result = [] i = start while i < stop: result.append(i) i +=step return result print story(job='king',name='Gumby') print story(name='Jerry',job='king') params = {'job':'language','name':'Python'} print story(**params) del params['job'] print story(job='stroke of genius',**params) print power(2,3) print power(3,2) print power(y=3,x=2) params =(5,)*2 print power(*params) print power(2,3,'Hello,World!') print interval(10) print interval(1,5) print interval(3,12,4) print power(*interval(3,7))输出结果:
D:\>python
Python.py
Once upon a time. There was a king called Gumby. Once upon a time. There was a king called Jerry. Once upon a time. There was a language called Python. Once upon a time. There was a stroke of genius called Python. 8 9 8 3125 Received redundant parameters: ('Hello,World!',) 8 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [1, 2, 3, 4] [3, 7, 11] Received redundant parameters: (5, 6) 81
------
作用域
>>> x = 1 >>> scope = vars() >>> scope['x'] 1 >>> scope['x'] +=1 >>> x 2#只在函数体内有效
>>> def foo(): x = 42 ... >>> x = 1 >>> foo() >>> x 1 >>> def output(x): print x ... >>> x = 1 >>> y = 2 >>> output(y) 2#函数体内使用外部变量
>>> def combine(param): print param+external ... >>> external = 'berry' >>> combine('Shrub') ShrubberryWARN:像这样引用变量是很多错误的原因:
假如局部变量跟全局变量重名该如何做呢?
>>> def comb(param): ... print param + globals()['param'] ... >>> param='Sherry' >>> comb('Jerry->') Jerry->Sherry那么如何更改全局变量呢?
>>> x = 1 >>> def change_global(): ... global x ... x +=1 ... >>> x 1 >>> change_global() >>> x 2
------
嵌套作用域: 一个函数嵌套在另一个函数里面
>>> def foo(): ... def bar(): ... print 'Hello,World!' ... bar() ... >>> foo() Hello,World!
>>> def multiplier(factor): ... def multiplyByFactor(number): ... return number*factor ... return multiplyByFactor ... >>> double = multiplier(2) >>> double(5) 10 >>> triple = multiplier(3) >>> triple(3) 9 >>> multiplier(5)(4) 20#再来看一个
>>> def A(x): def B(y): def C(z): return x+y+z return C return B >>> A(1)(2)(3) 6------
------
------
------
<<Python基础教程>>学习笔记 | 第06章 | 抽象
标签:python
原文地址:http://blog.csdn.net/jerry_1126/article/details/39447355