本章内容
1、函数
2、形参、实参
3、全局与局部变量
4、默认、位置、关键字参数
5、函数的递归
6、函数的嵌套
7、闭包函数
8、可变参数
9、函数的用法
10、匿名函数lambda
11、闭包函数
12、装饰器
13、高阶函数
14、列表生成式
15、生成器
16、迭代器
17、python内置函数
函数
使用函数的三大优点:
1、代码重用
2、保持一致性
3、可扩展性
return返回值:
1、当一个函数没有使用return显示定义的返回值时,python解释器会隐式的返回None
2、return一个值时,返回的是个object
3、ruturn多个值时,返回的是个tuple,里面的值可以是int、tuple、list、dict
函数是第一类对象的概念
1、函数可以被引用
f1 = func
f1() #f1可以被执行
2、函数可以为参数,
3、函数的返回值可以是参数
4、函数可以作为容器类型元素,
cmd = {‘n’:func1,'b':func2}
形参、实参
def hello(a,b): #这里的a,b属于形参
print 'hello,a,b'
a= 'python
b = 'jim'
hello(a,b) #这里的的a,b属于实参
形参:只有在被调用的时候才会分配内存,调用结束后,立即释放内存,值仅在函数内部有效(局部变量)
实参:有确定的值的参数,所有的数据类型都可以被当做参数传递给函数。可以向函数内传递任意的数据类型。
python解释器有自己的内存回收机制,a='hello' ,在内存中开辟了一个内存空间,a 变量去引用这个内存的空间,相当于a是这快地址的门牌号,如果解释器看到这个内存空间没有门牌号的话,这个内存空间则会被释放。
全局与局部变量
在子程序中定义的变量成为局部变量,在程序的一开始定义的变量成为全局变量。全局变量的作用于整个程序,局部变量的作用于是定义该变量的子程序,当全局变量与局部变量同名时,在定义局部变量的子程序内,局部变量起作用,在其他地方全局变量起作用。
局部变量:作用域只在当前的函数内部,外部变量默认不能被函数内部修改的,只能引用,如果想在函数内部修改全区变量,必须golbal,但是强烈建议不要这么用,调试不方便。
上面说的不能修改的的变量类型是int str ,但是函数内部是可以修改列表,字典,集合,实例(class)
下列说明,变量为列表类型时不引用golbal就可以被修改。
names = ['jim','rose','jack'] def chang_name(): names[0] = '吉姆' print(names) chang_name() print(names) 运行结果: ['吉姆', 'rose', 'jack'] ['吉姆', 'rose', 'jack']
函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:>>> a = abs # 变量a指向abs函数
>>> a(-1) # 所以也可以通过a调用abs函数
默认、位置、关键字参数
默认参数
def hello(name='world',age=19): print 'my name is %s,and I am %s years old'%(name,age) hello(name='Mr.python') ------------------------------ my name is Mr.python,and I am 19 years old 注:age我没有指定,则使用默认的值,19了,这个age就是设定了默认参数
位置参数
def hello(name='NB',age=20): #参数的默认值,调用时不声明,则使用默认参数 print 'my name is %s,%s years old'%(name,age) print hello() print hello('mypython',10) #这里面name和age对应的位置是不可以变动的。
关键字参数
def hello(name,age): print 'my name is %s,%s years old'%(name,age) print hello(age=1000,name='mypython') #这里可以通过关键字参数来随意的变更位置。
位置摆放:
默认参数,必须放在位置参数的后面
关键字参数,同上
小练习:
输入一个数,计算阶乘
def jiecheng(number): for i in range(1,(number+1)): sum = i * sum return sum number = raw_input('please input a number')
请注意,函数体内部的语句在执行时,一旦执行到
内部通过条件判断和循环可以实现非常复杂的逻辑。如果没有
return
时,函数就执行完毕,并将结果返回。因此,函数return
语句,函数执行完毕后也会返回结果,只是结果为None
。return 只返回一个值,如果有多个结果的话那会已(a,b.......)元组的形式返回
函数的递归
尾递归是指,在函数返回的时候,调用自身本身
利用递归的方式来计算阶乘。 就是函数自己调用自己。
def jiecheng(number): if number ==1: return 1 return number*jiecheng(number-1) #其原理就是自己乘以自己上一个数的阶乘。 print jiecheng(4)
小点:当明确的结束条件时,循环到999次时就会报错,不然一直跑下去会吃死内存。
递归特性:
1.必须有个明确的结束条件:
2.每次进入更深一层递归时,问题规模相比上次递归都应该减少
3.递归的效率不高
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出
这里有个递归的小练习,二分查找法,详见python小练习:
http://www.cnblogs.com/nopnog/p/6927870.html
空函数
如果想定义一个什么事也不做的空函数,可以用pass
语句:
def nop():
pass
pass
语句什么都不做,那有什么用?实际上pass
可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass
,让代码能运行起来。
pass
还可以用在其他语句里,比如:
if age >= 18:
pass
缺少了pass
,代码运行就会有语法错误。
函数的嵌套
分为两种:
1、嵌套定义:
小丽:
x = 1
def func1():
def func2():
def func3():
print (x)
return func3
return func2
func1()
嵌套的调用最多不要超过三层,不然的话可读性就会变差。
2、嵌套调用:一个调用另一个
小丽:
if x > y:
return x
else:
return y
def my_max4(a,b,c,d): #划分为最细的情况其实就是两个数的比较
res1 = my_max2(a,b)
res2 = my_max2(res1,c)
res3 = my_max2(res2,d)
return res3
print(my_max4(100,2,-1,5))
闭包函数
下面由嵌套函数引出闭包函数
def func1():
x=1
def func2():
print (x)
return func2
f=func1()
f()
这就是一个闭包函数,没有看出什么可用之处吧,下面做个总结:
1.首先是func2中x变量的查找,只会在它的上一层查找,在函数外如果有x的赋值,也不会影响func2中的x这就相当于x与func2打包在一起了。
2.惰性运算,f=func1()后把func2()给了f,但是f没有运行,只有f+()后才会运行
根据这两个特性没有看到闭包函数的nb之处是吧,继续看小丽。
利用一会学到的爬虫来说明
from urllib.request import urlopen
def page(url):
def get():
return urlopen(url).read
return get
baidu = page('http://baidu.com/')
baidu()
只要一baidu()就会去爬百度的网页了,走到这还是没看出啥来是吧,那再来个python=page('http://python.org') 直接python就会去爬python的网站了,这就相当于个接口,你把python传给别人,别人直接调用就可以,不用关系再去定义python的网址,因为python的网址已经隐藏到函数中了。
话说我随用随掉不可以吗?
def get(url):
return urlopen(url).read
get('http://www.baidu.com')
其实直接get('http://www.baidu.com') 与baidu() 没啥区别,区别在于闭包函数已经把变量(url=www.baidu.com)存在函数中了,什么时候用什么时候加()调用就可以了。
可变参数
*收集的是位置参数,不能收集关键参数,收集到的是元组---此方法名为可变参数。
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple
** 收集的是关键字参数,收集到的是字典 ---又名关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
def hello(name = 'world',*para,**par): print 'my name is %s'%(name) print para print par hello('Mr.python',1,2,3,4,5,6,7,8,haha='haha',nihao='nihao') 结果: my name is Mr.python (1, 2, 3, 4, 5, 6, 7, 8) {'nihao': 'nihao', 'haha': 'haha'} [Finished in 0.1s]
小用法:向函数中的*args传递列表。
def foo(x,y,*args):
print (x)
print(y)
print(args)
l = ['a','b']
foo(1,2,*l) #*l的用法,把列表分成单个的项然后传递给函数,相当于*['a','b'] = *args, 然后把*去掉,args = ['a','b'] 然后传递到函数中,函数再已('a','b')的形式输出
函数的用法
1.函数可以当作参数来使用:
def operate(fn,name): return fn(name) def sayhello(name): return 'hello %s!'%(name) def hehe(name): return 'hehe %s !'%(name) print operate(sayhello,'MR.python') print operate(hehe,'mr.python') ----------------------------------- hello MR.python! hehe mr.python ! [Finished in 0.1s]
2.结合sorted来使用:
arr2 = [('192.168.1.1',2),('192.168.1.2',1),('192.168.1.3',3)] def sort_fn(arg): return arg[1] #取的是列表内元素,各个元组的下表是1的关键字 print sorted(arr2,key=sort_fn) arr3 = [{'name':'python','age':10},{'name':'C++','age':8},{'name':'java','age':9}] def sort_dict(arg): return arg['age'] #取的是列表内元素,各个字典的下表age的关键字的值 print sorted(arr3,key=sort_dict) --------------------------------------------------------------------------- [('192.168.1.2', 1), ('192.168.1.1', 2), ('192.168.1.3', 3)] [{'age': 8, 'name': 'C++'}, {'age': 9, 'name': 'java'}, {'age': 10, 'name': 'python'}] 注释的地方容易理解吗?感觉只能靠记
下面通过写个简单的sorted,来做个练习。
实现了list的排序,但是列表和字典的怎么实现呢?
这里面设计到函数的引用,请查看过程。
匿名函数lambda
下面引入lambda,lambda 只能实现简单的函数
def qu_key1(arr):
return arr['age']
翻译为lambda 就是 lambda arr:arr['age']
下面用lambda来替代。
下面在利用lambda实现list也可以比较的功能。
利用函数的默认值,key=lambda x:x 来实现对list的比较,实际上就是没有是通过lambda什么都没有操作的。
贴上代码:
def my_sort(arr,key=lambda x:x): length = len(arr) for i in range(length-1): for j in range(length-1-i): if key(arr[j]) > key(arr[j+1]): arr[j],arr[j+1] = arr[j+1],arr[j] return arr # def qu_key(arr): # return arr[1] # def qu_key1(arr): # return arr['age'] a = [6,5,4,3,3,2,1] print my_sort(a) arr2 = [('192.168.1.1',2),('192.168.1.2',1),('192.168.1.3',3)] print my_sort(arr2,key=lambda x:x[1]) arr3 = [{'name':'python','age':10},{'name':'C++','age':8},{'name':'java','age':9}] print sorted(arr3,key=lambda x:x['age']) -------------------------------------------------------------------------- [1, 2, 3, 3, 4, 5, 6] [('192.168.1.2', 1), ('192.168.1.1', 2), ('192.168.1.3', 3)] [{'age': 8, 'name': 'C++'}, {'age': 9, 'name': 'java'}, {'age': 10, 'name': 'python'}]
看到上面的代码是不是有疑惑,那lambda是什么呢?先来个简单的例子,首先得明确一点lambda其实就是个函数:
g= lambda x:x+1 print g(1) >>2 当然,你也可以这样使用: lambda x:x+1(1) >>>2
可以这样认为,lambda作为一个表达式,定义了一个匿名函数,上例的代码x为入口参数,x+1为函数体,用函数来表示为:
1 def g(x):
2 return x+1
lambda 也可以多参数:
lambda x,y:x+y
非常容易理解,在这里lambda简化了函数定义的书写形式。是代码更为简洁,但是使用函数的定义方式更为直观,易理解。
lambda if 判断运算:
for i in map(lambda x:x*2 if x>5 else x-1 [1,2,3,4,5,6,7,8,9]):
注:lambda支持到最复杂的运算是三元运算,更复杂的运算不支持。
装饰器
装饰器:本质上是个函数,(装饰其他的函数)就是为了其他函数添加附加的功能。
1.最好不要修改源代码,有课能用的地方很多,错一点,会有连带反应。
2.不要更改调用的方式。
这不能动那不能动,想添加功能咋办?用装饰器喽。
原始装饰器:
标准格式:
def zhuangsq(fn): def new_hello(name): print 'good morning ' + name return 'haha ' + fn(name) return new_hello #装饰起代码必须在上面,用@zhuangsq替代了上面的 hello = zhuangsq(hello) @zhuangsq def hello(name): return 'nice to meet you ' + name print hello('MR.python')
哈哈,到了这里有一个参数或者不传递参数的装饰器都已经搞定了,但是如果有多个参数呢,再如果加上加上装饰器后再调用hello() 其实就是调用的new_hello()这个函数了,位置参数呢,哪有如何接受呢?别怕在new_hello()中用*args,**kwargs接受,但是接受了是个元组和字典啊,如何才能让内部的fn函数接受呢?哈哈,还是用*args,**kwargs,这里就会他元组和字典里的这些想
全部独立的分开。
效果如下:
new_hello(*arge,**kwargs):
print 'good morning' + name
return 'haha' + fn(*args,**kwargs)
如果在hello()函数中有return呢?现在我们知道装饰后其实就是运行的new_hello,这时候咋办new_hello中也需要return,那return什么?return fn这个函数。其实也就是hello()
装饰器之return 小例:
1 user,passwd = 'alex','12345' 2 3 def auth(func): 4 def wrapper(*args,**kwargs): 5 username = input('username:').strip() 6 password = input('password:').strip() 7 8 if user == username and passwd == password: 9 print('