• day10-函数的介绍,参数以及返回值


    函数的介绍

    1.什么是函数

    ​ ps : 函数就是存放代码的容器
    ​ 具备某一功能的工具-->函数
    ​ 事先准备工具的过程---> 函数的定义
    ​ 遇到应用场景拿来就用---> 函数的调用

    所以函数的使用原则:
    先定义
    后调用

    2.为何要用函数

    ​ 解决下述问题:
    ​ 1.代码组织结构不清晰,可读性差
    ​ 2.可扩展性差

    3.如何使用函数

    定义的语法

    def 函数名(函数1,函数2,函数3...):
            # 函数名 和 变量名的定义方式格式一样,但最好使用动词,
            '''函数的文档注释'''
            代码1
            代码2
            代码3
            return 返回值
    

    调用的语法

    一.定义的阶段:

    发生的事情

    1. 申请一个内存空间,将函数体代码放进去,

    2. 将函数内存地址绑定给函数名

      强调:只检测语法

    在python中的变量名受到了特殊照顾,会自作主张的将变量名对应的内存地址的那个值返回,一般一个变量名返回的就是一个内存地址

    所以调用函数名返回的是一个函数名对应的内存地址

    例子:函数和变量返回的都是内存地址,但待遇不一样

    def login():  # login = 函数的内存地址
        print(1111)
        print(2222)
        print(3333)
    
    
    x = 10  # x = 10 的 内存地址
    print(x)  # 10(本质是和函数一样的,返回内存地址,但python会自动给你将返回内存地址变成对应的那个值再返回)
    print(login)  # 0x0000029F7BE4A0D8
    

    二.调用函数

    语法:变量名加括号===>变量名()
    1.先通过函数名定位到函数的内存地址
    2.函数内存地址() ===> 触发函数体代码的运行
    强调:调用函数才会执行函数体代码

    login()  # 执行一边函数体代码
    login()  # 又执行一遍函数体代码
    

    这俩个函数名加括号调用函数执行函数体代码,充分的说明了函数的作用.

    例子说明:

    例子1:定义阶段只检测语法

    def func():
        print(1111  # 这是在定义阶段就检测的语法问题:SyntaxError: invalid syntax
    
    print('我还没执行到这呢')
    func()
    

    例2:调用阶段,才执行函数体代码

    def func():
        print(1111)
        x
        print(123)
    
    print('如果我执行了就代表函数定义阶段没问题')
    func()  # 报错的位置是line 76 NameError: name 'x' is not defined
    

    例3和例4都是在强调函数分为俩个阶段,定义阶段和调用阶段

    例3:
    # =====按顺序先将bar的内存地址放到内存,再将foo的内存地址放到内存
    def bar():
        print('from bar')
    
    def foo():
        print('from foo')
        bar()  # 执行到这是前面bar的内存地址已经有了
    
    foo()  # 所以调用的时候是没有问题的
    
    例4:
    ==== 将 foo的内存地址先放到内存,再将bar的内存地址放到内存,再执行foo
    
    def foo():
        print('from foo')
        bar()
    
    
    def bar():
        print('from bar')
    
    
    foo()
    

    你会发现也是没有问题的.按道理来说不应该是会报错嘛?因为foo内的bar前面还没被定义呢?
    但你忘了一件事,那就是函数是分俩个阶段的,一是调用,二才是执行.此时你看看.你执行foo()前,是不是已经将bar内存地址申请好了,因为定义的时候就已经申请好了,所以你在之后调用才不会有问题
    除非你在调用foo之后,再定义的bar,它才会找不到对应的内存地址

    三.定义函数的三种方式

    3.1无参

    概念:无参表示的就是在括号后面没有的"变量名"或者对应的"变量值"的

    例子:

    def func():
        print('xxxx')
        
    func()
    

    3.2 有参

    概念:有参表示的就是在括号后面有"变量名"或者对应的"变量值"的

    例子:

    def func1(x):
        print(x)
    func1(1)
    

    3.3 空

    概念:表示的是函数体代码为pass或者...的,只是保证执行时不会报语法错误,一般用于程序的设计搭建

    def login():
        pass  # 也可以是 ...
    login()
    

    应用场景:

    其实一个函数你要不要定义成一个无参的还是有参的,完全取决与你的函数体代码,里面的功能是不是要"写死"还是要"写活",参数就是一个可以变化的值,相当于变量,你不想写死,就给他一个可以变化的地方,而参数就是这么一个东西
    所以说定义一个函数是不是有参无参,完全看函数体代码的需求
    空函数表示的就是一个占位,表示这个函数定义阶段不会报错,并且可以执行,一般用于框架搭载时,先用pass占位,后面再来填代码的具体功能

    例子:登入功能

    def login():
        inp_name = input("username>>>: ").strip()
        inp_pwd = input("password>>>: ").strip()
        if inp_name == "egon" and inp_pwd == "123":
            print('login successful')
        else:
            print("username or password error")
    
    login()
    

    四:调用函数的三种形式

    4.1 语句

    len('hello')
    

    4.2 表达式

    res = len('hello') * 10  # 本质是函数的返回值进行运算
    print(res)
    

    4.3 可以当做参数传给另外一个函数

    print(len('hello'))
    

    像len()这种有返回值的函数一般都是作为具体的功能的作用,而print()这种没有返回值的一般都是作为语句的作用

    函数的参数

    总体分为俩大类:

    1.形参:在函数定义阶段括号内指定的参数,称之为形式参数,简称形参--> 相当于变量名
    2.实参:在函数调用阶段括号内指定的参数,称之为实际参数,简称实参 ---> 相当于变量的值

    形参与实参的关系是:
    在调用函数时,实参值会绑定给形参名,在函数调用完毕后解除绑定

    细分的话

    形参系列:

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

    特点: 每次调用,必须被赋值

    def func(x, y):
        print(x)
        print(y)
    
    func(1,2,3)
    func(1)
    

    二:默认形参:在定义函数时,就已经为某个形参赋值了.称之为默认形参

    特点:调用函数时,可以不为默认形参赋值

    注意:可以混用位置形参与默认形参,但是

    1. 位置形参必须在默认形参的前面

    2. 默认形参对应的值一般是一个不可变的类型

    3. 不管是位置形参还是默认形参都是在定义阶段就放到内存的

    例子:当形参为一个可变类型时,我该怎么处理这个可变类型

    def func(name, hobby, hobbies=None):
        if hobbies == None:
            hobbies = []
        hobbies.append(hobby)
        print(f'{name}:{hobbies}')
    
    
    func('jkey', 'eat')
    
    func('song', 'play', ['music'])
    

    例子2:当一个默认形参的值为一个变量时,值会怎么变化

    m = 1111
    
    
    def func2(x, y, z=m):
        print(x, y, z)
    
    
    m = 2222
    func2(1, 2)
    

    是不是发现它不会变成2222,哈哈哈,就是因为在定义函数的时候就已经将z=m赋好值了,这又充分说明了函数的形参在定义阶段就已经绑定到内存空间了.而m=2222 的时候,函数以及定义好了,所以z=m还是=1111

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

    特点:按照位置未形参赋值,一一对应

    例子:

    def func(name, age):
        print(name)
        print(age)
    
    
    func('egon', 17)
    

    三.关键字实参:在调用函数时,按照key=value的形式传值,称之为关键字实参

    特点:可以打乱顺序,但是仍然能够指名道姓的为指定的形参赋值

    def func(name, age):
        print(name)
        print(age)
    
    func(age=18, name='jkey')
    

    注意:可以混用位置参数与关键字参数,但是

    1. 位置实参必须在关键字实参的前面
    2. 不能为同一形参重复赋值

    例子解释:

    func('egon', age=18)
    
    
    # func(age=18,'egon')  # 报语法错误 line 41 error SyntaxError: positional argument follows keyword argument
    
    def foo(x, y, z):
        pass
    
    
    # foo(1,y=2,3)  # 语法错误 位置实参必须在关键字实参前面
    # foo(1, y=2, z=3, x=4)  # 不能给同一个形参重复赋值,TypeError: foo() got multiple values for argument 'x'
    foo(1, y=2, z=3)
    

    可变长参数系列

    概念:可变长参数指的是在调用函数时,传入的实参个数不固定,对应着必须有特殊形式的形参来接收移除的实参

    > * 和 ** 在 形参中 其实就是一种汇总,合并功能=============
    而实参无非俩种,所以它对应的特殊形式的形参应该是:
    1.溢出位置实参 ---> *
    2.溢出的关键字形参 ====> **

    1.在形参种的应用:会将溢出的位置实参合并成一个元组,然后赋值给紧跟其后的那个形参名-- 这个形参名一般为args

    def func(x, *args):  # *的功能是 将 溢出的 位置实参(也就是值) 绑定个args
        print(x)  # 1
        print(args)  # (2, 3, 4, 5)
    
    func(1,2,3,4,5)
    

    例子: 计算n个数之和

    def my_sum(*args):
        sum_num = 0
        for i in args:
            sum_num += i
    
        print(sum_num)
    
    my_sum(1,2,3,4,5)
    

    2.**在形参种的应用:**会将溢出的关键字实参合并成一个字典,然后赋值给紧跟其后的那个形参名 -- 这个形参名一般为 kwargs

    def func(x, **kwargs):  # **的功能是将溢出的关键字实参合并成一个字典
        print(x)  # 1
        print(kwargs)  # {'a': 2, 'c': 3}
    
    
    func(1, a=2, c=3)
    

    ========= * 与 ** 在实参中是一种打散功能==========

    * 在实参中的应用: * 后可以跟被for 循环遍历的所有类型,*会将紧跟其后的那个值打散成位置实参

    def func(x,y,z):
        print(x)
        print(y)
        print(z)
    
    func(*(11,22,33))
    # func(*"hello")  # TypeError: func() takes 3 positional arguments but 5 were given
    func(*{'x':1,"y":2,'z':3})  # x y z for遍历出来的默认是key
    

    ** 在实参中的应用: ** 只能跟字典类型,**会将字典打散成关键字实参

    def func(x, y, z):
        print(x)
        print(y)
        print(z)
    
    
    # func(**{'k1': 111, 'k2': 222})  # 相当于func(k2=222,k1=111) 报错:TypeError: func() got an unexpected keyword argument 'k1'
    func(**{"x": 111, "y": 222, "z": 333})  # 111
    222
    333
    

    * 和 ** 在形参和实参中的应用

    def index(x, y, z):
        print(x, y, z)
    
    
    def wrapper(*args, **kwargs):  # 这里的*和**是在形参中,功能是将wrapper的位置实参和关键字实参给汇和,合并
        index(*args, **kwargs)  # 而这里的* 和 ** 是在实参中,表示的功能是将汇合合并后的形参打散. 会将wrapper中传入的参数原封不动的传入到 index函数
    
    
    # wrapper(1, 2, 3, 4, 5, a=1, b=2, c=3)  # 所以该wrapper的实参参照的形参其实是index的形参规范.  所以这样传会报错:TypeError: index() got an unexpected keyword argument 'a'
    # 所以要想调用wrapper就必须满足index的形参规范
    wrapper(1, 2, 3)  # 1 2 3
    wrapper(1, y=2, z=3)  # 1 2 3
    

    了解:命名关键字形参:在*和**中间的形参称之为命名关键字形参

    特点: 必须按照key=value的形似传值

    例子:

    def func(x, m=1, *args, y=222, z, **kwargs):
        print(x)
        print(m)
        print(args)
        print(y)
        print(z)
        print(kwargs)
    
    
    func(1, 2, 3, z=4, a=1, b=2, c=3)
    # func(1, 2, 3, 4, 5, 6)  # 多余的 2, 3, 4, 5, 6 全部给了* 绑定给了args
    
    # 所以要想该z的值就必须得以关键字实参的形式来传
    func(1, 2, 3, 5, 6, z=4)
    

    函数的返回值

    函数的返回值可以放在 return关键字的后面,后面表示的就是该函数的返回值

    返回值可以是任何数据类型

    返回值的应用:计算俩个人月薪高的年薪

    def max2(x, y):
        """
        一般的函数就应该让对应的返回值表示的有意义,本函数就是一个例子
        :param x:
        :param y:
        :return:
        """
        if x > y:
            return x
        else:
            return y
    
    
    res = max2(10, 20)
    print(res * 12)
    

    return关键字的作用:当一个函数体代码在执行的时候(即被调用后),只要执行到return就会立即结束函数体的代码,并且将之后的值返回

    例子:

    def func():
        """
        当函数体内出现了 return时,执行到return时函数体代码会立即结束(不会往下执行了)
        但是你在return之后还有代码也没有关系,不会报语法错误,但是并不会执行
        :return: 111
        """
        print('=====>')  # =====>
        return 111  # 从这之后的函数体代码就不会被执行了
        print('=====>2')
        return 222
    
    
    func()
    

    返回值的三种类型

    def func1():
        """
        当返回值 只有一个的情况下,返回的就是这个值的本身
        :return: 111
        """
        print(111)
        return 111
    
    
    def func2():
        """
        当返回值 有多个的情况下, 返回的就是小元组
        :return: ('name', 111, True)
        """
        print(222)
        return 'name', 111, True
    
    
    def func3():
        """
        当没有返回值 或者 只有一个 return 或者 return None 的情况下, 返回的都是None
        :return:
        """
        print(333)
        return
    

    本章主要主要介绍了函数的基本使用

    需要注意的是函数的参数部分 记住形参有俩种加上俩个特殊形式(位置形参,默认形参,*和**),对应的实参也有俩种加上俩个特殊形式(位置实参,关键字实参,*和** ), 在形参时的 *和 ** 的的功能是汇合,而在实参时的 * 和 **表示的是打散.

  • 相关阅读:
    【PyTorch深度学习60分钟快速入门 】Part1:PyTorch是什么?
    如何编写一个gulp插件
    进阶之初探nodeJS
    模拟Vue之数据驱动5
    模拟Vue之数据驱动4
    模拟Vue之数据驱动3
    模拟Vue之数据驱动2
    树结构之JavaScript
    模拟Vue之数据驱动1
    CORS详解[译]
  • 原文地址:https://www.cnblogs.com/jkeykey/p/14203462.html
Copyright © 2020-2023  润新知