四、函数
1、定义
函数是逻辑结构化与过程化的一种编程方法。 (☆ 函数 return 返回的值需用 print 打印出来。打印函数时,没有return默认返回None)
(过程:没有返回值return的函数)
1 def test(x): 2 ''' 3 4 :param x:整形数字 5 :return:返回计算结果 6 ''' 7 y = 2*x+1 8 return y 9 a = test(1) 10 print(a) 11 12 def:定义函数的关键字 13 test:函数名 14 ():内可定义形参 15 ''' ''':文档描述 16 y = 2*x+1:泛指代码块或程序处理的逻辑 17 return:定义返回值
函数的好处:
- 代码重用
- 保持一致性,已维护
- 可扩展
1 def test1(): 2 msg = 'test01' 3 print(msg) 4 5 def test2(): 6 msg = 'test02' 7 print(msg) 8 return {'name':'Jack'} 9 10 def test3(): 11 msg = 'test03' 12 print(msg) 13 return 1,2,'a',['b'],{'name':'Jack'},None 14 15 t1 = test1() 16 t2 = test2() 17 t3 = test3() 18 print(t1) 19 print(t2) 20 print(t3) 21 22 输出: 23 test01 24 test02 25 test03 26 None 27 {'name': 'Jack'} 28 (1, 2, 'a', ['b'], {'name': 'Jack'}, None)
- 返回值数 = 0:返回None
- 返回值数 = 1:返回object
- 返回指数 > 1:返回tuple
2、函数参数:
- 形参与实参,形参不占用内存空间,被调用时才占用。使用实参时注意对应的形参位置,位置参数需在关键字参数左边。
- 默认参数:
1 例: 2 def test(x,type='Jack'): 3 print(x) 4 print(type) 5 test('Hello') 6 7 输出: 8 Hello 9 Jack
- 参数组:**字典,*列表 ( 可加多个参数)
- 一个 * 表示遍历列表
1 例: 2 def test(x,*args): 3 print(x) 4 print(args) 5 test(1,2,3,4,5) 6 test(1,*(2,3,4,5)) 7 test(1,*{'name':'Jcak','age':18}) 8 9 输出: 10 1 11 (2, 3, 4, 5) 12 1 13 (2, 3, 4, 5) 14 1 15 ('name', 'age')
- 两个 ** 表字典,输入字典
1 例: 2 def test(x,**kwargs): 3 print(x) 4 print(kwargs) 5 test(1,y=1,z= 2) 6 test(1,**{'name':'Jcak','age':18}) 7 8 输出: 9 1 10 {'y': 1, 'z': 2} 11 1 12 {'name': 'Jcak', 'age': 18}
- 总:使用*args,**lwargs
1 例: 2 def test(x,*args,**kwargs): 3 print(x) 4 print(args) 5 print(kwargs,kwargs.get('y')) 6 test(1,1,2,3,4,y=1,z=2) 7 test(1,*(1,2,3,4),**{'y':1,}) 8 9 输出: 10 1 11 (1, 2, 3, 4) 12 {'y': 1, 'z': 2} 1 13 1 14 (1, 2, 3, 4) 15 {'y': 1} 1
- 一个 * 表示遍历列表
3、全局与局部变量:
- 全局变量不进行缩进,局部缩进。加 global 可重新赋值全局变量,有局部变量存在时,需写在局部变量前。
1 例: 2 name = 'Jack' #全局 3 def test(): 4 name = 'Alan' #局部 5 print(name) 6 7 test() 8 print(name) 9 10 输出: 11 Alan 12 Jack
1 例: 2 name = 'Jack' 3 def test(): 4 global name #表示下面修改的name为全局变量 5 name = 'Alan' 6 print(name) 7 8 test() 9 print(name) 10 11 输出: 12 Alan 13 Alan
- 全局变量用大写,局部变量用小写。
- 使用 nonlocal 指定上一层局部变量,不能指定全局变量
1 例: 2 name = 'Jack' 3 def test1(): 4 name = 'Alan' 5 def test2(): 6 nonlocal name # nonlocal 指定上一层变量 7 name = 'John' 8 test2() 9 print(name) 10 print(name) 11 test1() 12 13 输出: 14 Jack 15 John
4、递归函数:
函数直接或间接调用函数本身,使用 return 终止
1 例: 2 def test(n): 3 print(n) 4 if int(n / 2) == 0: 5 return n 6 res = test(int(n / 2)) 7 return res 8 test(10) 9 10 输出: 11 10 12 5 13 2 14 1
- 必须有一个明确的结束条件,不然会一直循环后报错
- 每次进入更胜一成递归时,问题规模相比上一次有所减少
- 递归效率不高,递归层次过多会导致栈溢出
5、函数作用域:
作用域不随调用位置的改变而改变
- return到一个函数时,返回其内存地址
1 例: 2 def test1(): 3 print('in the test1') 4 def test2(): 5 print('in the test2') 6 return test1 7 8 res = test2() 9 print(res) 10 11 输出: 12 in the test2 13 <function test1 at 0x00000065FE5CC268> 14 #返回 test1 函数的内存地址
-
加上()运行函数时,返回函数 test1 ()运行的值,没有 return 结果的默认输出None。
1 例: 2 def test1(): 3 print('in the test1') 4 def test2(): 5 print('in the test2') 6 return test1 7 8 res = test2() 9 print(res()) 10 11 输出: 12 in the test2 13 in the test1 14 None
1 例: 2 def aa(): 3 name = 'test1' 4 def bb(): 5 name = 'test2' 6 def cc(): 7 print(name) 8 return cc 9 return bb 10 11 # bb = aa() 12 # cc = bb() 13 # print(cc) 14 # cc() 15 aa()()() 16 17 输出: 18 test2
6、函数式编程
函数式 = 编程语言定义的函数 + 数学意思的函数
函数式就是用编程语言去实现数学函数。这种函数内对象是永恒不变的,要么参数是函数,要么返回值是函数,没有for和while循环,所有循环由递归去实现,无变量的赋值(即不用变量去保存状态),无赋值即不改变。
面向过程:找到解决问题的入口,按照一个固定的流程去模拟解决问题的流程。
特点:
- 不可变数据
- 第一类对象
- 尾调用优化(尾递归)
不可变:不用变量保存状态,不修该变量
1 #非函数式 2 a = 1 3 #增加的测试函数 4 def incr_test1(): 5 global a 6 a += 1 7 return a 8 9 incr_test1() 10 print(a) 11 12 #函数式 13 n = 1 14 def incr_test2(n): 15 return n+1 16 17 print(incr_test2(2)) 18 print(n)
第一类对象:函数即变量(满足以下两个点的函数称为高阶函数)
- 函数名可以当做参数传递给另一个函数
1 例: 2 def foo(n): 3 print(n) 4 5 def bar(name): 6 print('my name is %s' %name) 7 8 foo(bar('jack')) 9 10 输出: 11 my name is jack 12 None #默认return返回None,所以n=None
- 返回值可以是函数名(给返回的函数赋予一个值,运行后输出。可以return函数本身)
1 例: 2 def foo(): 3 print('from foo') 4 5 def bar(): 6 print('from bar') 7 return foo 8 n = bar() 9 n() 10 11 输出: 12 from bar 13 from foo
尾调用:在函数最后一步调用另一个函数(最后一行不一定是函数的最后一步)
1 #函数bar在foo内为尾调用 2 3 def bar(n): 4 return n 5 def foo(x): 6 return bar(x) 7 8 #函数bar1和bar2在foo内均为尾调用,二者在if判断条件不同的情况下都有可能作为函数的最后一步 9 def bar1(n): 10 return n 11 def bar2(n): 12 return n+1 13 def foo(x): 14 if type(x) is str: 15 return bar1(x) 16 elif type(x) is int: 17 return bar2(x)
非尾调用:
1 #函数bar在foo内为非尾调用 2 def bar(n): 3 return n 4 def foo(x): 5 y = bar(x) 6 return y 7 8 #函数bar在foo内为非尾调用 9 def bar(n): 10 return n 11 def f00(x): 12 return bar(x)+1 13 #res = bar(x) 14 #return res+1
- map函数:(结果为迭代器,只能处理一次,再次处理不显示)依次处理序列中的每个元素,得到的结果是一个‘列表’,该‘列表’元素个数及位置与原来一样(传入两个参数:1、函数 2、iterable(可迭代对象))
def map(func,array)
1 #不使用map函数 2 num = [1,2,3,4,5] 3 #lambda x:x+1 匿名函数 4 def add_test(x): 5 return x+1 6 7 def map_test(array): 8 ret = [] 9 for i in num: 10 ret.append(i+1) 11 return ret 12 ret = map_test(num) 13 print(ret) 14 15 输出: 16 [2, 3, 4, 5, 6]
1 #使用内置函数map 2 num = [1,2,3,4,5] 3 def map(func,array): #func:函数 array:可迭代对象 4 ret = [] 5 for i in array: 6 res = func(i) #func(i)相当于执行某个函数 7 ret.append(res) 8 return ret 9 10 print(map(lambda x:x+1,num)) # print(map(add_test,num)) 11 12 msg = 'jack' 13 res = map(lambda x:x.upper(),msg) 14 print(res) 15 16 输出: 17 [2, 3, 4, 5, 6] 18 ['J', 'A', 'C', 'K']
- filter函数:如果布尔值为True,则返回函数运行的值。遍历列表中每个元素,判断每个元素得到的布尔值,如果是True则留下。
def filter(func,array)
1 people = ['a_jack','a_alen','jhon'] 2 res = filter(lambda n:n.startswith('a'),people) 3 print(list(res)) 4 # def filter(func,array): 5 # ret = [] 6 # for n in array: 7 # if func(n): 8 # ret.append(n) 9 # return ret 10 # print(filter(lambda n:n.startswith('a'),people)) 11 12 输出: 13 ['a_jack', 'a_alen']
- reduce函数:把一个函数作用在一个序列上,函数本身必须接收两个参数,reduce把序列前两个数先代入函数计算后,再继续与序列下一个元素进行代入计算。
def reduce(func,array,init=None)
1 from functools import reduce 2 num = [1,2,3,100] 3 print(reduce(lambda x,y:x*y,num,2)) 4 # num = [1,2,3,100] 5 # def reduce(func,array,init=None): 6 # if init is None: 7 # res = array.pop(0) 8 # else: 9 # res = init 10 # for i in array: 11 # res = func(res,i) 12 # return res 13 # print(reduce(lambda x,y:x*y,num,2)) 14 15 输出: 16 1200
1 from functools import reduce 2 print(reduce(lambda x,y:x+y,range(100),100)) 3 #print(reduce(lambda x,y:x+y,range(1,101),)) 4 num=[1,2,3,4] 5 print(reduce(lambda x,y:x+y,num,)) 6 7 输出: 8 5050 9 10
- 返回函数:返回函数不要引用任何循环变量、或者后续会发生变化的变量。(一定要引用时需新建一个函数,在用其函数的参数来绑定循环的值)
- 函数闭包:一个函数返回一个函数,其内部的局部变量被新函数引用,返回的函数需要调用时才执行。
1 def fun(): 2 l = [] 3 def f(j): 4 def g(): 5 return j*j 6 return g() 7 for i in range(1,4): 8 l.append(f(i)) 9 return l 10 f1,f2,f3 = fun() 11 print(f1) 12 print(fun()) 13 14 输出: 15 1 16 [1, 4, 9]
1 #函数引用循环变量后,输出的值在函数调用时才执行,没有立即返回每一个 i 的值 2 def count(): 3 l = [] 4 for i in range(1, 4): 5 def f(): 6 return i*i 7 l.append(f) 8 return l 9 f1, f2, f3 = count() 10 print(f1(),f2(),f3()) 11 12 输出: 13 9 9 9
- 函数闭包:一个函数返回一个函数,其内部的局部变量被新函数引用,返回的函数需要调用时才执行。
- 匿名函数:lambda(lambda 形参:处理流程),处理多个值时用括号如 lambda x,y,z:(x+1,y+1,z+1)一般不单独处理,返回值为该表达式的结果
1 例: 2 name = 'jack' #name = 'jack_name' 3 def change_name(x): 4 return name + '_name' 5 6 res = change_name(name) 7 print(res) 8 9 #使用lambda,输出结果一样 10 a = lambda x:name+'_name' 11 print(a(name)) 12 13 输出: 14 jack_name 15 jack_name
- 装饰器(decorator):调用一个以函数作为参数被返回的函数,用来增强某个新函数的功能又不改变这个新函数本身的定义。用 @ 进行引用。
1 #导入functools模板 2 import functools 3 def g(func): #func为一个函数名 4 # @functools.wraps(func) #将warpper.__name__返回的函数名等同于func的函数名 5 def warpper(): 6 print('输出新函数 %s():' %func.__name__) 7 return func() 8 return warpper 9 #引用g函数 10 @g 11 def now(): 12 return '你好' 13 print(now()) 14 15 输出: 16 输出新函数 now(): 17 你好
1 #带参数的装饰器 2 import functools 3 def g(text): 4 def f(func): 5 @functools.wraps(func) 6 def wrapper(): 7 print('参数内容:%s,函数:%s():' %(text,func.__name__)) 8 return func() 9 return wrapper 10 return f 11 #引用装饰器 12 @g('你好Python') 13 def now(): 14 return '你好' 15 print(now()) 16 17 输出: 18 参数内容:你好Python,函数:now(): 19 你好
- 偏函数(Partial function): 使用 functools.partial()把函数的参数固定,返回一个新函数。可接受函数参数,*arg,**kw三个参数
1 import functools 2 int2 = functools.partial(int,base=16) #固定base的值 3 print(int2('12345')) 4 arg = functools.partial(max,10) # *arg=(10,2,7,3)再进行比较 5 print(arg(2,7,3)) 6 7 输出: 8 74565 9 10
7、内置函数
- abs():取绝对值
1 print(abs(-1)) 2 3 输出: 4 1
- all():序列中所有元素为真时返回True,单个可迭代对象为空时,也返回True;0,[ ],None为False
1 print(all(['1','2',0])) 2 print(all('')) 3 4 输出: 5 False 6 True
- any():序列中有一个为真时返回True。
- bool():0,‘ ’,None为False,其它为真
- byte():编码与解码。用什么编码就用什么解码,默认使用 utf-8 解码。
1 name = '你好' 2 print(bytes(name,encoding='utf-8')) 3 print(bytes(name,encoding='utf-8').decode('utf-8')) 4 print(bytes(name,encoding='gbk')) 5 print(bytes(name,encoding='gbk').decode('gbk')) 6 # print(bytes(name,encoding='ascii')) ascii不能编码中文 7 8 输出: 9 b'\xe4\xbd\xa0\xe5\xa5\xbd' #utf-8:三个字节表一个中文 10 你好 11 b'\xc4\xe3\xba\xc3' #gbk:两个字节表一个中文 12 你好
- dir():打印某一个对象下面有哪些方法。help():打印某一对象下的方法该如何使用
- divmod():打印商跟余数。(可用于分页功能,10表示总共有多少条记录,3表示一页有多少记录,得出结果分3页,不为0时在多分一页。)
1 print(divmod(10,3)) 2 3 输出: 4 (3, 1)
- eval():提取字符串中的数据结构,也可运算字符串中的表达式。
1 dic = {'name':'jack'} 2 dic_str = str(dic) #此处转换成字符串,无法再进行索引取值 3 dic_str = eval(dic_str) #使用eval转换后,可恢复字符串中的数据结构 4 print(dic_str['name']) 5 6 输出: 7 jack
- frozenset():生成新的不可变集合,不能进行索引
1 a = frozenset('jack') 2 b = frozenset(range(10)) 3 print(a) 4 print(b) 5 6 输出: 7 frozenset({'k', 'j', 'a', 'c'}) 8 frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
- hash():可用来判断数据有没有被改动过。如果改动,数据的哈希值会发生改变
1 name = 'jack' 2 print(hash(name)) 3 name = 'alen' 4 print(hash(name)) 5 6 输出: 7 -1093188409928294597 8 7045170762214928410
- bin():十转二进制;hex():转十六进制;oct():转八进制
- isinstance():判断数据类型
1 print(isinstance(1,int)) 2 print(isinstance([1,2,3],list)) 3 print(isinstance(1,dict)) 4 5 输出: 6 True 7 True 8 False
- zip():将两个序列一一对应组成元组放在列表中。
1 print(list(zip(['a','b','c'],'123'))) 2 p = {'name':'jack','age':18} 3 print(list(zip(p.keys(),p.values()))) 4 5 输出: 6 [('a', '1'), ('b', '2'), ('c', '3')] 7 [('name', 'jack'), ('age', 18)]
- max():比较后取最大值 min():取最小值
- 处理的是一个可迭代对象,相当于一个for循环取出每个元素进行比较,不同类型不能比较。结合zip使用,字典默认比较keys。
-
从第一位开始每个元素依次比较,有一个分出大小后面的不再进行比较。
1 dic = {'age1':'10','age2':'30','age3':'20'} #a、g、e一个个进行比较 2 print(max(zip(dic))) 3 print(max(zip(dic.keys(),dic.values()))) 4 5 输出: 6 ('age3',) 7 ('age3', '20')
1 #比较列表中的字典 2 people = [ 3 {'name':'jack','age':19}, 4 {'name':'alen','age':25}, 5 {'name':'jhon','age':20} 6 ] 7 print(max(people,key=lambda dic:dic['age'])) 8 # res = [] 9 # for item in people: 10 # res.append(item['age']) 11 # print(res) 12 # print(max(res)) 13 14 输出: 15 {'name': 'alen', 'age': 25}
- pow():求出平方与余数
1 print(pow(3,3)) #3**3 2 print(pow(3,3,2)) #3**3%2 3 4 输出: 5 27 6 1
- reversed():反转序列,但不保存
- round():四舍五入
- slice():切片
slice(start,stop,step) #开始,结束,步长
1 l = 'hello' 2 s1 = slice(3,5) #赋值一个切片 3 s2 = slice(1,4,2) 4 print(l[s1],l[s2]) 5 6 输出: 7 lo el
- sorted():从小到大进行排序,加 key 对元素进行修改,reverse=True 时为反向排序。(高阶函数)
1 l1 = ['jack','Apple','zero'] 2 l2 = [2,4,-3,-1] 3 print(sorted(l1,key=str.lower)) 4 print(sorted(l2,key=abs,reverse=True)) 5 #l2的值绝对值后进行比较排序 6 7 输出: 8 ['Apple', 'jack', 'zero'] 9 [4, -3, 2, -1]
8、模块(Module)
一个 .py 文件称为模块。是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用。
包(Package):按目录来组织模块的方法,选择一个顶层包,下面目录为模块名(避免模块名与其他人冲突,使用 顶层包名.模块名)每一个包目录下面都必须有一个__init__.py
的文件,也是一个模块,模块名就是上层目录名
好处:
- 提高代码可维护性
- 避免函数名与变量名冲突
- 命名时不要使用中文、特殊字符
- 模块名不要和系统模块名冲突
- 在Python交互环境执行(import 模块名),若成功则说明系统存在此模块。
【类似_xxx
和__xxx
这样的函数或变量为非公开的函数(private),外部不需要引用的函数定义成private,外部需要引用的函数才定义为public。可以在满足某种条件时(如if条件成立的情况下)再调用非公开函数。】
安装第三方模块:
- 使用pip安装
pip install Pillow #Pillow为模块名
- 到 Anaconda官网 下载GUI安装包
搜索模块:默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys
模块的path
变量中(sys.path),可用sys.path.append(路径)添加搜索目录