函数详解

244 阅读8分钟

函数的基本使用

函数的本质

1.当我们编写代码时遇到许多地地方都需要用到同一个功能的时候,按照之前所需的知识,我们通常会选择在每个这样的地方反复编写同样的代码。这种方式也能解决问题,但是整体代码就会显得又臭又长。此时我们就引申出了函数。
2.函数可以看成是工具,提前准备好工具,随用随取即可。而且可以多次使用。

函数的语法结构

# 函数的定义(提前准备工具)
def 函数名(参数1,参数2):
    '''函数注释'''
    函数体代码
    return 返回值
# 函数的调用(开始选择工具并使用)
函数名()
1.def
	定义函数的关键字
2.函数名
	与变量名的命名规则一致,尽量做到见名知意
3.括号
	在定义函数的时候,函数名之后必须跟括号
4.参数
	定义函数括号内可以写参数(数量不固定),也可以不写
    用于接收外界传递给函数体代码内部的数据
5.函数注释
	类似于说明书,用于介绍函数的功能以及注意事项。非必要,但是建议写上
6.函数体代码
	整个函数最核心的区域(逻辑代码)
7.return
	定义函数的返回值,可有可无
    

函数的定义与调用

1.函数必须先定义再调用
    定义函数的代码必须放在调用函数的代码之前执行
2.定义函数用def关键字,调用函数用函数名加括号
3.函数在定义阶段值检测函数体代码的语法,不执行函数体代码
  只有调用阶段才会执行
4.函数名到底是什么东西
    函数名绑定的是一块内存地址,里面存放了函数体代码
    想要运行该代码,就需要调用该函数
5.函数名加括号的执行优先级最高(定义阶段除外)
    def my_func():
        print('this is my function')
    print(my_func())
    #this is my function   先调用了函数,执行函数内的print
    #None

函数的分类

1.内置函数
    解释器帮用户提前定义好的函数,用户可以直接调用  len()
    #  内置函数可以直接调用,但是内置方法必须使用 函数类型. 的方式才可以调用,相当于是数据类型独有的一些内置方法
2.自定义函数
    1.空函数
        函数体代码用pass顶替 暂时没有任何功能
        主要用于前期的项目框架搭建 提示主要功能
        def run():
            pass
    2.无参函数
    	函数的定义阶段括号内没有填写参数,调用阶段也无需传递参数
        def func():
        	print('from func')
    3.有参函数
    	函数的定义阶段括号内填写参数,调用阶段需要传递对应的参数
        def func(a,b):
            print('from func')

函数的返回值

返回值就时调用函数之后产生的结果 可有可无
获取返回值的方式是固定的
	变量名 = 函数() 
    函数有返回值则获取,没有则返回None
1.函数体代码没有return:默认返回None
    def my_fun():
        pass
    my_fun()  # None
2.函数体代码有return,但是后面没有值:依旧返回None
    def fun():
        pass
        return 
    res = fun()
    print(res)  # None
3.函数体代码有return关键字:后面写什么就返回什么(如果是数据值就直接返回,如果是变量则需要找到对应的数据值返回)    
    def fun():
        pass
        return 123
    res = fun()
    print(res)  # 123
4.函数体代码有return关键字,后面跟多个数据值,返回结果是将这些数据值组织成的元组
    def fun():
        pass
        return 1,2,3
    res = fun()
    print(res)  # (1, 2, 3)
    
    def fun():
        pass
        return [1,2,3],{'name':'jason'},'jason'
    res = fun()
    print(res)  # ([1, 2, 3], {'name': 'jason'}, 'jason')
5.函数体代码遇到return关键字会立刻列数函数体代码的运行
    def func():
        print('我是上边')
        return
        print('我是下边')
    func()  # 我是上边

函数的参数

参数有两大类
    1.形参(函数在定义阶段括号内填写的参数)
    2.实参(函数在调用阶段括号内填写的参数)
形参与实参的关系
	形参相当于是变量名    实参相当于是数据值
    形参与实参在函数调用阶段临时与实参进行绑定 函数运行结束会丽可删除
    >>>>:动态绑定  动态删除

位置参数

1.位置形参
    在函数定义阶段括号内从左至右依次填写的变量名称之为 位置形参 
2.位置实参
    在函数调用阶段括号内从左至右依次填写的变量名称之为 位置形参 
3.实参可以是数据值也可以是绑定了数据值的变量名
4.给位置形参传参的时候必须一一对应,不能多值或少值
    def func(a,b):
        print(a,b)
    func(1,2) # 1 2 
    func(1) # 报错
    func(1,2,3) # 报错

关键字参数

在函数 "调用阶段" 括号内以 '形参等于值' 的形式传值称之为关键字实参

1.指名道姓的给形参传值(打破了位置的限制)
2.位置参数必须在关键字实参的前面
    无论是形参还是实参 都遵循结构简单的在前面,结构复杂的写在后面
3.同一个形参在一次调用中只能传一次值
    def func(a,b):
        print(a,b)
    func(1,2)
    func(a = 1,b = 2)
    func(1,b = 2)
    func(1,a = 2)  # 报错  形参a不能被两次传值
    func(a = 1,2)  # 报错  位置参数不能在关键字参数的后面

默认值参数

在函数 '定义阶段' 括号内以 '形参 = 值' 的形式写的参数叫做默认值形参

#  person函数用于输出学生个人信息,当一个班的男生较多时,我们完全可以将gebder的默认值设为男生,这样录入男生信息时就可以不用传gender参数。如果是女生,可以临时再改。如下:
def person(name,age,gender = 'male'):
    print(f"""
    name:{name}
    age:{age}
    gender:{gender}
    """)

person('lihua',19)
person('kankang',19,'female')
person('kankang',19,gender='female')  # 三种方法都可以

可变长参数

可以打破形参实参的个数限制,任意传值

1.*号可以接收多余的 '位置参数' 并组织成 '元组' 的形式赋值给后面的变量名
    def my_fun(*args):
        print(args)
    my_fun()  # ()
    my_fun(['a','s','d','f'])  # (['a', 's', 'd', 'f'],)
    my_fun(1,'jason')  # (1, 'jason')
2.**号可以接收多余的 '关键字参数' 并组织成 '字典' 的形式赋值给后面的变量名
    def my_fun(**kwargs):
        print(kwargs)
    my_fun()  # {}
    my_fun(name = 'jason')  # {'name': 'jason'}
    my_fun(name = 'jason', age = 20)  # {'name': 'jason', 'age': 20}
    
3.若两者连用,则能传递任意类型的参数

*与**在实参中的作用

# 我们都知道*args在形参中可以接收所有多余的位置参数,并将这些参数赋值给一个元组,命名为args。那我们现在来学习*在实参中的作用。
def func(*args):
    print(args)
l1 = [1,2,3,4,5]   
# 需求:将l1中的值挨个作为实参传给函数func,并输出
    1.如果我们直接将l1作为参数:
        func(l1)  # ([1, 2, 3, 4, 5],)  可以看到,返回结果是一个元组,里边仅有一个数据值:l1这个列表整体,不符合题意。
    2.如果我们按照索引先后顺序传值:
        func(l1[0],l1[1],l1[2],l1[3],l1[4],)  #  (1, 2, 3, 4, 5)  这次结果正确了,但挨个写出列表内元素传值的方法未免太过麻烦。
    3.利用*传值
        func(*l1)  # (1, 2, 3, 4, 5)  仅用一个星号就代替了上述那么麻烦的方法。灰常地耗用。
既然有*,那肯定少不了**
def func1(**kwargs):
    print(kwargs)
d1 = {'name':'jason', 'age':20}
func1(name = 'jason', age = 20)  # {'name': 'jason', 'age': 20}
func1(**d1)  # {'name': 'jason', 'age': 20}  两种传值方式结果一模一样,但显然是下边那种更为方便

总结:*与**在实参中的作用
在实参前加 * 会将该实参打散成位置实参(列表,字符串)
在实参前加 ** 会将该实参打散成关键字实参(字典)

命名关键字参数

# def func(a,b,*args):
#     pass
# func(1,2)
# func(a=1,b=2)
# func(1,b=222)
'''需要形参在传实参的时候 必须按照关键字参数才可以'''
# 在形参*args的后面
# def func(a, b, *args, c):
#     print(a, b, args, c)
# func(1,2,3,4,5,6,7)  # 报错
# func(1, 2, 3, 4, 5, 6, c=666)
# 如果形参中还有**kwargs 那必须在它的前面
def func(a,b,*args,c,**kwargs):
    pass
func(1,2,3,4,5,6,c=123,name='jason')

函数名的使用

def func():
    print('this is func1')
1.函数名可以用来多次赋值
    name = func
    name()  # this is func1
2.函数名可以当做函数的实参
    def index(a):
        print(a)
    index(123)  # 123
    index(func)  # <function func at 0x000001CD81B1F0D0> 函数func的内存地址
3.函数名可以当做函数的返回值
    def index():
        return func
    res = index()
    print(res)  # <function func at 0x000001DBB4BDF0D0>
    res()  # this is func1
4.函数名可以当做容器里边的数据值
    l1 = [1,2,3,4,func]
    print(l1)  # [1, 2, 3, 4, <function func at 0x000001F49B86F0D0>]
    l1[-1]()  # this is func1