python - 函数

函数

  • def add(x,y): 函数名add(标识符)对应一个内存中的一个函数对象,因此也可以有多个标识符同时指向这个对象,同样可以调用。
  • 再次定义会创建一个新的function对象,不是对原来对象的修改;add(标识符)指向这个新的对象,原来的函数对象引用计数减一。
  • 函数调用:add(2,3) 将(2,3)传入add指向的函数对象中进行计算
  • 函数是一个可调用对象;callable(add) ==> True
  • 函数传参
    1、位置传参;2、关键字传参;3、可变位置传参;4、keyworld-only;5、可变关键字传参
  • 关键字参数必须在位置参数后面,每个参数只能接受一个值,否则多值错误;位置参数可以使用关键字传参
def sum(*iterable):
    print(type(iterbale))  # iterbale ==> 元组,不传入为空元组
    for i in iterable:
        pass
def sum1(iterable):
   print(type(iterbale))
       for i in iterable:
           pass
sum(x for i in range(10)))  # 传入生成器
sum1(range(5)))  # 传入一个对象
  • 可变位置参数:收集所有位置参数作为一个元组。
  • 可变关键字参数:收集所有关键字参数作为一个字典。
def fn(a, b=5, *args, **kwargs):
    pass
fn(5, a=2, c=3, b=4,z=5) 
##位置参数和关键字参数混用,关键字参数可以乱序,多余的kwargs收集为字典
  • keyword-only
    def fun(x=4, *args, y, **kwargs):  
    # y只能接受关键字参数,keyword-only参数
    pass
    def func(*args, x=0, y):  # 正确,keyword-only 默认值可以在前
    pass
    def func1(*,m): # m 必须为关键字传参
    pass    
  • 元组,列表,字符串,迭代器,生成器,range对象 均可以解构
  • 字典解构传参
def fun(a, b):
    pass
fun(**{"a":1,"b",2})               # ==> fun(a=1,b=2)
fun(*{"a":1,"b",2}.values())   # ==> fun(*(1,2))
  • 函数返回值:使用return 返回,函数执行return后函数结束,并返回return 指定的内容,未定义return默认return None
  • 函数作用域:函数定义的变量只在该函数内部,函数的局部作用域,也称本地变量
  • 全局变量、全局作用域:全局可见,可以向内部穿透,在函数内部可见

    函数嵌套:

  • 执行函数定义的时候不会执行内部的语句,内部定义的函数无法执行
    def func():
    x = 1
    def fn():
        x = x + 1  #  调用时将会报错,未定义本地x的值之前,使用了本地的x
  • global:声明全局变量,该作用域中的该变量将会在全局中寻找该变量。
    def func:
    global x # 声明全局作用域,必须是全局作用域,上一层使用的nonlocal
    x = 1  # 在函数中可以定义全局变量,且执行后外部可用
    x += 1 
    闭包
  • 闭包:函数嵌套使用时,内层函数使用到了外层函数定义的局部变量,形成了闭包。
  • python 中实现闭包的方式:
    1.内部可以外部引用类型的变量,并可对其进行内部修改
    def func():
    x = [1]
    def fn():
        x.append[4]
    return fn
    foo = func()
    foo()   #  每次输出将会在上一次的结果上增加一个元素;x 的操作与外部形成了闭包

    2.也可以使用nonlocal实现闭包:内部变量声明使用外部定义的变量

def func():
    x = 1
    def fn():
        nonlocal x  #声明该x 是外部变量 x  
        x = x + 1
    return fn
foo = func()
foo()  # 每次调用x 结果加1,foo函数未消亡,x 将不会被清除
  • 如果在内部函数中定义一个变量,该变量名与外部变量名相同,那么这个变量只会使用内部的定义的这个值,且需要满足先赋值,后使用
    def func():
    x = 1
    def fn():
        y = x + 1  # 此处使用了x 变量,但是在下一行才定义x 所以错误
        x = y   #  此处定义了与外部变量同名变量,在本函数内部所有范围内只能用该变量
        # 如果没有第5行对x的重新定义,第4行中x将会使用外部的x变量
  • nonlocal:非该函数本地的变量,但是也不能是全局中的变量,在其他的任意一层定义即可使用该变量;函数多层嵌套时,使用nonlocal将会一层层向外部寻找该变量,直到最外层函数(非全局变量,不会匹配同名的global变量名)
    def fn():
    global c
    c = 0
    def fn1():
        c = 0
        def fn2():
            def fn3():
                nonlocal c  # 会使用5行的c值,与第3行的c不是同一个比变量
    #若没有第5行的c定义,也无法匹配第3行的c变量。nonlocal无法匹配global变量  
  • global和nonlocal都只是申明本函数作用域内该元素,并向上匹配寻找

    默认值的作用域

  • 当函数定义之后,会在内存中生成一个函数对象 ,此时函数已经定义了许多的方法和属性(可以理解为函数的元数据),例如函数名字,默认值等,函数参数的默认值已被加载到函数的两个属性之中保存,分别为'. defaults','kwdefaults'中
    def fun(a, b=1, c={}, *d , m=1, **kw):
    a = 1
    x = 1
    return x
    dir(fun) # 查看该函数定义后的已经存在属性
    '''
    ['__annotations__', '__call__', '__class__', '__closure__',
    '__code__', '__defaults__', '__delattr__', '__dict__',
    '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
    '__get__', '__getattribute__', '__globals__', '__gt__', 
    '__hash__', '__init__', '__init_subclass__', '__kwdefaults__',
    '__le__', '__lt__', '__module__', '__name__', '__ne__', 
    '__new__', '__qualname__',  '__reduce__', '__reduce_ex__',
    '__repr__', '__setattr__', '__sizeof__', '__str__',
    '__subclasshook__']
    '''
    # 默认值被保存到 __defaults__ 和 __kwdefaults__中,分别保存位置参数
    # (使用元组)和kw-only参数(使用字典)
    fun.__defaults__       # ==> ( 1, {} )   保存了b和c的缺省值
    fun.__kwdefaults__     # ==> {'m': 1}     保存了m 的默认值
  • 注:当默认值为引用类型时,使用变量操作时可能会引起默认值的改变(在使用默认值时,进行一些引起本对象的操作就会引起默认值改变)
    如:list,bytearray,set,dict等可变类型对自身修改的操作,可变序列的 += 操作
    def fun(x = [], b=1):
    x.append(1)   # 此时 x 是默认值列表中对象,直接操作默认值参数的对象
    x += [1]         # += 执行列表的extend,也是对x本对象的改变
    b = 2             #b原本指向默认值列表中的1,现在指向新的对象2,原对象未改变
  • LEGB原则:寻找一个变量时候的先后顺序
    python - 函数
  • 当内部定义一个与全局变量或者build-in中相同的变量名时,将会使得全局或者build-in中的函数无法在本程序内使用,例如:
    print = 1   # 定义一个变量为print,此时print指向一个int对象,
    print(1)     #  此时将会报错,默认先使用本文件中的print,而不是build-in中

    匿名函数

    1. 匿名函数普通定义
    2. 定义后可以使用变量接收,此时等同于 def
    3. 创建对象后直接调用,在函数后部直接()进行调用
      特点:简洁,快速实现某种功能,一次性创建使用,节省内存。
      lambda x : x + 1    #  返回值===> 对象,":"后不能出现赋值表达式
      foo = lambda x : x + 1  # ===> 返回的对象使用一个变量接收,foo可调用
      (lambda x : x+1)(1)  # ==> 直接在创建后调用,()+ 传入参数调用
  • 使用场景
    构造一个类型,完成某种功能
    # 返回一个和str功能相同的函数对象,效果相同
    sorted([1,2,3,"a"], key = str)
    sorted([1,2,4,"a"], key = lambda x :str(x))
    # 可以定制一些自己的比较方式,例如:
    sorted([1,2,4,"a"], key = lambda x :hash(x)) # 使用元素哈希值排序
    sorted([1,2,4,"a"], key = lambda x :func(x)) # func为自己定义的函数,实现自定义
  • 构造某些类型,使用其返回值
d = defalutdict(lambda :0)
for k in "abc":
    d[k] += 1   # 第一次将会调用lambda并返回初始值0值,完成累加

d = defalutdict(lambda :[])    # 构造列表 == list
for k in "abc":
    d[k].append(1)
  • 嵌套使用,可用于闭包
    def fun():
    x =  [ ]
    return lambda  a : x.append(a)   # 匿名函数使用外层函数x,形成闭包

    递归函数

  • 函数调用过程:函数定义后,会在内存中生成函数对象,在调用函数时,函数将会在main函数之上压栈,对应的参数分别进行压栈,函数全部执行结束,函数对象进行弹栈,继续执行main函数;如果函数未进行弹出栈时调用新的函数,函数将会继续压栈,直到栈上层函数执行完成弹出后下层函数才会依次弹出;先进栈函数后出栈,后进栈函数一定会先弹出才能弹出下层函数
  • 递归函数:在自己函数内部反复调用自己,形成一个循环的调用。
  • 间接递归:A调用B函数,B调用C,C调用A形成间接的递归调用
  • 注:递归必须有边界条件,达到某个条件将对停止自身调用,并进行一层层的return 返回,这是一个“逆向”的操作,先执行内部函数,在执行外层函数 ;
    内层函数的 return 值将会成为外一层函数调用结果,注意 return 值的控制。
  • 递归思路:找出递推公式,即:F( n) = f ( n - 1) 的关系,有了该关系,基本可以写成以下形式
  • def func(n):
    if n == ?                   # 退出条件,可能不止一个
        return ?               # 一个确定的值,不可以再调用自身
    return func( n - 1 )  #  根据实际递推公式变化    
  • 递归分析: 分析返回结果 习惯从最里层分析。
  • 递归最大深度:无限递归将会消耗所有的栈资源,所以python中对递归层数进行了限制,查看方法:
    import sys 
    print(sys.getrecursionlimit())  # ==> 最大递归数
  • 递归会使用大量函数的压栈,开辟和清理新的栈空间需要时间和资源,效率较低;一般不使用

生成器函数

yield关键字
  • yield只能在函数中使用,当函数中有yield关键字,该函数执行时将会返回一个 生成器对象,每次使用 next驱动,遇到yield语句暂停执行,等待下一次next,但当执行到return 语句时,将结束生成器运行。
    def func():
    for i in range(3):
        yield i                   #  执行到yield,返回一次结果,并暂停函数执行 
    f = func()                     # 调用函数将不会执行,而是返回一个生成器对象
    next(f)                        # 使用next(),返回每次yield值 
    每次调用函数func 将会返回一个新的生成器,不能直接对func()进行操作,否则每次生成新的对象。
  • 每次调用函数func 将会返回一个新的生成器,不能直接对func()进行操作,否则每次生成新的对象。
  • 函数中如果有return语句,执行return语句将会结束函数,但是无法拿到return返回值。
  • 使用next函数执行到最后将会报错,StopIteration
    .send()方法
  • send()是生成器对象的一个方法,会推动生成器函数执行,直到遇到 yield 语句,并接受返回的yield 的值,同时,send会将实参传入生成器函数内部yield语句,作为yield函数体内部执行后的返回值,从而实现生产器函数内部的一种“交互”
    def foo():
    i = 0
    while True:
        i += 1
        res = yield i           #yield 的默认返回值None,外部使用send后将返回send值
        print(res)
    g = foo()
    g.send("abc")                # 执行next(g),并将"abc"作为生成器g内部yield的返回值 

    使用案例:重置计数器,send任意值重置计数

    def foo():
    i = 0
    while True:
        i += 1
        res = yield i                # 第一次执行会返回i, 然后暂停函数的执行
        if res is not None:      # 当收到send值,重置计数器i
            i = 0
        print(res)                    # 下一次next,才会使用res 即"abc"
    g = foo()                             # 生成器对象
    g.send("abc")
    yield from
    # 每一次执行返回可迭代对象中的一个值
    def foo():
    yield from [1,2,3]                 # 接iterable

    高阶函数

    定义及实现

  • 高阶函数:参数中至少有一个函数,或者返回值是一个函数对象的函数
    下面 key 参数的实现为高阶函数的实现原理,利用高阶函数,自行实现sorted排序函数
    a = [8, 7,6, 47, 464, 4, 32,3,2]
    def sort(nums, *, key=None, reverse=False):
    res = []
    for x in nums:
        for i, v in enumerate(res):                          #第一次不会for,append第一个元素
            cx = key(x) if key else x                        #key实现
            cv = key(v) if key else v                        #只用于比较,插入时使用原值 
            cmp = cx > cv if reverse else cx < cv    # reverse实现
            if cmp: 
                res.insert(i,x)                                     # 找到插入位置i , 插入 x 
                break
        else:
            res.append(x)
    print(res)
    sort(a, reverse=1, key=str)

    常用高阶函数

    sorted()
    排序函数:返回排序好的原列表

    # sorted(iterbale, *, key=None, reverse=False)
    # 结合 lambda 实现特定功能
    # 使用 lambda 按照其lambda返回值的数值大小依次进行排序
    sorted(a, key=lambda x: abs(x-10) )     # 以x-10的绝对值大小排序
    sorted(a, key=lambda x: 1)                   # 原来的序列

    filter()

  • 将可迭代对象中的每个元素放入function中,将function 返回值为等效false 的元素进行过滤
  • function接受一个参数,且返回值应当是bool类型,或其返回值等效布尔值
  • function参数如果是None,可迭代对象的每一个元素自身等效布尔值
    # filter(function, iterable)  ==> 迭代器,惰性对象
    list(filter(int, ["0", "12"]))             # ==> ["12"]  #int("0")后等效false
    list(filter(lambda x: int(x), ["0", "12"]))                 # 同上功能
    filter(None, [None, 0, "False", False, [],"",(), {}])  # ['False']

    map()

    # map(self, /, *args, **kwargs)
    # map(func, *iterables) --> map object  # 返回map对象,惰性  
    map(str, range(5))                         # [ '0, '1', '2', '3', '4']
    map(lambda x: str(x), range(5))       # 同上
    map(lambda x,y: (x,y), "abcde", range(10))      # 类似zip,返回组合后的元组