函数
为什么要使用函数
1、降低编程难度:通常将一个复杂的大问题分解成一系列的小问题,然后将小问题划分成更小的问题,当问题细化为足够简单时,我们就可以分而治之,各个小问题解决了,大问题就迎刃而解了。
2、代码重用:避免重复劳作,提供效率
函数的基本使用
在Python中,定义一个函数要使用def
语句,依次写出函数名、括号、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用return
语句返回。
我们以自定义一个求绝对值的函数为例:
def jueduizhi(x):
if x >= 0:
return x
else:
return -x
函数体内部的语句在执行时,一旦执行到return
时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。
如果没有return
语句,函数执行完毕后也会返回结果,只是结果为None
。return None
可以简写为return
。
调用函数,那么只需要使用函数名加括号,就能执行,但如果函数定义了参数,那么必须在执行的时候传递参数给函授,否则会报异常!
jueduizhi(-10)
函数的参数
默认参数
可变参数(参数组)
可变关键字参数
命名关键字参数
def func(name,age,*,city,job):
print(name,age,city,job)
>>> func('daxin',22,city='beijing',job='linux')
daxin 22 beijing linux
函数的返回值
用return的方式取得proc下的进程号
#!/usr/bin/env python
import os
PidFile = os.listdir('/proc')
def isPid(pid):
if pid.isdigit():
return True
return False
for i in PidFile:
if isPid(i):
print i,
注意:在函数中我们一般不会对结果进行打印,而是使用return把结果返回给调用它的地方。
函数的变量
三元运算符和匿名函数
if 1 > 3: print 'lt' else: print 'gt'
print 'lt' if 1>3 else 'gt'
解读:
如果 1大于3,那么返回lt,否则返回gt。这样写起来既简单,逼格又高。
匿名函数
# 常规版本:
def fun(x,y)
return x*y
# lambda版本:
r = lambda x,y:x*y
print(r(1,2))
lambda 参数列表:[表达式]
由于lambda返回的是函数对象(构建的是一个函数对象),所以可以定义一个变量去接收
# 搭配reduce的应用
# reduce(fun,list)
# reduce依次把list中的数字赋值给fun进行计算
# 注意fun必须是连个数字间的运算,list必须是整型数字
# 例:求1到100的累加
reduce (lambda x,y:x + y,xrange(1,101)
# 应用:
ret = (lambda x,y:x+y)(2,3)
print(ret)
Python内置函数
abs() # 计算绝对值,abs(-10),接收number,返回一个number
max() # 求序列的的最大值(可迭代的),同时也可以比较一些数字
min() # 求序列的最小值(可迭代的),同时也可以比较一些数字
len() # 求序列的长度,(字典也可以,求index的个数)
divmod(x,y) # 求x,y的商和余数,存放在元组中
pow(x,y,z) # 求x的y次方,如果有第三个参数,那么把前面的结果和第三个参数求余
round(number) # 用来四折五入,后面可以跟精度(先把number编程浮点数,然后看保留几位)把12.45转换成float,那么就不是12.45了,而可能是12.4999999了
callable() # 判断一个对象是否是可调用的,比如函数,类都是可以调用的,但是变量无法调用
type() # 查看对象的类型
isinstance(对象,类型) # 判断一个对象是否是给定类型。类型可以是多个,只要是其中1个即可(类型1,类型2)
cmp(x,y) # 比较两个对象的大小,x大于y返回1,x等于y返回-1,x小于y返回-1,字符串也可以比较,是按单个字符的大小开始比对。
str() # 可以把任何数据转换成字符串(字典,元组,也会直接输出)
hex(10) # 把10进制转换为8进制
eval() # 把字符串当做一个有效的表达式来求值
# eval('1+2') 返回3
# eval("[1,2,3]") 返回[1,2,3]
oct() # 把10进制转换为8进制
chr(i) # i的范围是0~255,求i对应的acsii码值
ord(i) # i的范围是ascii的值,求对应的值
filter(函数或空,序列) # 用来过滤,把序列的每一个元素,交给函数来处理。如果为None,则什么也不做,直接输出(序列),如果有函数,那么只有函数的返回值为True才打印序列中的元素
例子:
def even(n):
if n % 2 == 0:
return True
filter(even,xrange(10))
[0, 2, 4, 6, 8]
高级写法:
filter(lambda x: x%2 ==0,xrange(10)
zip(seq1,seq2,seqN...) # 把seq1和seq2组合成1个大的列表,每个序列取1个组成元组,存放在整合后的列表中,如果序列的长度不同,那么就以最小的序列的长度为基准。
map(func,seq1,seq2) # 返回也是一个列表,func可以是一个函数也可以是个None,如果为None,那么返回的列表长度以当前最长列表为基准,依旧是依次去每个列表中去取,只不过没有的,用none补充
例子:
l1 = [1, 2, 3]
l2 = ['a', 'b', 'c', 'd']
l3 = ['I', 'II', 'III', 'IV', 'V', 'VI']
map(None,l1,l2,l3)
[(1, 'a', 'I'),
(2, 'b', 'II'),
(3, 'c', 'III'),
(None, 'd', 'IV'),
(None, None, 'V'),
(None, None, 'VI')]
# 如果函数位上是一个函数,那么如果只有1列表,那么就需要这个函数定义1个参数,然后每次把列表中的1个元素交给函数进行处理,并打印,或者保存在迭代器中去
# 如果需要处理两个序列,那么函数就必须定义2个参数,去接受这两个序列的元素。
# 高级应用:
map(lambda x,y:x+y,l1,l4)
reduce(func,seq) # 只能定义1个序列,然后传递给func进行处理。(lambda函数求累加)
函数高级
函数对象
函数是第一类对象,可以当作数据进行传递,具有如下特点
1、可以被引用
def func():
print('from func')
f = func #把函数地址当作变量一样进行传递
f()
2、可以当作参数传递
def func():
print('from func')
def wapper(func):
print('from wapper',func)
wapper(func) #把函数的地址作为变量传递给另一个函数
注意:把函数作为参数传入,这样的函数称为高阶函数.
3、返回值可以是函数
def func():
print('from func')
def wapper(func):
return func
f = wapper(func) #把函数的内存地址传递,并返回,用f接收那么f就等于func的内存地址,那么加上括号就可以运行
f()
4、可以当作容器类型的元素
def func():
print('from func')
func_dict = {'func':func} #直接作为字典的值存储,那么调用函数就可以用 func_dict['func']() 直接进行调用了
函数嵌套
函数的嵌套主要分为嵌套调用,以及嵌套定义。
1、函数的嵌套调用
def max2(a,b): #判断两个变量的最大值
return a if a > b else b
def max4(a,b,c,d): #判断四个变量的最大值
res1 = max2(a,b) #函数的嵌套调用
res2 = max2(res1,c)
res3 = max(res2,d)
print(res3)
max4(10,100,21,99)
2、函数的嵌套定义
def func1():
print('from func1')
def func2():
print('from func2')
def func3():
print('from func3')
func3() # 只有在func2中才能调用内部定义的函数func3
func2()
func1()
注意:在函数的内部定义函数,所以内部函数的作用于就在外部函数内,在其他地方就无法进行调用
列表生成式
通过简洁的语法可以生成一个列表,或者对一组元素进行过滤,还可以对元素进行转换处理的式子。# 语法格式: [ exp for val in collection if condition ]
[ i for i in range(1,11) if i % 2 == 0 ]
# 列表生成式打印 1-10 中的偶数
1 def func(): 2 a = [] 3 for i in range(1,11): 4 if i % 2 == 0 : 5 a.append(i) 6 return a 7 8 9 print(func())
通过列表生成式,我们可以直接创建一个列表,但是收到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素是可以按照某周算法推算出来,那我们是否可以在循环过程中不断的推算处后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,成为生成器(generator)。
生成器
# 修改列表生成式,创建生成器
a = ( i for i in range(1,11) if i % 2 == 0 )
# 使用yield关键字创建 生成器
def func():
for i in range(1,11):
if i % 2 == 0 :
yield i
包含yield语句的函数会被Python解释器翻译为生成器。当函数被调用时,返回的是一个生成器对象,这个对象支持迭代器借口,每当遇到yield关键字的时候,你可以理解成函数的return语句,yield后面的值,就是返回的值,但不是想一般的函数在return后退出函数,生成器函数在生成值后会自动刮起并记录当前执行的状态,在函数恢复时会从上一次yield语句的后面继续执行后面的语句,直到遇到下一个yield语句。
区别:
- 一个直接返回了表达式的结果列表,而另一个是一个对象,该对象包含了对表达式结果的计算引用,通过循环可以直接输出。
- 生成器不会一次性列出所有的数据,当你用到的时候,在列出来,更加节约内存。
- 如果想要列出所有的数据,可以使用for循环,或者直接使用list(),来创建。
迭代器
重复的过程为迭代,每次重复既一次迭代,并且每次迭代的结果是下一次迭代的初始值,为什么要用迭代器? 使没有索引的数据类型,依然能够通过循环,迭代去获取数据的方式.
可迭代对象
可迭代的对象,具有 __iter__ 方法的对象,而执行 __iter__ 方法,那么对象就变成了迭代器。迭代器没有索引,只能通过 __next__ 方法来获取元素,每执行一次,获取一个元素,当迭代器被获取完毕,__next__ 会抛出异常:StopIteration,表示没有值了
例子:
迭代一个字典,对于字典进行迭代,那么默认只能获取字典的key,但是可以通过其他方法来获取字典的value
dic = {'a':1,'b':2,'c':3}
d = dic.__iter__()
while True:
try:
key = d.__next__() #获取字典的key
print(dic[key]) #通过key获取value
except StopIteration:
break
扩展:
1)既然一个对象的长度可以用len,或者用对象的 __len__()方法,其实执行len(对象),其实就是在执行对象的 __len__ 方法
2)那么把一个对象转换为迭代器,不仅仅可以用对象的 __iter__(),也可以使用iter(对象),来进行转换
3)获取迭代器的元素,不仅仅可以用 __next__(),也可以用next(对象)
迭代器的特点
1、如何判断对象是可迭代对象,还是迭代器对象
from collections import Iterable,iterator
print(isinstance('abc',Iterable))
# collections里面放着python内置的扩展的数据类型
# 通过导入特殊的数据类型来进行判断
2、可迭代的数据类型
只要对象含有__iter__方法,执行__iter__方法,可以得到迭代器对象,列表,元祖,字典,字符串,集合,文件描述符 (可以对可迭代对象执行__iter__方法来变成迭代器)
3、迭代器协议
1)对象有__next__,__iter__方法称为迭代器
2)对于迭代器对象来说,执行__iter__得到的结果,仍然是它本身。(for循环有关)
4、for循环原理
1)遵循迭代器协议,执行对象的__iter__方法,然后利用__next__(next)去获取元素
2)并且会帮我们捕捉异常,并且进行break
3)所以for循环的对象必须具有__iter__方法(可迭代对象变成了迭代器,而迭代器执行__iter__还是迭代器)
4)for循环又称为迭代循环
5、迭代器的优点和缺点
优点:
1)提供了一种不依赖下标的迭代方式(非序列对象可以被迭代)
2)就迭代器本身来说,更节省内存(只有对迭代器进行next方法,才会读取下一个值)
缺点:
1)迭代器只能被迭代一次,既一次性的,不如序列类型灵活
2)只能往后取值,不能往前倒退
3)无法获取迭代器对象的长度
装饰器
如果我们想在一个已经上了线的项目上的某个函数上添加一个功能,但是又不希望更改函数的源代码,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印函数运行时间的decorator。
import time
def timer(func):
def wapper():
start_time = time.time()
res = func()
end_time = time.time()
return res,(end_time - start_time)
return wapper
@timer # 在被装饰函数上添加 Python 特有的语法棒棒糖表示,装饰后面的函数
def index():
time.sleep(3)
return 'hello index'
print(index())
@timer:表示 index = timer(index) , 其实就是把原来的index函数给偷偷替换了。
这个时候会出现一个问题,我们都知道使用func.__name__会打印当前函数的名称,而虽然我们偷偷替换了index函数,但是在打印func.__name__的时候,还是会打印wapper,那是因为我们真正执行的是wapper函数,那么有些依赖函数签名的代码执行就会出错。
所以这里在wapper函数上添加Python内置装饰器,来继承该属性
import time
import functools # 需要引入代码包
def timer(func):
@functools.wraps(func) # 继承函数的__name__等属性
def wapper():
start_time = time.time()
res = func()
end_time = time.time()
return res,( end_time - start_time )
return wapper
@timer
def index():
time.sleep(3)
return 'hello index'
print(index.__name__)