3-1python语法基础-函数-函数创建调用和参数,函数作用域,装饰器迭代器生成器,递归函数,匿名函数
########
函数学习什么
1,函数定义
2,函数的参数
形参,实参,关键字参数,
3,函数作用域,
4,装饰器
5,迭代器生成器
6,递归函数,
7,匿名函数
########
函数的定义,函数调用,函数返回值和返回值接收
def func1(): name = "andy" age = 18 dict1 = {"name": "andy"} list1 = [1, 2, 3] return name,age,dict1,list1 print(func1()) name = func1() print(name) name,age,dict1,list1 = func1() print(name,"-----",age,"-----",dict1,"-----",list1)
#####
1,函数可以有返回值,也可以没有返回值,只要执行了return就不会往后执行了,
2,函数的返回值可以有一个,也可以有多个,有多个返回值,return 1,2 多个参数使用逗号分隔,而且返回值的类型可以是任意的类型,
3,如果是一个变量接收,就是一个元组,如果多个变量接收,但是要求和返回值的数量一致,否则会报错,
######
函数参数,形参实参,无参数,默认参数,调用使用位置参数,关键字参数,
def hollo3(age, name="heihei"): print(age) print(name) hollo3(name='andy', age=18)
####
理解函数的参数:
1,函数可以有参数,也可以没有参数,有参数一定要传递,否则会报错的,
2,定义函数的参数,叫做形参,调用的时候传入的参数,叫做实参,
3,定义函数的时候,定义参数的时候,必须先定义位置参数,最后定义默认参数,默认参数也叫做关键字参数,
4,函数调用的时候就使用了关键字参数,这样就可以不用管参数的顺序了,默认是位置参数,
5,有了默认参数之后,调用函数的时候,这个参数可以不写,则使用默认值,
####
函数参数-----不定长参数(动态参数)
def hollo3(name,*args,**kwargs): print("name",name) print("args",args) # (60,70) # 这是返回一个元组 print("kwargs",kwargs) # {'a': 1, 'b': 2} 这是一个字典, hollo3("xiaoxiao") hollo3("xiaoxiao",60,70) hollo3("xiaoxiao",60,70,a=1,b=2) list1 = [60,70] dict1 = {'a': 1, 'b': 2} hollo3("xiaoxiao",*list1,**dict1)
####
一定要重视这种不定长参数,使用是非常的广泛的,
1,不定长参数,或者叫做动态参数
2,加了星号 * 的参数会以元组(tuple)的形式导入,加了两个星号 ** 的参数会以字典的形式导入。然后就可以像处理元组和字典一样处理参数了,
3,先后顺序:必须先定义args,然后定义kwargs,如果有关键字参数一定放在kwargs之前,
4,参数名加星就可以了,后面的args,不是必须是这个,但是星一定要有,一般使用args,这是一种编码习惯,
####
不定长参数的调用
1,这种参数,可以不传递任何的值,打印出来是一个空的元组,或者一个空的字典,
2,如果想要传递元组的形式,可以调用的时候,传入单个值,如果像要传入字典的方式,就是name = value的方式
3,还可以使用列表传入,使用字典传入,
调用方式很灵活,一定要注意,
######
函数的命名空间和作用域
a = 1 def func(): a = 4 print(a) print(a) func()
#####
1,命名空间,一个全局的,一个是局部的,函数内部定义的变量就是局部变量,
2,作用域和命名空间是分不开的,作用域分为,全局作用域和局部作用域,
#####
函数的命名空间和作用域----global,globals,locals,
a =1 def func(): x = "aaa" global a a += 1 print("函数内部的globals",globals()) # 这个还是全局的打印出的是一样的, print("函数内部的locals",locals()) # 这个是可以打印出x的,但是没有a, func() print("全局的globals", globals()) # 这里的globals = locals print("全局的locals", locals()) # 这里就是没有x的,
####
1,对于不可变类型,你可以在局部查看全局的变量,但是不能修改,如果要修改,你就需要使用global进行声明,
2,自己的代码中应该尽量少使用global,这样会导致代码非常不安全,混乱,会污染全局变量,
#####
函数的嵌套和闭包,深刻体会函数名的使用
def func(): name = 'python' def inner(): # print(123) print(name) print(inner.__closure__) # inner() # 这种是闭包,但是这种写法比较浪费资源,每次调用函数都要生成name变量,所以不这样写 return inner # return inner 在外部函数中返回内部函数的名字,这就是一个闭包最常用的方式, f = func() # f = func() = inner f() # f() = inner f() # # 这种写法我调用这个函数100次,但是里面的name变量就只会生成一次了,不会反复生成了,节省资源, # 输出结果:python
####
1,什么是简单的函数嵌套?什么是闭包?根本只有一个,就是内部函数是否使用了外部函数的变量,这也是闭包的定义!!!
2,看代码里面,inner.__closure__,这个就可以验证是否闭包,返回cell,就是闭包,返回none就不是闭包,
3,需要深刻体会函数名的意义,你看f接收了一个内部函数的名字,只需要f(),这个时候就是调用的内部函数了,
4,函数名,就是一个内存地址,可以赋值给变量,可以通过变量直接(),调用,可以通过作为容器中的元素,比如[func1,func2]
5,return inner 在外部函数中返回内部函数的名字,这就是一个闭包最常用的方式,
#####
函数的装饰器
def outer(func1): def inner(*args,**kwargs): print(123) return func1(*args,**kwargs) return inner @outer def say_hello(): print("同学你好") # say_hello2 = outer(say_hello) # say_hello2() say_hello()
###
深刻认识装饰器,
1,首先一定是一个闭包,也就是是一个函数嵌套,并且内部函数引用了外部函数的变量,
2,一定要传入的是一个函数,并且要在内部函数中,把这个函数调用
3,装饰器的意义就是在于可以在函数的开始之前,或者结束之后,增加一些东西,
4,必须要深刻理解这个函数的装饰器,这个应用是非常的广泛的,
5,有点像是hook函数,并且修改函数,
######
装饰器---带参数的装饰器
def info(value): def outer(func): def inner(*args, **kwargs): print(value) return func(*args, **kwargs) return inner return outer @info("456") def say_hello(): print("同学你好") say_hello()
####
所以带参数的装饰器,就是在装饰器外部再加一层函数嵌套,就可以实现了
###
装饰器---多个装饰器
####
def outer1(func): def inner(*args, **kwargs): print("outer 1 before") ret = func(*args, **kwargs) print("outer 1 after") return ret return inner def outer2(func): def inner(*args, **kwargs): print("outer 2 before") ret = func(*args, **kwargs) print("outer 2 after") return ret return inner @outer2 @outer1 def say_hello(): print("同学你好") say_hello() """ outer 2 before outer 1 before 同学你好 outer 1 after outer 2 after """
####
这样你就了解了,在函数前面的装饰器内容,是从上往下执行的,而在函数后面的装饰内容,是从下往上执行的,
就和套娃一样的概念,
###
内置装饰器
有三种我们经常会用到的装饰器, property、 staticmethod、 classmethod,他们有个共同点,都是作用于类方法之上。
#####
迭代器和可迭代对象的区别
from collections import Iterable from collections import Iterator print(isinstance([], Iterable)) # true print(isinstance([], Iterator)) # false 列表不是迭代器,
print(dir([])) class A: def __iter__(self): pass def __next__(self): pass a = A() print(isinstance(a, Iterable)) # true print(isinstance(a, Iterator)) # true # 由此可以证明,只有有__iter__',__next__(),这两个方法就是迭代器,
认知:
1,迭代器协议,要同时具有__iter__',__next__(),这两个方法就是迭代器,,可迭代的不一定是迭代器,因为要有next方法,
2,可迭代协议,只要能被for循环就是可迭代的,如果是迭代器一定是可迭代的,因为里面有iter方法,
3,我们常见的可以迭代的对象,list,dict,元组,集合,文件,range(),enumerate
4,使用迭代方法的好处:1.可节省内存空间,2.会从容器里面挨个取值,直到取完为止
5,之前讲的iter方法和next方法,工作中基本不会用到,因为for循环已经做了这件事,这是为了讲解迭代器的原理,还有就是为了生成器做铺垫,
#####
生成器---使用生成器函数创建,yield,
def func1(): print(1) yield "a" print(2) yield "b" ret = func1() print(ret) # <generator object func1 at 0x7f8f6cabb6d0> print(ret.__next__()) # 1 a print(ret.__next__()) # 2 b for i in ret: print(i)
###
1,这个生成器也有__iter__(),__next__())两个方法,所以生成器也是一个迭代器,
2,只要有yield关键字的函数就是生成器函数,
3,yield只能写到函数里面,外边不能写,yield不能和return共用,yield不会把代码结束,return会把代码结束,
4,生成器既然也是迭代器,所以可以进行for循环,
####
生成器---使用生成器表达式创建,
# 生成器表达式 # 列表推导式, list1 = ["鸡蛋%d"%i for i in range(10)] print(list1) # 生成器表达式 g = (i for i in range(10)) print(type(g)) # <class 'generator'> # g是一个生成器, # 和列表推导式的不同点 # 1,括号不一样, # 2,返回值不一样, # 生成器表达式几乎不占用内容,这是它的优点,缺点就是返回值还是一个表达式,取值需要第二步, # 生成器表达式只能做简单的事情,想要复杂的功能的生成器还是生成器函数, # 这些推导式你不会没有任何影响,使用for循环和生成器函数都能解决,只是面试的时候会有,别人的代码会有,所以你要掌握
######
递归函数
############### 递归 ############## # 递归的定义——在一个函数里再调用这个函数本身 # 递归的最大深度——998 # 二分查找算法 # 你观察这个列表,这是不是一个从小到大排序的有序列表呀? # 如果这样,假如我要找的数比列表中间的数还大,是不是我直接在列表的后半边找就行了? l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88] def search(num,l,start=None,end=None): start = start if start else 0 end = end if end is not None else len(l) - 1 mid = (end - start)//2 + start if start > end: return None elif l[mid] > num : return search(num,l,start,mid-1) elif l[mid] < num: return search(num,l,mid+1,end) elif l[mid] == num: return mid
#####
匿名函数
def func1(n): return n+n func2 = lambda x:x+x func3 = lambda x, y: x * y print(func1(3)) # 6 print(func2(3)) # 6 print(func3(3,3)) # 9
####
认知:
1,匿名函数的定义,func = lambda x : x**x,,函数名 = lambda 参数 :返回值
2,参数 参数可以有多个,用逗号隔开,参数也可以使用默认值参数,
3,返回值和正常的函数一样可以是任意数据类型
4,匿名函数不管逻辑多复杂,只能写一行,且逻辑执行结束后的内容就是返回值,
5,这个匿名函数经常可以和filter和map函数进行结合使用队序列进行操作
#####