zoukankan      html  css  js  c++  java
  • 函数基础语法及使用

    函数

    函数是用来盛放代码的容器,一个函数就是一个功能。

    作用:

    1、减少代码冗余。

    2、增强代码可读性。

    3、使程序易于扩展。

    函数使用

    原则:先定义,后调用。

    函数名定义规则与变量名相同,函数名尽量用动词。

    # 定义函数基本结构
    def 函数名():
        代码体
    
    # 定义函数的完整结构
    def 函数名(参数1,参数2,参数3。。。):
        """
        注释信息
        """
        代码体
        return 返回值
    

    定义阶段:只检查语法,不执行代码。

    和定义变量相同,先申请一块内存空间,将代码放进去,将内存空间与函数名关联。

    # abc虽然没有定义变量,但这是没定义变量引发的逻辑错误,并没有语法错误,所以不会报错。
    def f():
        a + b + c
    

    调用阶段:执行函数体代码。

    调用阶段是先通过函数名找到代码体对应的内存空间,括号()表示执行代码体的代码。

    名字对应的是内存地址,打印函数名得到的是函数体的内存地址。

    def f():
        pass
    print(f)
    <function f at 0x0000000000507160>
    

    函数定义的三种形式

    无参函数

    def f1():
    	代码体
    

    有参函数

    def f2(x,f):
        代码体
    

    空函数

    用于先定义好程序大概所要用的功能,然后再根据思路补全函数。

    def f3():
        pass
    

    函数的提示信息

    Python不会限制传入参数的数据类型,但我们在定义时可以提示调用者函数参数和返回值的数据类型。

    def func(x:'参数1的提示信息',y:'参数2的提示信息')->'返回值得提示信息':
        pass
    
    # 查看提示信息,返回一个字典。
    print(func.__annotations__)
    {'x': '参数1的提示信息', 'y': '参数2的提示信息', 'return': '返回值的提示信息'}
    

    定义时的注释

    def f1(x,y):
        """
        函数功能注释
        :param x: 参数x的注释
        :param y: 参数y的注释
        :return: 返回值得注释
        """
        pass
    
    # 使用help()内置函数查看函数注释信息.
    help(f1)
    
    Help on function f1 in module __main__:
    
    f1(x, y)
        函数功能注释等
        :param x: 参数x的注释
        :param y: 参数y的注释
        :return: 返回值得注释
    

    函数调用的三种形式

    1、语句形式。

    f1()
    

    2、表达式形式。

    ret = f() + 10
    

    3、函数调用可以当做一个参数传给另一个函数。

    f1(f2())
    

    函数的返回值

    return的两种作用

    1、控制返回值。

    情况1:没有return或return后为空。此时返回值为None。

    def f1():
        pass
    print(f1())
    None
    
    def f2():
        return
    print(f2)
    None
    

    情况二:单个返回值。

    def f1():
        return 10
    print(f1())
    10
    

    情况三:用逗号分隔返回多个值。多个值会以元组的形式返回。

    def f1(a,b,c):
        return a,b,c
    
    print(f1(1,2,3))
    (1, 2, 3)
    

    返回值的id和函数调用的id是同一个。

    def f(a):
        return a
    res = f(3)
    print(id(res), id(f(3)))
    8791056713440 8791056713440
    

    2、终止函数

    函数内可以有多个return,函数执行过程中,遇到return就会立即终止函数,并将return后面的值当做本次调用的结果返回。

    def f1(a,b):
        if a >b:
            return a
        else:
            return b
    print(f1(10,20))
    20
    

    函数的参数

    两大类:

    • 形参:在定义函数时,括号内指定的参数,称之为形式参数,简称形参,本质就是变量名。
    • 实参:在函数调用时,括号内传入的值,称之为实际参数,简称实参,本质就是变量值。
    def total(a,b):
        print(a + b)
    total(10,20)
    
    # a,b就是形参。
    # 10,20就是实参。
    

    实参与形参的关系:

    在调用函数时,实参的值会绑定给形参,该绑定关系可以在函数内使用。在函数调用结束后,会解除绑定关系。

    def f1(a,b):
        print(a + b)  # 函数内可以访问到a和b
    f1(1, 2)
    3
    
    # 函数外则无法访问到a和b
    print(a, b)
    NameError: name 'a' is not defined
    

    形参:

    在函数调用时调用一次函数形参只能被传一次值,不能重复传值。

    位置形参:

    在定义函数时,按照从左到右的顺序依次定义的变量名,称之为位置形参。

    特点:必须被传值,多一个不行,少一个不行。

    # 必须传入和位置参数同样数量的值。
    def func(a, b):
        pass
    
    func(10)
    TypeError: func() missing 1 required positional argument: 'b'
    
    # 不能重复传值。
    func(111,222,a=333)
    TypeError: func() got multiple values for argument 'a'
    

    默认形参(具有默认值得形参):

    在定义函数时,就已经为某个形参赋值,该形参就称为默认参数。

    特点:在调用阶段可以不用给默认形参传值。若有传值,新值会覆盖默认值。

    def f(x, y=10):
        print(x, y)
    f(20)
    20 10
    

    注意:默认形参的值是在函数定义阶段规定死,之后的改变不会影响。默认形参的值最好为不可变类。

    n = 10
    print(f'函数定义前 n:    {id(n)}')
    
    def f1(x, y=n):  # 在定义阶段 y所指向的内存地址已经固定。
        print(f'函数内 y:    {id(y)}')
    
    n = 111  # n 的值为不可变类型,在值改变后内存地址也改变,但不会影响函数已定义的值。
    print(f'函数定义后 n:    {id(n)}')
    f1(222)
    
    函数定义前 n:    8790942025664
    函数定义后 n:    8790942028896
    函数内 y:    8790942025664
    222 10
    

    注:默认形参的值若为可变类型,则会被函数定义后的改变所影响。

    n = [10,20]
    print(f'函数定义前 n:    {id(n)}')
    
    def f1(x, y=n):  # 在定义阶段 y所指向的内存地址已经固定。
        print(f'函数内 y:    {id(y)}')
        print(x,y)
    
    n.append(30)  # n 的值为可变类型,在值改变后内存地址不变,还是指向同一个内存地址。
    print(f'函数定义后 n:    {id(n)}')
    f1(222)
    
    函数定义前 n:    41272960
    函数定义后 n:    41272960
    函数内 y:    41272960
    222 [10, 20, 30]
    

    写函数时,函数不应该被定义之后的外界因素影响。

    也就是说,在函数定义完后,函数运行结果应该是可预测的。默认形参的默认值若为可变类型,会让最后的结果充满不确定性,给人带来困惑。

    可变长参数*和**

    *会将溢出位置实参汇总成元组,然后赋值给其后变量名,通常应该是args。

    def f(a,b,*args):
        print(a,b)
        print(args)
    f('哼','哧','哈','嘿')
    哼 哧
    ('哈', '嘿')
    
    # 也可以不给*传值。结果为空元组。
    f('哼','哧')
    哼 哧
    ()
    

    利用*写一个接收能任意个数值的求和函数。

    def total(*args):
        n = 0
        for i in args:
            n += i
        return n
    print(total(1,2,3,4,5))
    15
    

    **会将溢出关键字实参汇总成字典,然后赋值给其后变量名,通常应该是kwargs。

    def f(a,b,**kwargs):
        print(a,b)
        print(kwargs)
    
    f('哼','哧',x='哈',y='嘿')
    
    哼 哧
    {'x': '哈', 'y': '嘿'}
    
    # 同样也可以不给**传值,结果为空字典。
    哼 哧
    {}
    

    *和**的组合使用,能接收符合语法的任意实参。

    def f(*args,**kwargs):
        print(args)
        print(kwargs)
    
    f(1,2,3,4,5,m=6,n=7,x=8,y=9)
    (1, 2, 3, 4, 5)
    {'m': 6, 'n': 7, 'x': 8, 'y': 9}
    

    命名关键字形参

    在*args之后,**kwargs之前的参数,也可以有默认值,仅接受关键字实参。

    def f(a,b,*args,c='嘿',d,**kwargs):
        print(a,b)
        print(args)
        print(c,d)
        print(kwargs)
    
    f(10,20,30,40,d='哈',k1='v1',k2='v2')
    
    10 20
    (30, 40)
    嘿 哈
    {'k1': 'v1', 'k2': 'v2'}
    

    形参最终顺序

    语法规定,违反则报语法错误。

    func(位置形参,默认形参,*args,命名关键字形参,**kwargs)
    

    实参:

    位置实参:

    在调用函数时,按照从左到右的顺序依次传入的值,称之为位置实参。

    特点:按照位置与形参一一对应。

    # 必须传入和位置参数同样数量的值,a和b能收到什么值取决于实参的位置。
    def func(a, b):
        pass
    
    func(10,20)
    func(20,10)
    

    关键字实参:

    在调用函数时,按照key=value的形式指定的实参,称之为关键字实参。

    特点:可以打乱顺序,但仍能为指定的形参传值。

    def func(a, b):
        print(a,b)
    
    func(10,20)
    10 20
    func(b=20,a=10)
    10 20
    
    # 指定关键字实参时,形参名不要带引号,否则会语法报错。
    func('b'=20,'a'=10)
    SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
    

    可变长实参*和**

    在函数调用时,*会将后面的可迭代对象拆分成位置实参。拆分后的值应与位置形参的数量一致。如果这个可迭代对象为字典,传入函数的值为字典的key。

    def func(a,b,c):
        print(a,b,c)
    func(*[1,2,3])
    1 2 3
    
    func(*{'k1':1,'k2':2,'k3':3})
    k1 k2 k3
    

    在函数调用时,**会将后面的字典(必须是字典)拆分成关键字实参。字典的key必须是形参名。

    def func(a,b,c):
        print(a,b,c)
    
    func(**{'a':1,'c':2,'b':3})
    1 3 2
    

    实参最终顺序

    func(位置实参,关键字实参,*iterable,**dict)
    

    *和**的魔性用法

    在函数嵌套中,外层函数使用*和**接收任意参数,内层函数使用*和**将接收的任意参数拆分。这个方法在装饰器中会使用到。

    def wrapper(*args,**kwargs):  # wrapper先接收为(1,2,3) {'a':4,'b':5},传给inner
        def inner(*args,**kwargs):  # 最后接收到1,2,3,a=4,b=5,再通过*和**聚合为(1,2,3) {'a':4,'b':5}传给args和kwargs
            print(args,kwargs)
        inner(*args,**kwargs)  # 调用inner接收后用*和**拆分为1,2,3,a=4,b=5 传给inner函数。
    
    wrapper(1,2,3,a=4,b=5)
    (1, 2, 3) {'a': 4, 'b': 5}  # 最终输出
    

    在变量解压赋值中,*能将溢出的值聚合成一个列表赋值给后面的变量名,该名约定俗成为下划线 _ 。

    t = (1,2,3,4)
    a, *_ = t
    print(a, _)
    1 [2, 3, 4]
    
    *_, a = t
    print(_,a)
    [1, 2, 3] 4
    
    a, *_, b = t
    print(a, _, b)
    1 [2, 3] 4
    

    函数对象

    函数是第一等公民

    1、函数可以当变量赋值

    def f1():
        print('from f1')
    f2 = f1  # 将f1的内存地址关联给f2
    f2()  # f2加括号也能调用
    from f1
    

    2、可以当做参数传给另一个函数。

    def f1():
        print('from f1')
    
    def f2(x):
        x()
    f2(f1)  # 将f1赋值给x,在f2内调用f1()
    from f1
    

    3、可以当做一个函数的返回值。

    def f1():
        print('from f1')
    
    def f2(x):
        return x
    
    ret = f2(f1)
    
    ret()  # 拿到返回值f1,加括号也能调用
    
    from f1
    

    4、函数可以当做容器类型的元素。

    def func():
        return 100
    
    l1 = [1,2,func]
    
    res = l1[-1]()  # 通过索引取到func,加()能调用
    print(res)
    
    100
    

    通过以上特性,可以写一个函数功能字典。

    def haha():
        print('哈哈')
        
    def hehe():
        print('呵呵')
    
    # 若要添加功能,只需添加函数和功能字典即可,主逻辑代码无需改变。
    def hei():
        print('嘿嘿')
    
    func_dict = {
        '0':['退出'],
        '1':['打印哈哈',haha],
        '2':['打印呵呵',hehe],
        # 添加一个key:value
        '3':['打印嘿嘿',hei],
    }
    
    
    while 1:
        for i in func_dict:
            print(i,func_dict[i][0])
        choise = input('>>>').strip()
        if choise == '0':
            break
        elif choise in func_dict:
            func_dict[choise][-1]()
        else:
            print('更多功能开发中...')
    

    函数嵌套

    嵌套定义

    在函数内再定义函数。

    def wrapper():
        def inner():
            print('from inner')
        return inner
    
    # 通过调用wrapper得到返回值为inner,然后将返回值加()即可调用.
    res = wrapper()
    res()
    from inner
    

    使用嵌套定义,可以写一个同类型多种功能的函数,通过输入参数选择不同的功能。这样在调用时只需知道一个函数的用法即可,而无需调用多个函数。

    例如,写一个计算圆的周长或面积的函数。

    from math import pi
    
    def circle(radius,mode=0):
        def perimiter(radius):
            return 2 * pi * radius
        def area(radius):
            return pi * (radius ** 2)
        # 判断如果mode为0则计算周长,如果为1则计算面积。
        if mode == 0:
            return perimiter(radius)
        elif mode == 1:
            return area(radius)
        else:
        	return 'input 0 or 1'
        
    res = circle(10,mode=1)
    print(res)
    314.1592653589793
    

    嵌套调用

    在一个函数内再调用其他函数。

    例如,写一个比较四个数返回最大数的函数,可以先写一个比较两个数的函数,然后通过调用这个函数来比较四个数。

    def max2(a,b):
        return a if a > b else b
    
    def max4(a,b,c,d):
        ret1 = max(a,b)
        ret2 = max(ret1,c)
        ret3 = max(ret2,d)
        return ret3
    
    res = max4(1,2,3,4)
    print(res)
    4
    

    使用嵌套调用,可以让一个功能复杂的函数拆分成多个功能简单的函数,然后再通过一个函数调用多个简单函数。这样能使代码逻辑更清晰。

  • 相关阅读:
    React准备
    React组件
    从uri获取图片文件的File对象
    ES6
    Promise.all
    js的ctrl+s保存功能
    浏览器端读取和生成zip文件
    vscode配置及快捷键
    Array
    最全React技术栈技术资料汇总
  • 原文地址:https://www.cnblogs.com/ChiRou/p/13419725.html
Copyright © 2011-2022 走看看