python函数
#函数定义 def mylen(): """计算s1的长度""" s1 = "hello world" length = 0 for i in s1: length = length+1 print(length) #函数调用 mylen() ''' ''' def mymax(x,y): the_max = x if x > y else y #if语句还可以这么写,长知识了 return the_max m1 = mymax(10,20) #按位置传递参数 m2 = mymax(x=5,y=10) #按形参名传递参数 m3 = mymax(5,y=6) #混合使用 print(m1) #20 print(m2) #10 print(m3) #6 ''' ''' def stu_info(name,sex = "male"): #默认参数 """打印学生信息函数,由于班中大部分学生都是男生, 所以设置默认参数sex的默认值为'male' """ print(name,sex) stu_info('alex') stu_info('eva','female') ''' ''' def defult_param(a,l = []): #默认参数是个可变类型 l.append(a) print(l) #l.pop() #可以通过pop使l变回空列表,这样就避免了可变默认参数的陷阱 defult_param('alex') #['alex'] #这样出现了错误,l=[]形参是可变类型,在第一次调用的时候值就改变了 defult_param('egon') #['alex', 'egon'] def trans_para(*args,**kwargs): print(args,type(args)) print(kwargs,type(kwargs)) trans_para("jinxin",12,[1,2,3,4],[3,4,],(1,4,7),{"a":"123","c":456},country="china") #('jinxin', 12, [1, 2, 3, 4], [3, 4], (1, 4, 7), {'a': '123', 'c': 456}) <class 'tuple'> #{'country': 'china'} <class 'dict'> #动态参数,也叫不定长传参,就是你需要传给函数的参数很多,不定个数,那这种情况下,你就用*args,**kwargs接收,args是元祖形式,接收除去键值对以外的所有参数,kwargs接收的只是键值对的参数,并保存在字典中。*args和 **kwargs
def function(arg,*args,**kwargs): #这是比较全的形参数 print(arg,type(arg)) #arg只能接收一个变量 print(args,type(args)) #args可以接收除键值对的所有变量并存在元组中 print(kwargs,type(kwargs)) #kwargs可以接收键值对,保存在字典中 function("jinxin",12,[1,2,3,4],[3,4,],(1,4,7),{"a":"123","c":456},country="china") # jinxin <class 'str'> # (12, [1, 2, 3, 4], [3, 4], (1, 4, 7), {'a': '123', 'c': 456}) <class 'tuple'> # {'country': 'china'} <class 'dict'>
def get_max(a,b): the_max = a if a > b else b return the_max print(get_max(20,8))print(max(100,20)) def get_add(*num): # *num表示要传不定数量的变量,相当于一个列表 sum = 0 for i in num: sum += i return sum s = [1,2,3,4,5,6] print(get_add(*s)) # *s把列表中的元素迭代传入形参中 def person(*msg, **kw): print('name:', msg[0], 'age:', msg[1], 'other:', kw) msg = ["李强",25] extra = {"city":"北京","job":"engineer"} person(*msg,**extra) #这里的**extra类似于上面说的*s #name: 李强 age: 25 other: {'city': '北京', 'job': 'engineer'}
def fact(n): if n==1: return 1 return n * fact(n - 1) print(fact(5)) #递归函数,递归调用的次数过多,会导致栈溢出 def fact(n): return fact_iter(n, 1) def fact_iter(num, product): if num == 1: return product return fact_iter(num - 1, num * product) print(fact(5)) #尾递归,尾递归调用时,尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出,遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出,因为在内存中开辟这个函数空间后没有结束就又开辟了一个函数空间,一直开辟 ######汉诺塔问题 def move(n,a,b,c): if n == 1: print(a,"-->",c) else: move(n-1,a,c,b) move(1,a,b,c) move(n-1,b,a,c) move(,"a","b","c") #这是直接百度出来的,果然很有智慧啊,我想了半天结果把自己绕进去了,自己还是笨啊
二分法
l1 = [1, 2, 4, 5, 7, 9] def two_search(l,aim,start=0,end=None): end = len(l)-1 if end is None else end #只有在第一次需要给 end 赋值,其他情况 end 值是变化的 mid_index = (end - start) // 2 + start #因为 start值是变化的,所以中间值的索引也跟着变化 if end >= start: if aim > l[mid_index]: return two_search(l,aim,start=mid_index+1,end=end) #当目标大于中间值时,从中间值的下一个往后找 elif aim < l[mid_index]: return two_search(l,aim,start=start,end=mid_index-1) #当目标小于中间值时,从开始往中间值的前一个找,这是有必要的,当目标值不存在时,通过这样的索引变化,可以引起start > end ,这样就可以跳出循环,返回查无此值了 elif aim == l[mid_index]: return mid_index else: return '没有此值1' #我觉得这句是没有用的,因为比较的过程中不是大于就是小于就是等于 else: return '没有此值2' print(two_search(l1,5))
def func1(a,b=2, *args,d,**kw): #参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数 print("a=",a,"b=",b,"args=",args,"d=",d,"kw=",kw) #使用命名关键字参数必须在他的前面有*号,无论是单独的*号或者是*args中的*号或者是**kw中的*号 func1(1,3,4,5,d=10,city="北京",job="play") #a= 1 b= 3 args= (4, 5) d= 10 kw= {'city': '北京', 'job': 'play'} args = (1,3,4,5) kw = {"d":10,"city":"北京","job":"play"} func1(*args,**kw) #把参数统一放到元组和字典中 #a= 1 b= 3 args= (4, 5) d= 10 kw= {'city': '北京', 'job': 'play'}
# globals() # 获取全局作用域中的内容 # # locals() # 获取局部(当前)作用域中的内容 a = 10 # 全局 def func(): # 全局 a = 40 # 局部 b = 20 # 局部 def abc(): # 局部 d = 30 # 是局部 print("哈哈") print(a, b) # 这⾥使⽤的是局部作⽤域 print(locals()) #{'d': 30, 'b': 20, 'a': 40} abc() # print(globals()) # 打印全局作用域中的内容 # {_'a': 10, 'func': <function func at 0x0000025B9BAA1EA0>} 这是摘出来的一些关键的我们定义的全局变量 print(locals()) # 打印局部作用域中的内容,locals()打印当前这个函数局部作用与中的内容 # {'abc': < function func. < locals >.abc at 0x00000292429A18C8 >, 'b': 20, 'a': 40} func() # print(globals()) #{_'a': 10, 'func': <function func at 0x0000025B9BAA1EA0>} 这是摘出来的一些关键的我们定义的全局变量 # print(locals()) #{_'a': 10, 'func': <function func at 0x0000025B9BAA1EA0>} 可以看到在最外部local()和globals()效果是一样的,可以这样理解local是当下的作用域中的变量内容,local在哪一层就返回哪一层的变量(使用或定义的),对于更内或者更外的不管
#***************************全局变量global********************* # global: # 1,声明一个全局变量。 # 2,在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)。其实作用域只限于这些不可变的,即字符串和数字因为他们的内容是存放到小数据池,对于列表
#来说,他的变量名存放的是指向变量内容的地址,变量内容存放在另一个内存空间,所以列表作为函数的默认参数时,列表是局部变量,当函数结束后释放的是列表中的地址,而地址指向的列表内容不改变,而因为是默认值再次执行时,再次赋值相同的地址
a=1 def func(): global a #没有这个语句时,输出的a的值就是1了 a = 3 func() print(a) #3 #对可变数据类型(list,dict,set)可以直接引用不用通过global li = [1,2,3] dic = {'a':'b'} def change(): li.append('a') dic['q'] = 'g' print(dic) #{'a': 'b', 'q': 'g'} print(li) #[1, 2, 3, 'a'] change() print(li) #[1, 2, 3, 'a'] #与函数内的值是一样的 print(dic) #{'a': 'b', 'q': 'g'} #****************************************nonlocal******************************** #nonlocal: # 1,不能修改全局变量。 # 2,在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。 def add_b(): b = 42 def do_global(): b = 10 #若无此处b的赋值,则报错,在do_global函数中,只能使用函数内定义的变量和全局变量, b += 2 #若无 b =10 且无b+=2,不报错,因为print是内置函数,可以引用所有变量,在小范围的作用域可以引用大范围的作用域中的变量,但是注意不能改变其值 print(b) #12 def dd_nonlocal(): nonlocal b b = b + 20 #这个引用的是do_global中的b,所以在此操作后do_global中b的值变为32 print(b) #32 dd_nonlocal() print(b) #32 #此时b的值已经被dd_nonlocal函数中的nonlocal改变,注意程序是顺序执行的之前的b +=2,print(b)不会改变 do_global() print(b) #42 #这里的b变量是add_b中定义的局部变量 add_b()
#********************************函数的嵌套调用********************************* def max2(x,y): m = x if x>y else y return m def max4(a,b,c,d): #求四个数中的最大数 res1 = max2(a,b) res2 = max2(res1,c) res3 = max2(res2,d) return res3 print(max4(23,-7,31,11)) #31 #************************************函数的嵌套定义********************************* def f1(): print("in f1") def f2(): print("in f2") f2() print("in f3") f1() # in f1 # in f2 # in f3 ''' ''' def f1(): #② print("a") #③ #分析下函数的运行过程,首先我们认为函数的定义是def f():开始包括这个def f()内的语句,即相对def f()缩进的,因为在python中缩进相当于{},所以相对def f()缩进的语句就是他的代码块,很明显看似定义结束的f()并不在这个代码块中,所以这个f()只是嵌套函数中其他函数对此函数的引用,当然在形式上可以认为是函数定义的结束 def f2(): #⑥ #函数首先忽略定义,执行f1(),执行f1()的时候再找f1()的定义,依次类推 print("b") #⑦ def f3(): #⑩ print("c") #⑪ print("d") #⑧ f3() #⑨ print("e") #④ f2() #⑤ f1() #① # a # e # b # d # c
#**************************函数变量的作用域链********************* #函数的作用域链:小范围作用域可以使用大范围的变量,但是反之不行,他是单向的 def f1(): a = 1 def f2(): def f3(): print(b) f3() f2() f1() #1,这里小范围的作用域即函数f3可以引用(使用)大范围的作用域f1中的变量,但是不能改变变量的值 def f1(): a = 1 def f2(): a = 2 f2() print('a in f1 : ',a) f1() #a in f1 : 1 #大范围的作用域不能使用小范围作用域的变量值
#***********************************函数名的本质************************* def func(): print('in func') f = func #函数名本质上就是函数的内存地址 print(f,type(f)) #<function func at 0x000001A41A4E1EA0> <class 'function'> #可以被当作容器类型的元素 def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1,f2,f3] d = {'f1':f1,'f2':f2,'f3':f3} #调用 l[0]() d['f2']() #可以当做函数的参数和返回值 def f1(): #④ ⑧ print('f1') #⑤ ⑨ def func1(argv): #② argv() #③这里代入f1,就是函数f1(),此时调用函数f1() return argv #⑥ 返回值时f1 f = func1(f1) #① 此时f=func1的返回值即f1 f() #⑦ f()就是f1(),调用f1,再输出个f1, 如果看的不清晰的话,可以多设置几个断点,然后单步执行,很容易看到语句执行的先后顺序 #函数是种第一类对象 # 第一类对象(first-class object)指 # 1.可在运行期创建 # 2.可用作函数参数或返回值 # 3.可存入变量的实体 # 总之可以把函数名当做普通变量使用
#************************************闭包***************************** ''' ''' 闭包函数: 内部函数包含对外部作用域而非全剧作用域变量的引用,该内部函数称为闭包函数 函数内部定义的函数称为内部函数,如下 '''
装饰器是根据闭包的原理写的 ''' def func(): name = '太白金星' def inner(): print(name) return inner f=func() #或者func()()这种形式,表明使用func的返回值作为新的函数名,并执行.因为inner函数是func函数的内部定义函数,不能在外部直接使用,只能在func函数内部使用,利用返回值巧妙地将func和inner联系起来 f() 当我们调用func()
时,每次调用都会返回一个新的函数,即使传入相同的参数
f1=func()
f2=func()
print(f1==f2) #False
def func(): a = 2 def inner(): print("111") return inner func() inner() #这会报错的,因为inner是局部作用域的变量,不能直接调用,只能在func()函数内调用执行,对于闭包函数,可以通过返回值,执行内部函数 def func(): a = 2 def inner(): print("111") return inner f = func() # func()的返回值是inner,然后赋值给f,执行f()即执行inner(),但这个和上面的例子是不同的,这里的返回值是局部作用域的返回值inner,inner作为内部变量可以执行inner()函数 f() #这两个语句也可以写为 func()() def func(): a = 2 global inner #这时把inner声明为全局变量,这样在函数外部也可以调用inner()函数了 def inner(): print("111") return inner func() inner()
#****判断闭包函数的方法_closure_**** #输出的__closure__有cell元素 :是闭包函数 def func(): name = 'eva' def inner(): print(name) print(inner.__closure__) return inner f = func() f() #(<cell at 0x0000028E44F775B8: str object at 0x0000028E45008110>,) #eva #输出的__closure__为None :不是闭包函数 name = 'egon' def func2(): def inner(): print(name) #name引用的是全局变量而不是局部变量 print(inner.__closure__) return inner f2 = func2() f2() #None #egon #******闭包嵌套******* def wrapper(): money = 1000 def func(): name = 'eva' def inner(): print(name,money) return inner return func f = wrapper() i = f() i() #eva 1000 #******闭包函数获取网络应用***** from urllib.request import urlopen def index(): url = "http://www.xiaohua100.cn/index.html" def get(): return urlopen(url).read() return get xiaohua = index() content = xiaohua() print(content) # 使⽤闭包, 可以保证外层函数中的变量在内存中常驻. 供后⾯的程序使⽤.
#注意到返回的函数在其定义内部引用了局部变量args, # 所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用, # 所以,闭包用起来简单,实现起来可不容易。 # 另一个需要注意的问题是,返回的函数并没有立刻执行, # 而是直到调用了f()才执行。我们来看一个例子: def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() # fs=[f,f,f],此时的i=3 print(f1(),f2(),f3()) # 9 9 9 #返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。 #返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。 #如果一定要引用循环变量怎么办? # 方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变: def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f(),通过i和j的传递,把循环变量和新创建的函数参数绑定 return fs f4,f5,f6 = count() #fs中的值是(j=1时的g,j=2时的g,j=3时的g)count里返回的是就是(g,g,g),并赋值给f4,f5,f6 print(f4(),f5(),f6()) #到这一步调用g()函数
面试题: def mul(): return [lambda x : i*x for i in range(4)] print([m(2) for m in mul()]) # output: # [6, 6, 6, 6] 推荐博客:https://blog.csdn.net/yitiaodashu/article/details/79025502 等价于: def mul(): func_list = [] for i in range(4): def lam(x): return x*i func_list.append(lam) return func_list print([m(2) for m in mul()]) 简单来说,在python里,相对而言的局部变量绑定的是值,非局部变量绑定的是空间, 而不是值本身,所以,for循环生成的i,相对于函数lam来说,是全局变量,所以绑定的是i所在的内存地址,但i最后变成了3,lam绑定的是3。所以导致了,生成的四个函数所得值时相同的 那么如何实现结果为 [0, 2, 4, 6] 呢? 按照刚才的思路,我们只需将lam函数中的 i 变成局部变量,这样函数lam绑定的就是值, 而不是内存地址: def mul(): func_list = [] for i in range(4): def lam(x,i=i): return x*i func_list.append(lam) return func_list print([m(2) for m in mul()])
#利用闭包返回一个计数器函数,每次调用它返回递增整数 def createCounter(): a = 0 def counter(): nonlocal a a = a+1 #因为把a放在了左边认为是重新赋值的新局部变量,所以如果没有nonlocal a 会报错的,认为a没有被定义 return a return counter def createCounter(): s = [0] def counter(): s[0] = s[0]+1 #通过一个序列赋值给s,这样做的目的是方便子函数能够直接使用父函数内的变量值, return s[0] #而不会产生“local variable 'xxx' referenced before assignment”这样的错误。 return counter counterA = createCounter() print(counterA(), counterA(), counterA(), counterA(), counterA()) def createCounter(): def f(): n=0 while True: n=n+1 yield n sun = f() def counter(): return next(sun) return counter counterA = createCounter() print(counterA(), counterA(), counterA(), counterA(), counterA())