函数

函数

函数介绍

  • 什么是函数:函数就是盛放代码的容器,把实现某一功能的一堆代码丢到一个函数中,做成一个工具

  • 使用函数

    • 原则:先定义后引用
    • 定义阶段:函数体代码不执行,只会检测语法
    • 调用阶段:先通过函数名找到函数的内存地址,然后'函数的内存地址()'会触发函数体代码的运行
  • 返回值:用return来控制函数的返回值

    • 函数中可以有多个return,但只要return执行一次,这个函数就会立刻结束,并返回return后跟着的值
    • 当函数没有return或者return后不跟值时返回None
    • 当函数的return后跟了多个值时,会返回一个元组,可以用解压赋值拆解使用
  • 函数定义的三种方式

    # 1. 无参函数
    def func():
        print('1')
    # 2. 有参函数
    def func(x):
        print(x)
    # 3. 空函数
    def func():
        pass
    
  • 调用函数的三种方式

    # 语句形式
    print()
    # 表达式形式
    res = input()
    # 可以把函数的调用当作参数传给另一个函数
    res = int(input())
    

函数的参数

  • 形参:在函数定义阶段括号内定义的变量,称之为形式参数,简称形参
  • 实参:在函数调用阶段括号内 传入的值,称之为实际参数,简称实参
  • 两则的关系:实参的值会绑定给形参名,让后可以在函数内使用,函数调用结束后,解除绑定
  1. 位置形参:在函数定义阶段按照从左往右的顺序依次定义的形参

  2. 默认形参:在函数定义阶段就已经为某个参数赋值。在调用阶段可以不必为其赋值,默认形参可以与位置形参混合使用,但位置形参必须在默认形参前面。默认形参的值只在函数定义阶段被赋值一次,默认形参的值通常是不可变类型

  3. 位置实参:在函数调用阶段按照从左往右的顺序依次传入的值,必须与形参一一对应

  4. 关键字实参:在函数调用阶段按照key=value的格式传入的值,可以打乱顺序,可以与位置实参混合使用,但位置实参必须在前,且不能为同一个参数赋值

  5. 可变长参数, 在函数调用阶段,实参的个数不固定,而实参是为了形参赋值的,所以对应着必须有一种特殊格式的形参

    1. 可变长形参
      • 形参中的 * :*会接收输入的位置实参,然后将其组成元组,赋值给紧跟其后的变量名
      • 形参中的 ** :** 会接收输入的关键字实参,将其组成字典,赋值给紧跟其后的变量名
    2. 可变长实参:
      • 实参中的 * :* 后面跟的必须是一个可迭代对象, *会将其打散成位置实参
      • 实参中的 ** :** 后面必须跟的是一个字典,** 会将其打散郑关键字实参
    ===========*args===========
            def foo(x,y,*args):
                print(x,y)
                print(args)
            foo(1,2,3,4,5)
    
            def foo(x,y,*args):
                print(x,y)
                print(args)
            foo(1,2,*[3,4,5])
    
    
            def foo(x,y,z):
                print(x,y,z)
            foo(*[1,2,3])
    
            ===========**kwargs===========
            def foo(x,y,**kwargs):
                print(x,y)
                print(kwargs)
            foo(1,y=2,a=1,b=2,c=3)
    
            def foo(x,y,**kwargs):
                print(x,y)
                print(kwargs)
            foo(1,y=2,**{'a':1,'b':2,'c':3})
    
    
            def foo(x,y,z):
                print(x,y,z)
            foo(**{'z':1,'x':2,'y':3})
    
            ===========*args+**kwargs===========
    
            def foo(x,y):
                print(x,y)
    
            def wrapper(*args,**kwargs):
                print('====>')
                foo(*args,**kwargs)
    
  6. 命名关键字参数,在 * 后定义的参数,必须被传值(哟默认值的除外),且必须按照关键字实参的形式传值

    def foo(x,y,*args,a=1,b,**kwargs):
    	print(x,y)
    	print(args)
    	print(a)
    	print(b)
    	print(kwargs)
    
    foo(1,2,3,4,5,b=3,c=4,d=5)
    结果:
    1 2
    (3, 4, 5)
    1
    3
    {'c': 4, 'd': 5}
    

类型提示

在定义函数是可以加上参数或则返回值的类型的提示

def func(s:int,y:str)->int:
    print(s,y)
    return 0

函数对象

函数是第一类对象,即函数可以当作数据传递

  1. 可以被赋值,引用
  2. 可以当作参数
  3. 可以当作函数返回值
  4. 可以当作容器对象
def func():
	pass

f=func
f()

print(func)

def foo(func):
return func

l=[1,func]
  • 该特性可以用于取代多分支的if

函数嵌套

  • 有嵌套调用和嵌套定义
  • 在函数内的调用和定义
# 函数的嵌套定义
def func():
    def func2()
        pass

    func2()
func()
# 函数的嵌套调用
def max1(a,b):
    return a if a>b else b
def max2(x,y,i,j):
    return max1(max1(x,y),max1(i,j))

名称空间与作用域

  • 名称空间;存放名字与其对应的内存地址的地方,三种名称空间及其加载顺序

    1. 内置名称空间:存放的是python解释器自带的名字,python解释器启动时产生,解释器关闭就销毁
    2. 全局名词空间:存放的是顶级的名字,py程序运行时产生,程序结束就销毁
    3. 局部名称空间:在执行文件过程中如果调用函数,才产生的
  • 名字的查找顺序:局部名称空间 --> 全局名称空间 --> 内置名称空间

    • 在全局无法查看局部内的名称,而在局部可以查看到全局的
  • 作用域

    #1、作用域即范围
    - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
    - 局部范围(局部名称空间属于该范围):临时存活,局部有效
    #2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
    x=1
    def f1():
        def f2():
            print(x)
        return f2
    x=100
    def f3(func):
        x=2
        func()
    x=10000
    f3(f1())
    
    #3、查看作用域:globals(),locals()
    LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
    locals 是函数内的名字空间,包括局部变量和形参
    enclosing 外部嵌套函数的名字空间(闭包中常见)
    globals 全局变量,函数定义所在模块的名字空间
    builtins 内置模块的名字空间
    

闭包函数

  • 闭函数:被封闭起来的函数 -->定义函数内部的函数,闭函数的特点是只能在函数内使用

  • 包函数:该函数引用了一个名字,该名字来自于e这一层

  • 总结:闭包函数指的是"定义在函数内部的函数",它引用了一个"来自于外层函数作用域中的名字"

  • 闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

  • 应用领域:延迟计算(原来我们是传参,现在我们是包起来)

    方案一:直接以参数的形式传入
        def wrapper(x):
            print(x)
        wrapper(10)
    方案二:
        def outer(x):
            def wrapper():
                print(x)
            return wrapper
        f=outer(10)
        f()
    

装饰器

  • 装饰器就是闭包函数的一种应用场景

  • 装饰器是指我们为被装饰对象添加新功能的一个工具,遵循以下两个原则

    1. 不修改被装饰对象源代码
    2. 不修改被装饰对象的调用方式
  • 装饰器必须遵循“开放封闭原则”

    • 开放指的是对拓展新功能的开放,封闭指的是对修改源代码以及调用方式是封闭的
  • 装饰器模板

    # 无参装饰器
    def outer(func):
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            return res
        return wrapper
    # 有参装饰器
    def outer2(mode):
    	def outer(func):
    		def wrapper(*args,**kwargs):
    			res = fun(*args,**kwargs)
    				mode
    			return res
    		return wrapper
    	return outer
    
  • 装饰器使用:语法糖 @装饰器名字

    无参装饰器
    @outer  # index = outer(index)
    def index():
    	pass
    # 有参装饰器
    @outer(x=10)
    def index():
    	pass
    
  • wraps装饰器:对装饰器装饰,不改变被装饰对象的函数结构,将装饰后的函数伪装成被装饰前

    def outer(func):
        @wraps(func)
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            return res
        return wrapper
    

迭代器

  • 迭代器是迭代取值的工具

  • 优点:提供了一种新的、统一的取值方式,不依赖于索引和key取值;惰性计算,不耗费内存

  • 缺点:取值不够灵活,一次性的,只能往后取值,无法预知数据的个数

  • 可迭代对象:iterable

    • 内置有__iter__方法
  • 迭代器对象:iterator

    • 内置有__iter__方法
    • 内置有__next__方法
  • 调用:

    • 可迭代对象.__iter__() --> 返回迭代器对象
    • 迭代器对象.__next__()-->返回的是下一个值
  • 注意点:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

  • 自定义迭代器

    • 函数内但凡出现yield关键字,再调用函数不会执行函数体代码,而是会得到一个生成器对象
    • 生成器就是一种自定义的迭代器
    • yieldVSreturn
      • 相同点:返回值层都一样
      • 不同点:return只能返回一次值,函数就立刻结束,而yield可以进行多次返回
    def func():
       print('hello 1')
       yield 1
       print('hello 2')
       yield 2
       print('hello 3')
       yield 3
    
    def my_range(start,stop,step=1):
       while start < stop:
           yield start
           start +=step
    my_range(1,7,2)  # 1 3 5
    

生成器

列表生成式:nums = [i for i in range(5) if i%2==0]  
# num = [0,2,4]
字典生成式:res = {i:i**2 for i in range(5)}
# res = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
集合生成式:res = {i for i in range(5)}
#  res = {0,1,2,3,4}
生成器:(expression for item in iterable if condition)

三元表达式

  • res = 条件成立时的返回值 if 条件 else 条件不成立时的返回值

面向过程编程

  • 面向过程编程时一种编程思想or编程范式
  • 面向过程核心是”过程“二字,过程即流程
  • 优点:复杂的问题流程化,进而简单化
  • 缺点:牵一发而动全身,扩产性差,即高耦合

函数递归

  • 函数的递归调用是函数嵌套调用的一种特殊形式

    • 具体指的是在调用函数的过程有直接或间接调用自己,称之为函数的递归调用
  • 函数的递归调用其实就是用函数实现的循环

    例:
    def f1():
        print('from f1')
        f1()
    f1()
    
    函数递归调用实现二分法:
    def search(find_num,nums):
        print(nums)
        if len(nums) == 0:
            print('not exists')
            return
        mid_index = len(nums)//2
        if find_num>nums[mid_index]:
            new_num = nums[mid_index+1:]
            search(find_num,new_nums)
    	elif find_num<nums[mid_index]:
            new_num = nums[:mid_index-1]
            search(find_num,new_num)
        else:
            print('没找到哦')
    

匿名函数

匿名函数就是没有名字的函数
正常的:
    def func(x,y,z):
        return  x+y+z
匿名:
	lambda x,y,z:x+y+z
# 与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次即释放,除非让其拥有名字
 
func = lambda x,y,z:x+y+z
func(1,2,3)

使用匿名函数实现 max,min,sorted 对字典比较时比较value值
salaries = {'axx' : 18888, 'zxx' : 1888, 'yxx' : 3888}
max(salaries, key=lambda k: salaries[k])
min(salaries, key=lambda k: salaries[k])
sorted(salaries, key=lambda k: salaries[k])

了解
names = ['egon_nb', "lxx_sb", "hxx_sb"]
filter(lambda name: name.endswith('sb'), names)
map(lambda name: name + "vip", names)

from functools import reduce
reduce(lambda x, y: x + y, [1, 2, 3, 4, 5, 6], 100)
reduce(lambda x, y: x + y, ["a", "b", "c"])
posted @ 2021-07-02 19:50  zheng-sn  阅读(154)  评论(0)    收藏  举报