一 函数的介绍和基本使用
为什么要使用函数?
- 避免代码重用
- 提高代码的可读性
函数的定义与调用
def 函数名(参数1,参数2)
''' 函数注释'''
print('函数体')
return 返回值
定义:def关键字开头,空格之后接函数名和圆括号,最后还要加一个冒号。
函数名:函数名是包含字母,数字,下划线的任意组合,但是不能以数字开头。虽然函数名可以随便取名,但是一般尽量定义成可以表示函数功能的。
函数的返回值
return的作用:结束一个函数的执行
首先返回值可以是任意的数据类型。
函数可以有返回值:如果有返回值,必须要用变量接收才有效果
没有返回值的时候分三种情况:
- 当不写return的时候,函数的返回值为None
- 当只写一个return的时候,函数的返回值为None
- return None的时候,函数的返回值为None(几乎不用)
return返回一个值(一个变量)
return返回多个值(多个变量):多个值之间用逗号隔开,以元组的形式返回。
接收:可以用一个变量接收,也可以用多个变量接收,返回几个就用几个变量去接收
函数的参数
def fun(s):#参数接受:形式参数,简称形参 ''' 计算字符串长度的函数---------函数的功能 参数s:接受要计算的字符串--------参数的信息 return:要计算字符串长度 ---------返回值得信息 ''' length=0 for i in s: length+=1 return length ret=fun('helloword')#参数传入:实际参数,简称实参 print(ret) 实参和形参 形参:是函数定义时候定义的参数 实参:函数调用的时候传进来的参数 传递多个参数 可以传递多个参数,多个参数之间用逗号隔开。 站在传参的角度上,调用函数时传参数有两种方式: 按照位置传参数 按照关键字传参数 动态参数 按位置传值多余的参数都由args统一接收,保存成一个元组的形式 按关键字传值接受多个关键字参数,由kwargs接收,保存成一个典的形式 args: def fun(a,b,*args): sum=a+b for i in args: sum+=i return sum print(fun(1,5,6,4))#输出1+5+6+4的和 kwargs: def fun(a,b,**kwargs): print(a,b,kwargs) # 按照关键字传参数 fun(a = 10,b = 20,cccc= 30,dddd = 50)#输出10 20 {'cccc': 30, 'dddd': 50} def f(a,b,*args,defult=6,**kwargs): #位置参数,*args, 默认参数,**kwargs # print(a,b,args,defult,kwargs) return a,b,args,defult,kwargs #传参数的时候:必须先按照位置传参数,再按照关键字传参数 print(f(1,2,7,8,ccc=10,der=5))
二 函数嵌套及作用域链
1 函数命名空间
全局命名空间:创建的存储“变量名与值的关系”的空间叫做全局命名空间
局部命名空间:在函数的运行中开辟的临时的空间叫做局部命名空间
内置命名空间:内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法。
三种命名空间之间的加载顺序和取值顺序:
加载顺序:内置(程序运行前加载)-->全局(从上到下顺序加载进来的)-->局部(调用的时候加载)--->内置
取值:在局部调用:局部命名空间--->全局命名空间--->内置命名空间
2 作用域:就是作用范围
命名空间和作用域是分不开的
作用域分为两种:
全局作用域:全局命名空间与内置命名空间的名字都属于全局范围,在整个文件的任意位置都能被引用,全局有效
局部作用域:局部命名空间,只能在局部范围内生效
站在全局看:
- 使用名字的时候:如果全局有,用全局的
- 如果全局没有,用内置的
- globals方法:查看全局作用域的名字【print(globals())】
- locals方法:查看局部作用域的名字【print(locals())】
在函数内部创
global name name = 'hahha'
3 闭包
闭 :内部的函数
包 :包含了对外部函数作用域中变量的引用
闭包的定义:如果在一个函数里,对在外部作用域(但不是全局作用域)的变量进行引用,内部函数就被认为是闭包
闭包=函数块(内部函数)+定义函数时的环境
def outer(): x = 10 def inner(): #条件1 inner就是内部函数 x =20 print(x) #条件2 外部环境的一个变量 return inner # outer()() f = outer() # 返回的是inner的内存地址 print(f) f()
三 嵌套,匿名和高阶函数
1 嵌套函数
函数里不仅可以写代码, 还可以嵌套函数
name = "harry" def change(): name = "caishuang_harry" def change2(): name = "harry0000" print("第三层打印", name) change2() # 调用内层函数 print("第二层打印", name) change() print("最外层打印", name) 输出结果: 第三层打印 harry0000 第二层打印 caishuang_harry 最外层打印 harry
2 匿名函数
匿名函数就是不需要显示的指定函数名 # 这段代码 def calc(x,y): return x**y # 换成匿名函数 calc = lambda x,y:x**y print(calc(2,5)) 匿名函数结合map处理数据 res = map(lambda x:x**2,[1,5,7,4,8])
3 高阶函数
将一个函数作为参数传入另外一个函数 def get_abs(n): return int(str(n).strip("-")) def add(x, y, f): return f(x) + f(y) print(add(5, -10, get_abs))
四 递归函数
1 递归的定义
- 什么是递归:在一个函数里在调用这个函数本身
- 最大递归层数做了一个限制:997,但是也可以自己限制
- 最大层数限制是python默认的,可以做修改,但是不建议你修改。(因为如果用997层递归都没有解决的问题要么是不适合使用递归来解决问题,要么就是你的代码太烂了)
- 结束递归的标志:return
- 递归解决的问题就是通过参数,来控制每一次调用缩小计算的规模
- 使用场景:数据的规模在减少,但是解决问题的思路没有改变
2 递归的特性
- 必须有一个明确的结束条件
- 没次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高, 递归层次过多会导致栈溢出
3 递归函数实现斐波那契数列
def fib(n): print("-----",n) if n==1: return 1 elif n==2: return 1 else: return fib(n-1)+fib(n-2) print(fib(5))
4 迭代器实现阶乘
def fact(n): if n==1: return 1 return n*fact(n-1) print(fact(5))
五 装饰器
1 为什么要使用装饰器呢?
装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展
装饰器的本质:就是一个闭包函数
2 装饰器实现代码运行时间统计
import time def warp(func): def inner(): start_time = time.time() func() end_time = time.time() print('执行%s' % str(float(end_time) - float(start_time))) return inner @warp def foo(): time.sleep(2) print('haha') @warp def bar(): time.sleep(3) print('hehe') foo() bar()
3 带参数的装饰器
当加了很多装饰器的时候,现在忽然又不想加装饰器了,想把装饰器给去掉了,但是那么多的代码,一个一个的去闲的麻烦,那么,我们可以利用带参数的装饰器去装饰它,这就他就像一个开关一样,要的时候就调用了,不用的时候就去掉了。
给装饰器里面传个参数,那么那个语法糖也要带个括号。在语法糖的括号内传参。在这里,我们可以用三层嵌套,弄一个标识为去标识
def warpper(f): def inner (a,b): f(a,b) print("inner") return inner @warpper def foo (a,b): print(a+b) foo(1,2) # 嵌套三层函数 # 带参数的装饰器:(相当于开关)为了给装饰器传参 # F=True#为True时就把装饰器给加上了 F=False#为False时就把装饰器给去掉了 def outer(flag): def wrapper(func): def inner(*args,**kwargs): if flag: print('before') ret=func(*args,**kwargs) print('after') else: ret = func(*args, **kwargs) return ret return inner return wrapper @outer(F)#@wrapper def hahaha(): print('hahaha') @outer(F) def shuangwaiwai(): print('shuangwaiwai') hahaha() shuangwaiwai()
4 多个装饰器装饰一个函数
def foo(fun): def inner(*args,**kwargs): print('in foo(: before') ret = fun(*args,**kwargs) print('in foo(: after') return ret return inner def bar(fun): def inner(*args,**kwargs): print('in bar: before') ret = fun(*args,**kwargs) print('in bar: after') return ret return inner @foo @bar def fun(): print('饿了吗') fun() ''' @foo和@bar的执行顺序:先执行foo里面的 print('in foo: before'),然后跳到了bar里面的 print('in bar: before') ret = fun(*args,**kwargs) print('in bar: after'),完了又回到了foo里面的 print('in foo: after')。 '''
六 迭代器和生成器
1 什么是生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以生成器就出现了,他弥补了直接生成大列表的不足,改成为定义某种计算过程,然后在需要的时候进行计算后得出想要的结果,从而节省了大量的内存空间。这就是生成器:generator
如果列表元素可以按照某种算法推算出来那么就可以在循环的过程中不断推算后续的元素
2 创建一个生成器
#生成器不会将数据直接存入内存,而是在调用时才会生成数据加载到内存当中 #生成器就是一个可迭代对象 #生成器一共有两种创建方式 1 是通过()的方式 2 是通过yield # s=(i*2 for i in range(1,1000)) # print(s) # print(next(s)) #等价于s.__next__() in Py2:s.next() # print(next(s)) # # for i in s: # print(i) def foo(): print('ok') yield 1 g=foo() next(g) #可迭代对象一般可以是字典 列表 迭代器 生成器,对象一般有___iter___方法
3 生成器推算斐波那契
# 不使用生成器 def fib(max): n,before,after=0,0,1 while n < max: print(before) before,after=after,before+after n+=1 # fib(10) # 使用生成器 def fib2(max): n,before,after=0,0,1 while n < max: # print(before) yield before before,after=after,before+after n+=1 g=fib2(8) print(next(g)) print(next(g)) print(next(g)) print(next(g)) print(next(g))
4 生成器的send方法
#send仅仅比next多一个功能,就是可以给yield前面的变量赋值 #第一次send前如果没有next只能传一个b.send(None)=send(b) def bar(): print('ok1') count=yield 1 print(count) print('ok2') yield 2 b=bar() next(b) b.send('eeee')
5 生成器实现协程伪并发
import time def consumer(name): print('%s 装备吃包子了'%name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!"%(baozi,name)) def producer(name): c = consumer('A') c2 = consumer('B') next(c) next(c2) print("%s准备做包子"%name) for i in range(10): time.sleep(1) print("做了两个包子") # c.send('韭菜'+str(i)) # c2.send("白菜"+str(i)) c.send(''.join(['韭菜',str(i)])) c2.send("白菜"+str(i)) producer('cs')
6 迭代器
我们已经知道 可以直接作用于for循环的数据类型有以下几种:
- 1 一类是集合数据类型 如list tuple dict set str等
- 2 一类是generator, 包括生成器和带yield的generator function
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable, 可迭代的意思是可遍历 可循环
#生成器都是迭代器,迭代器不一定是生成器 #list tuple, dict, string:Iterable(可迭代对象) l=[1,2,3,5] d=iter(l) print(isinstance(l,list)) #isinstance函数判断数据类型 #满足两个条件:1 iter方法 2 有neet方法 就是迭代器 #for 循环内部三件事 #1 调用可迭代对象的iter方法返回一个迭代器对象 #2 不断调用迭代器的next方法 #3 处理StopIteration异常 for i in [1,2,34]: iter([1,2,34]) a = 'ssdfs' print(a.swapcase())