python学习之函数
- 三元运算表达式
- a = 1
- b = 2
- c = a if a > b else b # c 等于 如果a大于b得a,否咋等于b
- 编程方式
- 1、面向过程式编程:根据业务逻辑从上到下垒代码实现功能
- 2、函数式编程:将某功能代码封装到函数中,日后使用相同功能时,无需重复编写,直接函数调用即可
- 3、面向对象编程:对函数进行分类和封装,让开发“更快更好更强”
- 函数式编程
- 函数定义
- 将可以实现某种功能,可重复使用的,组织在一起的代码块
- 函数的作用
- 增强代码的重用性和可读性
- 函数语法
- def 函数名 (参数可有可无)
- 函数体(代码块)
- return
- def 函数名 (参数可有可无)
- 函数返回值 return
- 函数执行完毕后,返回给调用者的结果(调用者需通过定义变量接受)
- 函数一遇到return就结束,不再执行后面的代码,结束函数
- 不写return ,python内置规定默认返回None
- return None ,返回None
- return 单个值 ,返回此单个值
- return 多个值 ,将多个值包含在(元组)中,返回给调用者(可通过定义解构来直接接受)
- 函数参数
- 形参:函数定义时,声明的变量即括号里的变量
- 位置参数
- 默认参数
- def fun(a,b,c ="默认值"):
- 1、必须先声明位置参数,才能声明默认参数
- 动态参数
顺序:位置参数,*args,默认参数,**kwargs
- 1、第一种:动态的位置参数
- 语法:def fun(*args):pass
- 传参:将多个参数以元组的形式传给函数
- args 是元组,它包含了所有实参传过来的位置参数
- 2、第二种:动态的关键字参数
- 语法:def fun(**kwargs): pass
- 传参:将多个参数以字典的形式传给函数,传参格式必须为“key”= “value”
- kwargs 是字典,它包含了所有实参传过来的关键字参数
- 3、第三种:无敌传参
- 语法:def fun(*args,**kwargs):pass
- *,**的作用:
- 形参:聚合 (将元素聚合成元组,字典)
- 实参:打散 (打散成各个元素,当变量传给函数)
- 1、第一种:动态的位置参数
- 实参:调用者调用函数时实际给函数传的值
- 位置参数
- 实参与形参个数、位置必须一一对应
- 关键字参数
- 混合参数
- 1、位置参数与关键字参数混合使用
- 2、必须先写位置参数、后关键字参数,不然报错:(SyntaxError: positional argument follows keyword argument)
- 位置参数
- 作用域:
- 在计算机执行自定义的函数时,计算机只会把函数名放在内存中,而函数体不会放在内存当中,因此在pycharm执行函数时,并不会真正的运算函数,函数内的语句题没有执行,只有遇见函数名()时才会在内存中圈定一块函数的运行区域,使函数开始执行。
- 全局命名空间
- 直接在py文件中,函数外声明的变量都属于全局命名空间
- 局部命名空间
- 函数中声明的变量会放在局部命名空间
- 内置命名空间
- 存放python解释器提供的变量名称,list,tuple,str,int这些都是内置命名空间
- 加载顺序:
- 内置-全局-局部
- 取值顺序
- 局部-全局-内置
- 作用域命名空间
- 1、全局作用域:全局命名空间+内置命名空间(函数外面的)
- 2、局部作用域:局部命名空间(函数里面的)
- 查看方式:
- 1、globals()函数查看全局作用域中的内容
- 2、locals()函数查看局部作用域中的变量和函数信息
- 改变变量的作用域
- 1、global 变量 将函数局部作用域中的变量改成全局变量(也就是调用全局)
- 2、nonlocal 变量 将当前局部作用域变量,改成上一级作用域中的变量(调用上级作用域)
如果上一级没有该变量,继续找上一级,直到找到为止(非全局),否则返回空
- 函数名
第一类对象:可以在运行期调用、可用作函数参数或返回值,可存入变量的实体(当普通变量使)- 1、函数名的内存地址,print(func),它是函数整体的地址;id(func)是单纯func这个变量名的地址
- 2、函数名可以赋值给其他变量 如:f = func
- 3、函数名可以当做容器类的元素 如:【func1,func2】或【func1(),func2()】
- 4、函数名可以当做函数的参数 如: def f(func)
- 5、函数名可以当函数的返回值
- 闭包
- 在python中对闭包有特殊的机制,遇到闭包,它会在内存中开辟一个空间,不会随着函数的结束而结束,所以方便了多次的调用,减少多次对内存空间的关闭或打开,增加了代码块的执行效率。
内层函数对外层函数(非全局)的变量的引用- 检测是不是闭包,函数名.__closure__ #cell
通过nolocal引用的上一级局部变量,该函数也是闭包 - 装饰器都是闭包函数
- 语法范例
- def func(f):
- a = 0
- def inner():
- print("此处调用外层函数的变量a或f,故inner函数为闭包函数",a)
- return inner
- def func(f):
- 检测是不是闭包,函数名.__closure__ #cell
- 高阶函数
- 两条件满足其一即可称为高阶函数
- 1、函数名作为函数的返回值 return func_name
- 2、函数名作为参数传入 func(fun_name)
- 两条件满足其一即可称为高阶函数
- 装饰器(wrapper)
- 定义:在不改变原函数的功能外(及调用方式),添加或扩展额外的功能
- 开闭原则:对代码的扩展是开放的,对代码的修改是封闭的
- 语法范例:(手写装饰器)
- def wrapper_name (func):
- def inner(*args,**kwargs):
- """计划在执行功能函数前扩展的功能"""
- ret = func(*args,**kwargs)
- """计划在执行功能函数后扩展的功能"""
- return ret
- return inner
- def inner(*args,**kwargs):
- def wrapper_name (func):
- 装饰器调用:
- func = wrapper_name(func)
- func()
- 语法糖调用:
- 在需要扩展新功能的功能函数上方@装饰器函数名
- @wrapper
- 形参:函数定义时,声明的变量即括号里的变量
- 函数定义
- 带参数的装饰器
- 1、为了自定义是否使用扩展功能,更智能化,可以为是否使用扩展功能加个开关
- 2、具体就是在装饰器函数外面再嵌套一层函数wrapper_out(flag),设定一个形参flag,返回值为装饰器函数名wrapper
- 3、在inner函数里加一层判断flag为True使用附加功能,False为不添加附加功能
- 3、在功能函数上加上@wrapper_out(True或者Falsh)
-
1 # 带参数的装饰器 2 # # 自定义是否使用添加的功能,故需要定义一个开关来是否执行通过装饰器添加的功能 3 # # 1、如果在wrapper函数里添加的参数,就改变了调用装饰器的方式,因为调用装饰器时并没有给与参数:@wrapper 4 # # 2、如果在inner函数里再添加参数,而inner函数里所有的参数都会给原功能函数,就会导致原功能函数执行异常 5 """ 3、****故只能在wrapper函数外层再添加一层函数,加一个参数作为开关,来决定是否使用扩展功能,这就是装饰器添加参数***""" 6 def wrapper_choice(flag): # falg 是定义是否使用扩展功能的开关参数 7 def wrapper(func_name): 8 def inner(*args, **kwargs): 9 if flag == True: # 就使用扩展的功能 10 '''函数执行前添加的功能代码块''' 11 ret = func_name(*args, **kwargs) 12 '''函数执行后添加的功能代码块''' 13 return ret # 返回执行结果 14 else: # 直接执行原函数,不要扩展功能 15 ret = func_name(*args, **kwargs) 16 return ret 17 return inner # 返回带有新功能且可以执行原函数的函数名 18 return wrapper # 返回装饰器函数名 19 20 @wrapper_tell_you(True) # 先执行右边的wrapper_tell_you(True)返回wrapper装饰器函数名,从而形成@wrapper 21 def fun2(*args, **kwargs): 22 pass 23 fun2() # 调用,相当于inner()调用
-
- 装饰器的嵌套
- 功能函数嵌套多层装饰器,记住取值顺序就不怕嵌套多少了
- 装饰器中函数前添加的功能,函数后添加的功能可以用作括号来表示,中间是被装饰函数
- 【 ( 被装饰函数 ) 】
- 从左至右取值
-
1 # 多个装饰器嵌套 2 '''取值顺序:[ ( 目标 ) ] 从左到右显示, 3 最外层装饰器函数执行前功能、 4 内层装饰器函数执行前功能 5 原功能函数 6 内层装饰器函数执行后功能 7 最外层装饰器函数执行后功能''' 8 def wrapper1(func1_name): 9 def inner(*args, **kwargs): 10 print("上车") 11 ret = func1_name(*args,**kwargs) 12 print("下车") 13 return ret 14 return inner 15 16 def wrapper2(func2_name): 17 def inner(*args, **kwargs): 18 print("出发了……") 19 ret = func2_name(*args, **kwargs) 20 print("到站了……") 21 return ret 22 return inner 23 24 def wrapper3(fun3_name): 25 def inner(*args, **kwargs): 26 print("计划去东莞了……") 27 ret = fun3_name(*args, **kwargs) 28 print("不虚此行,质量不错……") 29 return ret 30 return inner 31 32 @wrapper3 33 @wrapper2 34 @wrapper1 35 def by_car(): 36 print("去浪了……") 37 return 38 39 by_car() 40 ''' 41 结果为: 42 计划去东莞了…… 43 出发了…… 44 上车 45 去浪了…… 46 下车 47 到站了…… 48 不虚此行,质量不错…… 49 '''
- 函数的注释:
- 在函数体第一行用三个双引号将需要注释的内容写在里面,包括函数是干嘛的、函数的参数、函数的返回值
- 语法:
- def func_name(args):
- """函数的注释内容"""
- pass
- def func_name(args):
- 函数注释的查看:print(func_name.__doc__)
- 函数名的查看:(因如果函数被装饰过,就不是它本来的名字了,而是装饰器返回来的inner)
- 1、未被装饰过的函数名查看方法:print(func_name.__name__)
- 2、被装饰过的函数需先导入模块、再在装饰器inner函数上面加一个装饰器@wrap(func_name)
- 2.3、通过print(fn.__name__) 查看到的就不是inner而是本身的fn函数名了
-
1 # 函数名的显示 2 ''' 3 1、未加装饰器函数的函数名查看 4 print(func_name.__name__) 5 2、加了装饰器的函数,因为看起来还是原来的函数名,实际是返回来的inner函数名, 6 故python做了一个优化,加了个功能,将函数名转换回来 7 2.1、导入模块 from functools import wraps 8 2.2、在inner函数上方加一个固定装饰器 @wraps(func_name) 9 ''' 10 from functools import wraps 11 def wrapper(fn): 12 @wraps(fn) # 这个代码的作用,将inner的__name__替换成fn的__name__ 13 def inner(*args, **kwargs): 14 print("功能1") 15 ret = fn(*args, **kwargs) 16 return ret 17 return inner 18 @wrapper 19 # 原功能函数 20 def fun2(*args, **kwargs): 21 pass 22 print(fun2.__name__) # fun2
- 迭代器
-
- 迭代器协议:必须为可迭代对象提供一个next方法,执行该方法要么返回迭代的下一项,要么就引起一个stopIteration的异常,终止迭代(只能往前走,不能回退)
- 可迭代对象iterable:内部含有__iter__方法的对象、遵循可迭代协议、str、list、tuple、dict、set、range(int、float不是)
- 判断是否为可迭代对象:print("__inter__" in dir(对象)) 或 from collections import Iterable;print(isinstance(对象,Iterable))
- 迭代器iterator:内部含有__iter__和__next__方法的对象、遵循迭代器协议
- 判断是否为迭代器:print("__iter__" and "__next__" in dir("djsks".__iter__())) 或 from collections import Iterator;print(isinstance(对象,Iterable))
- 可迭代对象转成迭代器:对象.__iter__()
- 迭代器的好处:节省内存、满足惰性机制(运行一次在内存中取一次值)、不能反复取值,只能从前到后,下一次接着上一次后面取,不可逆
- 有哪些用了迭代器:for、sum、max、min等
- for循环函数就是迭代器最好的例子
- 1、通过__iter__()方法将可迭代对象转换成迭代器【对象.__iter__()】
- 2、通过__next__()方法进行取值【对象.__next__()】
- 3、通过异常处理机制规避报错
- -----------------------下例以while模拟-------------------------------
- lis = [11,33,44,55,66]
- while True:
- try:
- gn = lis.__iter__()
- print(gn)
- except Exception:
- break
- try:
- -------------------------end--------------------------------------------
-
- 生成器
-
- (一般是函数)内部带有关键字yield的对象,生成器的本质是迭代器,遵循迭代器协议(所有的生成器一定是迭代器,反之不一定)
- 函数中如有yield就不是普通的函数而是生成器,yield功能类似如return,为调用者返回函数执行结果,但yield后还有编码,不结束,而return是直接结束函数
- --------------------示例------------------------
- def func():
- print("你想吃啥")
- a = yield 2222
- print(222)
- yield 3333 # 出现yield就是生成器
- gn = func() # gn就是迭代器
- print(gn.__next__()) # 生成器调用元素,到第一个yield结束,等待第二次继续调用
- print(gn.__send("红烧鲤鱼")
- -----------------------------------------------------
- send()和next()执行方法一样,都是返回一个迭代器内部的元素,使光标向后移动一个位置,但send可以上上一个yield传递一个参数,默认传none,改变上一个yield的值;第一次调用不能用send的因为第一个前面没有yield;最后一个元素不能改变其值
-
人生苦短,我用python!