标签: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