一、函数定义
1.1 不带参数的定义与调用
定义:
def name():
print('ok')
实例:
def f():
print('ok')
f()//调用函数,其中f是一个存函数语句的变量,加()才能调用并返回结果
1.2 带参数的函数
定义:
def add(x,y): //型参
print(%s+%s %(x,y))//函数调用时必须要有这二个实参,否则会报错,按顺序对应,变量不能直接用变量名,不知是字符串还是变量,要用占位符,
告知是变量类型
add(2,3)//实参
实例:
def action2(n):
print ('starting action2...')
with open('日志记录','a') as f:
f.write('end action%s '%n)
action2(2)//创建一个文件日志记录,向里添加数据,若打印日志有重复也要定义成函数,如下日志定义,由于日志定义里不指打印日志还有不同的时间
import time
def logger(n):
time_format = '%Y-%m-%d %X' //定义显示格式
time_current = time.strftime(time_format) //显示当前时间
with open('日志记录','a') as f:
f.write('%s end action%s '%(time_current,n))//变量有二个用了二个占位符,后面要用二个变量名按顺序放,其中一个变量是函数内部变量
def action(n):
print('starting action2...')
logger(n)
参数类型:
1.print_info(39,'hu') //必需参数,直接写入数据
2.print_info(age=39,name='hu')//关键字参数,用变量把值固定
3.默认参数,如果某个参数是固定的值
def print_info('name','age','sex'='male')
print_info('hu',45)//会打印三个最后一个是默认的
print_info(name='hu',name=45)
//若默认值改变
print_info('hu',45 'female')----默认值改为女了
4.不定长参数,使用时给多少参数据都可以
元组参数
def add(*args) //args表参数,*表不定长,注这里args是无命名参数,能当做元组去处理
print(args) //打印参数
add(1)//1
add(1,2)//1,2
例:加法器
def add(*args)
sum=0
for i in args://args是一个元组序列
sum+=i
print(sun)
add(1,2,3)//6
//注:以上的变量未命名,直接是对象本身,这些无名的变量被处理成元组
字典型参数:
def print_info('hu',36,job='IT')//这个参数跟以上参数的区别是这个参数有命名,相当于键值对了,这个有命名参数要如何传不定长参数?
def print_info(**kwargs)----有命名的给创建成字典,把名字与值给放到字典里当键值对
print_info(name:'hu','age':30) //{name:hu,age:30}
有命名与无命名都包含:
def print_info(*args,**kwargs)//字符串,列表等被args接收生成元组,有名的被kwargs接收,生成字典,注意:前是无名的后要都是有名的才行不然会报错
print(arge)
print(kwargs)
for i in kwarges:
print('%s:%s'%(i,kwargs[i]))
print_info('hu',36,job='IT')//函数内部生成元组arge=('hu',36) 字典kwarges={'job':'IT'}
总结:关于不定长参数位置:*args放左边,**kwargs放右边
如果有默认参数,放左边:def new_add(sex='male',*args,**kwargs):
不定长参数特殊用法:
def f(*args):
print(args)
f([1,2,3])//结果([1,2,3])---把列表变为元组的一个参数,如果想直接反把列表变为元组?
f(*[1,2,3])--结果(1,2,3)--args对应[]---直接把列表变为元组
def f2(**kwargs):
print(kwargs)
f2(**{'name':'alex'})---就变为字典了
1.3 高阶函数:
满足二个条件其中一个就是高阶函数:
1.函数名可以作为参数输入
2.函数名可以作为返回值
def f(b):
return b*b
def foo(a,c,func):----func是一个函数做为一个变量成为foo的参数
ret=func(a)+func(c)
return ret
foo(1,2,f)---f表示func函数参数实例(一个实例函数),函数名是一个变量可以赋值,这个f表示引用的内存地址,函数名可以做为参数,
还可以做为函数的返回值,f是变量不运行函数,直接打印出来会是一个内存的引用地址 f()---执行了函数
二、函数的返回值
2.1 函数返回值
每个函数执行后都会有一个return返回值,如果不写默认是return None//返回为空
return返回什么内容给谁?
def f():
print(fla)
return None
b=f() //有二个值fla None
return 对象(字符 数字 列表 字典等都行)//这个是返回给函数调用者,函数执行结束返回的值
作用:1.结束函数 2.返回某对象
实例:
def f():
print('ok')
return 10
print(false)//这个不会打印,因为到return就结束了。格式上是包含函数里的
函数调用者f()----ok 10(这个函数是返回给函数调用者的)
a=f()----结果就是Ok与10,若带参数的话不同的调用者的返回值可能不同
注意:
1.函数里没有return,会默认返回一个None
2.如果return返回多个对象,那么python会帮我们把多个对象封装成一个元组返回,变为一个对象了
三、函数作用域
3.1 引入
def f():
a=4 //函数内定义的只能函数自己用外面不能用
print(a)---报错无结果,函数是有作用域的
3.2 函数作用域有四层
概述:
注:函数名也是变量的一种
最外层:built_in,系统固定模块里面的变量,比如int print等
内一层:global,全局变量,就是模块级别定义的变量
再内一层:enclosing:嵌套的父级函数的局部作用域
最内层:local,局部作用域
实例:
例: x=int(2.3) // built_in变量,int是一个函数变量是python写好的,所以这个变量在全局找不到才会到最外层去找 g_count=9 // global def outer(): o_count = 1 // enclosing i_count=8 def inner(): i_count=3 // local print(i_count) 找i_count从最内向最外去找,内的优先级高,先打内的如果内没有就向外找打印,若外层打印就不向内找了 例2: count=10 def outer(): print(count) // 打印是10这里表示在局部作用域里引用了全局变量 count = 5 //会报错,局部作用域里不能修改全局作用域的变量 outer() count=10 def outer(): count = 5----这时不会报错,因为这个相当于在局部作用域里新加了一个变量跟全局变量没关系,没用引用全局变量 count=count+1--会报错,这里表示引用count,所以不能修改 outer() 如何在局部作用域里修改全局变量? count=11 def outer(): global count----告诉是全局变量这时才能改 count=5 print(count) outer()----这时改的就是全局的 如何在local里改enclosing变量? x=int(2.3) // built_in变量 g_count=9 // global def outer(): o_count = 1 // enclosing def inner(): nonlocal o_count // python引进的,local改enclosing变量 i_count=3 // local print(i_count)
四、递归函数
4.1 概述
定义:自己调用自己的函数是递归函数
特性:
1.调用自身函数
2.有一个结束条件
3.但凡是递归可以写的循环都可以解决
4.递归的效率很低,一般少使用
实例:
阶层5!=5*4*3*2*1
循环方式: def f(): ret=1 for i in range(1,n+1):---要从1开始 ret=ret*i return ret print(f(5)) 递归方式:递归函数--一个函数里执行自己的函数 def fact(n): return n*fact(n-1)--自己调自己再加个结束条件
实例2:斐波那契数列 0 1 1 2 3 5---后面的数是前二个数的和
循环方式: def fibo(n): before=0 after=1 for i in range(n-1):----i没在下面使用,只是控制下面代码的控制次数来的 ret=before+after before=after after=ret return ret 递归方式: def fibo(n): if n <=2 return n return fibo(n-1)+fibo(n-2) print(fibo(8))
五、内置函数
5.1 常用函数
abs(x)--绝对值,正负数的绝对值
all(iterable)----这个参数必须是序列,对这个序列遍历如果有空值就返回false,否则是True,检查一个序列有没有空值
bool()--判断一个表达式是true还是false
dict()--创建字典
divmod()--求余
eval()---还原原来的类型,也可以当计算器用,print(eval(1+2))---3
frozenset()--不可变集合
isinstance()
int()
next()
5.2 四个重要的内置函数
1.过滤器:filter(fun1,str) //第一个参数是函数名,第二个参数是序列,把序列的值一一传给函数过滤处理
过滤器(是一种迭代器,把对象用容器装起来本身是指针只存指针位置)
str=['a','b','c','d'] def fun1(s): if s != 'a': return s ret = filter(fun1,str) //这个内置函数的作用就是把str元素一一传给函数的s里做参数 print(ret)---<filter object at 0x000000000A3A9A0>是一个过滤器对象,相当于指针指向上面这个元组的位置,这只是一个容器占内存很少
作用:遍历str里的元素当成参数传给fun1这个函数,如果不是a的就返回出来,返回('b','c','d'),过滤掉不需要的,就是过滤器的 如果想看这个容器的值如何做? print(list(ret))---这时的值就加载到内存了就变成了('b','c','d')
2.处理器:map(fun2,str)//把序列的值一一传给函数过滤处理数据处理
map(fun2,str) str = [('a','b')] def fun2(s): return s + "alvin" ret=map(fun2,str)----同上map把str的元素传给fun2,不过滤,对每个元素进行一个处理,它也是一个迭代器 print(list(ret))----['aalvin','balvin'] 注: map与filter的区别:filter生成的结果str里的元素不变只过滤掉会减少,还map是改变了str里的元素
3.reduce(函数名,序列)
在python2里可以直接调用,但在3里就不行了,需导入模块:from functools import reduce
def add1(x,y): return x + y print(reduce(add1,range(0,9)))//return的作用是序列里的元素都执行add1函数,结果就是一个值不是迭代器了
4. lambda:无名函数,没有名字的函数
add = lambda a,b : a + b
print add(2,3)
例:
form functools import reduce
print(reduce((lambda x,y : x + y),range(1,6)))
六、装饰器
6.1 什么是装饰器?
概述:用相同的调用方式给原函数新加功能
使用装饰器的前提:
1.函数的作用域(四层)
2.高价函数:函数做为另一个函数的参数
3.闭包:如果一个内部函数里,对外部作用域(但不是在全局作用域)的变量进行引用(里面引用了外部的变量),那么内部函数就被认为是闭包
def outer(): x=10 def inner():---内部函数 print(x)---引用它外部函数的变量 return inner---这个内函数就是闭包,特点:它会脱离外部函数的限制而运行 如何在最外层调用inner函数? f=outer()---外层函数变成一个对象了里面的方法再调用后用() f()--这时就是调用inner了相当于outer()()---结果是10 由于inner是内嵌函数不能直接调用,否则会报错,在最外层打印内嵌函数的变量都会报错,函数名也是变量所以最外层调用不了 inner是局部变量,全局无法调用 以上的例子跟如何解释闭包? f=outer()---第一个函数执行完了后,其内定义的变量等都不生效了 f()---执行第二个函数,这是在第一个函数执行完后执行的,理论上x=10就失效了,但在打印时为什么还是10?这种现象就是闭包
所以,闭包=内部函数+定义函数的环境
6.2 装饰器
1.引入之修改原函数
原函数 def foo(): print('foo...') 新加需求 import time def foo(): start = time.time()---当前时间 print('foo...') time.sleep(2) end = time.time() print('spend %s'%(end-start))---新加的需求 这种违反了开放封闭原则 之前的是公共函数,有很多模块会使用到这个函数,直接修改原函数如果有问题会影响所有的模块,所以公共函数就被封闭了不让改了,那要如何加功能呢? 开放原则:公共函数不能修改但可以扩展
2.引入之新功能定义为新函数
def show_time(f):---fun是函数名,函数名是变量 start=time.time() f()---函数调用,所以外层函数执行时这个函数也是调用返回结果的状态 time.sleep(2) end = time.time() print('spend in %s'%(end-start)) show_time(foo)//调用,但这时foo的调用方式改变了,其它所有模块要用这个功能那么所有人都要改代码不现实,如何实现还用foo()来实现这个功能 这个结果表示意思是foo函数运行所用的时间如何通过f()也实现这个意思?
3.引入之加闭包修饰器
def show_time(f): def inner():---闭包 start=time.time() f() time.sleep(2) end=time.time() print('spend is %s'%(end-start)) return inner---外层函数的返回值,它是把内层函数加载到内存里就结束了,这里返回的是内层函数的内存地址,内层函数未执行 foo=show_time(foo)---一个函数名内存地址等价于@show_time--python提供的简单写法,这个foo就是返回的inner foo()---这里是调用了内层函数是结果,这还是原来的调用方式,这个就相当于inner()
4.完整修饰器
def show_time(f): def inner():---闭包 start=time.time() f() time.sleep(2) end=time.time() print('spend is %s'%(end-start)) return inner @show_time ----直接把二个函数关联起来,这个就是对象inner def foo(): print('foo...') ---定义公共函数据f foo() 若其它函数使用 @show_time ----直接把二个函数关联起来 def bar():----在基础库的函数上加个@show_time,其它模块就可以调用了 print('bar...') bar()---如果不加@show_time它调用的是原函数,加上之后调用的是inner()
6.3带参数的装饰器
(1)原功能函数加参数的装饰器
当foo函数里带参数时如何保持调用方式不变就把新加的功能实现?
def show_time(f): def inner(x,y):---闭包 start=time.time() f(x,y) time.sleep(2) end=time.time() print('spend is %s'%(end-start)) return inner @show_time---表明下面函数用过新加的功能 def add(a,b):---可以加成不定长函数试试 print(a+b) time.sleep(2) add(2,3)---这里跑的是inner()函数,所以要有二个型参,跑这个时也会f变为add,所以add时的参数要从inner里拿到
(2)装饰器show_time加参数
现在的参数只有函数,这里作用就是通过这个参数判断功能函数要不要使用这个装饰器,例如:现在想在show_time再加一个功能用于打印日志,但有些函数想用这个功能有的不想用如何用一个参数控制
def logger(flag): def show_time(f):--闭包,这时show_time就可以用外层函数的参数flag了 def inner(x,y):---闭包 start=time.time() f(x,y) time.sleep(2) end=time.time() print('spend is %s'%(end-start)) if flag='true': print('日志记录') return inner return show_time @logger('true')--带参数的装饰器,这个结果返回的是一个show_time的内存地址 def add(a,b):---可以加成不定长函数试试 print(a+b) time.sleep(2)
(3)登录实例
def login_rage(fin): def login(f):---这个作用是f()可以调用里面的函数,实际上f()根据判断条件可以跑二个代码新加功能可要可不要,一个跑inner一个跑它本身 def inner(): if fin == 'False': username = input('username:') password = input('passwd:') username--在文件里取出,passwd在文件里取出 if user == username and passwd == password: f() return inner-----是login跑完后返回的结果,返回的是inner的内存地址,login就是指这个地址的指针 return login @login_rage(fin='')---这个值也从某个保存文件里取出,如果不带参数可通过它让下面的函数直接执行inner,有参数了就自动选择要不要加功能 def home():---首页 print(welcome to home page) def finamece():----金融 print(welcome finacome)
七、生成器
1.列表生成器:生成列表的表达式,固定表达式
a=[x for x in range(10)]---表示遍历range时的每个值再赋值给第一个x,注意:第一个变量x必须与for后的变量名一样
print(a)---[1,2,3,4,5,6,7,8,9]
b=[x*x for x in range(12)]----对里面的值可以做相同处理
def f(n):
return n**3---f这个函数结束值包含在函数结构体里,索近四位,根据位置可知哪个返回值是哪个函数的
c=[f(x) for x in range(3)]
2.序列赋值
t=['123',8]
a,b=t
print(a,b)---打印的是二个值,元组与列表都是一样
3.生成器创建方法之一
注:列表生成器只要运行里面所有的数据都写在内存里了占内存很多
s=(x*x for x in range(12)) print(s)//generator object <genexpr> at 0x00000003I4000----是一个生成器的地址,这时里面的所有数据都没有进行处理 只有一个生成器的位置,想用哪个值就生成哪个值,是一个个生成的,不再是全部生成好后加内存里 如何一个个生成元素并处理?生成器内置的一个方法 print(s._next_())---由于这是特别的方法一般不建议这样用,但有一些系统函数也有这样的功能,可以直接调用 print(next(s))=0,这里取到第一个值,由于next()是公用的系统函数,其他对象也可以用所以用next(s)---s做为参数,s.只能调用自己内部的方法而这个方法不是这个对象内部的面是公用的 Python3---里面的方法_next_ 而python2的是next可以直接用 结果:
print(next(s))---拿第一个 print(next(s))---拿第二个,次数多于原个数时就会报错了,如果很多时要如何取值?
通过for循环来取值--只能打印一次,第二次打印不出来了,因没有变量引用被回收了 for i in s:-----把所有值取出来,for里面实际是做了next操作 print(i)
4.生成器创建方法之二 yield
def foo(): print('ok') yield 1---相当于return返回值 print('ok2') yield 2 reture None--默认返回为空 foo()---运行后是一个生成器对象,一个生成器地址,这时foo里面的代码都不会执行,它是一个生成器 yield #:#表示循环次数,这是一次yield就运行一个代码,如何运行这个函数? next(foo())---ok next(foo())---ok2,会记住上一次yield,所以这个就从上一次yield会执行,这是调用函数,实际就是第一次next进去后yield就给了返回值给next(),第二次再next时第一个yield就没了及前面的执行体就没直接从下一个开始 这时next(foo())本身的值就是1或2 d=next(foo()) print(d)=1------第一个ok是调用的函数结果,且给调用者一个返回值1保存在调用者d里 使用for来取: for i in foo(): print(i) g=foo() print(g)----ok 1 ok2 20--for里面会next每一可迭代对象,第一美人鱼next(foo())时打印了一个ok又给调用都一个返回值1,所以有二个值ok 1
5.生成器内的元素处理方法之next
可迭代对象?内部有_iter_方法的都是可迭代对象,例:元组 列表 字典 生成器,在python3中系统函数iter()可以被这些对象使用 for i in 序列(可迭代对象): 斐波那契数列不用递规,用其它方式解决? 0 1 1 2 3 5 8 def fib(max): n,before,after = 0,0,1 while n < max: print(after) before,after = after, before+after n=n+1 用生成器来解决? def fib(max): n,before,after = 0,0,1 while n < max: yield before----这个函就变成了生成器,相当于return,给调用它的函数返回一个值,运行一个函数会执行代码块有一个结果,但遇到return就会给这个函数赋一个返回值,这就是函数自己的值与函数调用的值不同的区别 before,after = after, before+after n=n+1 print(fib(8))---这就是个生成器的地址 g=next(fib(8))----调一次出现一个值想看其中一个的就用for循环来取
6.生成器内的元素处理方法之send
def bar():-----生成器,若想调用它进入函数里执行,用next才会去执行,用send也可以,不过它可以传值不会一个个去执行,它是生成器内置函数 print('ok1') count=yield 1---若yield后不跟其它就返回的是空 print(count) print('ok2') yield 2 b=bar()---就不是调用函数了就是一个生成器对象了 next(b)----执行一次会出现一个结果 第一次用send调用时必须是b.send(None)=next(b),第一次send前如果没有next就没有进去只能传一个send(None) b.send(None)---结果是ok1,而这个调用这个函数的实体就得到yield的一个返回值1,这个函数结束 第二次开始就可以给send一个参数了 b.send('dan')----第二次进入,dan就赋值给yield 1了再进下一步打印ok2,给这个函数的返回值是2
7.生成器之yield伪并发
八、迭代器
1.生成器都是迭代器,迭代器不一定是生成器 list tuple dict string这些内置方法都有_iter_,所以它们都是可迭代对象, eg: q=[1,2,4] d=iter(q)---变为迭代器了,把列表放盒子里,只能用next去调值 print(d)---这是一个可迭代对象的内存地址 iterator object at 0x0000003fkjej next(d)---去调用里面的值,调一次出现一个值,这里列表转化为迭代器了 什么是迭代器? 满足二个条件:1.有iter方法 2.有next方法 for循环内部三件事 如:for i in [1,2,34] 1.调用可迭代对象的iter方法返回一个迭代器对象 2.不断调用迭代器对象的next方法 3.处理stopIteration(异常处理) 2.isinstance()--判断对象类型是否正确 form collections import Iterator--迭代器 print(isinstance([3,4],list))---ture ,判断[3,4]是不是list类型 Iterable:可迭代对象类型,列表是可迭代对象类型 Iterator:迭代器类型 3.迭代器协议 1.有next()方法 2.有iter()方法-----作用是给可迭代对象变为一个迭代器,li=[1,2,3] iter(li)--就是一个迭代器