函数的基本使用

什么是函数

函数就类似于具备某一功能的工具,提前定义好之后可以反复使用(即:函数就是盛放代码和功能的容器)

函数:函数的基本使用、函数参数的使用 - 图1

函数的使用原则:

先定义 ,后调用

为何要用函数==>解决下述问题:

  • 1.代码组织结构不清晰、过于冗余、可读性差
  • 2.代码没有返回值(无法使用之后的返回结果)
  • 3.兼容性、可维护性、可扩展性差、修改繁杂等等

如何用函数

  • 先定义———> 三种定义方式
  • 后调用———> 三种调用方式
  • 返回值———> 三种返回值的形式

函数定义的语法:

  1. def 函数名(参数1,参数2,参数3,...):
  2. '''函数的注释'''
  3. 函数体代码
  4. return 返回值
  5. '''
  6. 1.def: (必须)
  7. 定义函数的关键字;
  8. 2.函数名:(必须)
  9. 函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
  10. 3.括号冒号:(必须)
  11. 括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
  12. 4.参数:(可选)
  13. 括号内定义参数,参数是可有可无的,且无需指定参数的类型;
  14. 5.函数注释: (可选)
  15. 描述函数功能,参数介绍,非必要,但是建议加上,从而增强函数的可读性;
  16. 6.函数体:(必须)
  17. 由语句和表达式组成;
  18. 7.return值:(可选)
  19. 定义函数的返回值,return是可有可无的。
  20. '''

函数的定义与调用

定义函数

定义函数发生的事情

  • 1、在内存空间中申请一块内存空间存储函数体代码
  • 2、将函数体代码所在的内存空间地址绑定给函数名

强调:定义函数只检测语法,不执行代码

  1. def login(): # login = 函数的内存地址
  2. print(111) # 函数体代码
  3. print(222)
  4. x = 10 # x = 10的内存地址
  5. print(x) # 10
  6. '''
  7. 原则上打印应该返回内存地址,但是返回的是值。
  8. 变量名受到python解释器特殊照顾由内存地址直接找到返回值打印。
  9. '''
  10. print(login) # <function login at 0x7fa136db91f0>
  11. '''
  12. 但函数名不同,打印显示的是内存地址。定义函数的目的是为了把代码丢到容器,
  13. 只需要运行不需查看。则无需被照顾。
  14. '''

调用函数

调用函数发生的事情:

  • 1、先通过函数名定位到函数的内存地址
  • 2、函数名加括号会触发函数体代码的运行

强调:调用函数才会执行函数体代码

  1. # 示例:
  2. def login():
  3. print('hello')
  4. login() # hello 触发函数体代码的运行
  5. # 语法错误
  6. def func():
  7. print(111 # 无效语法,定义阶段都无法通过
  8. # 逻辑错误
  9. def func():
  10. print(111)
  11. x # x 没有定义,变量名也需要先定义后引用
  12. func() # 调用肯定出错
  13. # 示例:
  14. def bar():
  15. print('from bar')
  16. def foo():
  17. print('from foo')
  18. bar()
  19. foo()
  20. '''
  21. from foo
  22. from bar
  23. '''
  24. # 语法错误
  25. def foo():
  26. print('from foo')
  27. bar()
  28. foo() # bar没有定义,调用肯定出错
  29. def bar():
  30. print('from bar')

定义函数的三种形式

1.无参函数

  • 函数在定义阶段括号内没有参数
  1. # 函数体不需要外部传参数进行,它本身代码就是固定的,所以就是无参函数
  2. def say():
  3. print('welcome to here')
  4. say()
  5. # 参数是函数的调用者向函数体传值的媒介,有参无参是根据函数体代码去走的,看函数体代码是否需要外部传参数
  6. def login():
  7. inp_name = input("请输入用户名>>>: ").strip()
  8. inp_pwd = input("请输入密码>>>: ").strip()
  9. if inp_name == "jack" and inp_pwd == "123":
  10. print('登录成功!')
  11. else:
  12. print("用户名或密码错误")
  13. login() # 直接调用无需传参数,input功能已经实现与用户交互

2.有参函数

  • 函数在定义阶段括号内有参数
  1. # 函数体代码需要外部传参数进行,这种函数就需要定义为有参函数
  2. def max2(x, y): # 参数---》原材料
  3. if x > y:
  4. print(x)
  5. else:
  6. print(y)
  7. max2(10,20)

3.空函数

  • 函数在定义阶段括号内没有参数,并且函数体代码为空(pass)
  1. # 函数体为pass代表什么都不做,称之为空函数。定义空函数通常是有用的,因为在程序设计的开始,往往是先想好程序都需要完成什么功能,然后把所有功能都列举出来用pass充当函数体“占位符”,这将使得程序的体系结构清晰且可读性强。例如要编写一个程序,我们可能想到的功能有用户认证,下载,上传,等功能,可以先做出如下定义:
  2. def auth_user():
  3. """用户认证"""
  4. pass # 代表占位符
  5. def download_file():
  6. """下载文件"""
  7. pass
  8. def upload_file():
  9. """上传文件"""
  10. pass

调用函数的三种形式

  1. # 1.语句
  2. len("hello") # 单纯的调用,例如len统计hello的长度
  3. # 2.表达式
  4. res = len("hello") # 将调用函数的返回值赋值给res
  5. res = len("hello") * 10 # 将调用函数的返回值乘以10的结果赋值给res
  6. print(res)
  7. # 3 .可以当做参数传给另外一个函数
  8. len("hello") # 从形式上看就是将函数调用,从本质讲是跟2同理
  9. print(len("hello")) # 先调len的返回值然后再赋值给print

函数的返回值

return 详解

  1. def max(x, y):
  2. if x > y:
  3. return x
  4. else:
  5. return y
  6. res = max(10, 20)
  7. print(res)
  • 1、return是一个函数结束的标志,函数可以有多个return,但只要执行一次return,整个函数就立即结束,并且将return后的值当做本次调用的结果返回

    ```python def func(): print(“=====”) # 只运行到了此行 return 111 # return后的值当做本次调用的结果返回 print(“**“) # ruturn 下面的值不会被执行 return 222

res = func() print(res)

‘’’

111 ‘’’

  1. -
  2. <a name="0cee9588"></a>
  3. #### 2、return后的返回值有三种情况
  4. - 1)函数内可以没有return 或者 return None 或者 return:返回的都是None
  5. - 2return 值:返回的就是这一个值
  6. - 3return 1,值2,值3:会自动组织成一个元组返回
  7. ```python
  8. # 示例1:
  9. def func():
  10. print(111)
  11. return
  12. print(2222)
  13. res = func()
  14. print(res)
  15. '''
  16. 111
  17. None
  18. '''
  19. # 示例2:
  20. def func():
  21. return 111,"xxx",[22,33]
  22. res = func()
  23. print(res) # (111,"xxx",[22,33])返回的是元组

函数:函数的基本使用、函数参数的使用 - 图2

函数参数的使用

  • 函数参数的两大分类:

    • 1、形参:在函数定义阶段括号内指定的参数,称之为形式参数,简称“形参”

    • 2、实参:在函数调用阶段括号内传入的值,称之为实际参数,简称“实参”

形参与实参的关系:

我们可以将形参看成是变量名,实参看成是变量值,在调用函数时,实参值会绑定给形参名,在函数调用完毕后解除绑定

  1. def func(x, y): # x, y是形参,在定义阶段()内传值。形参指的是变量名。
  2. print(x, y)
  3. func(111, 222) # 111,222是实参,在调用阶段传送的值,实参指的是变量值
  4. """
  5. 形参的表现形式只有一种就是变量名
  6. 实参的表现形式有很多种(但是把握核心就是:数据值)
  7. """

形参系列

位置形参

  • 在函数定义阶段按照从左往右的顺序依次定义的变量名
  • 特点:
    • 每次调用必须被赋值,按照位置一一对应
    • 位置参数在绑定的时候多一个不行少一个也不行
  1. def func(x, y): # 定义位置形参:x,y,两者都必须被传值
  2. print(x)
  3. print(y)
  4. func(1, 2) # 可以正常赋值,必须刚刚好
  5. func(1, 2, 3) # 报错,多了一个值
  6. func(1) # 报错,少了一个值

默认形参

  • 在函数定义阶段就已经为某个形参赋值了
  • 特点:
    • 在函数调用阶段,可以不用为其赋值,则使用默认的
    • 在函数调用阶段,也可以继续为其赋值, 则使用你给的
  1. def func(name, age=18): # age=18是默认形参,无需赋值,也可以被赋值
  2. print(name)
  3. print(age)
  4. func("jack") # name正常赋值
  5. func("jack", 19) # 正常赋值
  • 注意: 可以混用位置形参与默认形参,但是
    • 1、位置形参必须在默认形参的前面
    • 2、默认形参的值通常应该是不可变类型
    • 3、默认形参的值是在函数定义阶段赋值的
  1. # 例一:
  2. def func(name, hobby, hobbies = []): # 三次调用都用的是同一列表
  3. hobbies.append(hobby)
  4. print('%s的爱好是%s'%(name,hobbies))
  5. func('jack','read') # jack的爱好是['read']
  6. func('tom','play') # tom的爱好是['read', 'play']
  7. func('bob','movie') # bob的爱好是['read', 'play', 'music']
  8. '''格式越简单的越靠前 格式越复杂的越靠后'''
  9. # 例二:
  10. m = 111
  11. def func(x, y ,z=m): # 在定义阶段就把111赋值给了z
  12. print(x)
  13. print(y)
  14. print(z)
  15. m = 666
  16. func(1, 2)

实参系列

位置实参

  • 在函数调用阶段括号内按照从左往右的顺序依次传入的数据值
  • 特点:
    • 按照位置为形参赋值,按照位置一一对应
    • 位置参数在绑定的时候多一个不行少一个也不行
  1. def func(name, age): # 定义位置形参:name,age,两者都必须被传值
  2. print(name)
  3. print(age)
  4. func("jack") # 报错,少了一个值
  5. func("jack",19) # 正常传值

关键字实参

  • 在函数调用阶段,按照 形参名 = 数据值 的形式指名道姓的传值
    • 特点:可以打乱顺序,但是仍然能够指定道姓的为指定的形参赋值
  1. def func(name, age): # 定义位置形参:name,age,两者都必须被传值
  2. print(name)
  3. print(age)
  4. func(age = 18,name = "jack") # 正常传值
  • 注意:可以混用位置实参与关键字实参,但是

    • 1、位置实参必须在关键字实参的前面
    • 2、不能为同一形参重复赋值
  1. # 例一:
  2. def func(name, age): # 定义位置形参:name,age,两者都必须被传值
  3. print(name)
  4. print(age)
  5. func("jack",age = 18) # 正常传值
  6. func(age = 18,"jack") # 报错,语法错误
  7. '''格式越简单的越靠前 格式越复杂的越靠后'''
  8. # 例二:
  9. def foo(x, y, z):
  10. pass
  11. foo(1, y=2,3) # 报错,语法错误
  12. foo(1, y=2, z=3, x=4) # 报错,语法错误

函数:函数的基本使用、函数参数的使用 - 图3

可变长参数

  • 可变长参数指的是在函数调用阶段,传入的实参个数不固定,对应着必须有特殊形式的形参来接收多余的实参
  • 实参无非两种形式
    • 多余的位置实参 *
    • 多余的位置关键字实参 **

与*在形参中是一种汇总行为

* 在形参中的使用

  • 用于接收多余的位置实参,并组织成元组的形式赋值给紧跟其后的形参名

  1. # 函数无论传入多少位置参数都可以正常运行
  2. def func(x, *y): # *是接收多余位置实参的功劳,不是y变量名的功劳。*后面可以跟任意变量名。
  3. print(x)
  4. print(y)
  5. func(1, 2, 3, 4, 5) # x = 1 , y = (2,3,4,5)
  6. '''
  7. 1
  8. (2,3,4,5)
  9. '''
  10. def func(x, *args): # args变量名通常搭配*使用
  11. print(x)
  12. print(args)
  13. func(1, 2, 3, 4, 5) # 同上是一个道理
  14. def my_sum(*args): # 求和运算的函数
  15. res = 0
  16. for i in args:
  17. res += i
  18. print(res)
  19. my_sum(1, 2) # 3

** 在形参中的使用

  • 用于接收多余的位置实参的关键字实参,并组织成字典的形式赋值给紧跟其后的那个形参名
  1. # 函数无论传入多少关键字参数都可以正常运行
  2. def func(x, **kwargs): # kwargs变量名通常搭配**使用
  3. print(x)
  4. print(kwargs)
  5. func(1,a=2,b=3,c=4)
  6. '''
  7. 1
  8. {'a': 2, 'b': 3, 'c': 4}
  9. '''
  10. # 定义一个函数无论传入多少位置参数和关键字参数都可以正常运行
  11. def index(*a,**b):
  12. print(a,b)
  13. index() # () {}
  14. index(1,2,3,4) # (1, 2, 3, 4) {}
  15. index(a=1,b=2,c=3) # () {'a': 1, 'b': 2, 'c': 3}
  16. index(1,2,3,4,a=1,b=2,c=3) # (1, 2, 3, 4) {'a': 1, 'b': 2, 'c': 3}
  17. """
  18. 墨守成规
  19. 可变长形参 *与**后面的变量名其实是可以随便定义的
  20. 但是python中推荐使用
  21. *args
  22. **kwargs
  23. def index(*args, **kwargs):
  24. pass
  25. """

与*在实参中是一种打散行为

* 在实参中的使用

  • 会将列表、元组内元素打散成位置实参的形式一一传值

  1. def func(x, y, z):
  2. print(x)
  3. print(y)
  4. print(z)
  5. func([11, 22, 33]) # 报错
  6. func(*[11,22,33]) # func(11,22,33) *会把列表这种实参打散成位置实参
  7. func(*"hello") # func("h","e","l","l","o") 报错,但能证明*的打散行为
  8. func(*{"k1":111,"k2":2222}) # func("k1","k2") 报错,但证明*能把字典打散成key

** 在实参中的使用

  • 会将字典键值对打散成关键字实参传入

  1. # ** 只能跟字典类型
  2. def func(x, y, z):
  3. print(x)
  4. print(y)
  5. print(z)
  6. func(**{"k1": 111, "k2": 2222}) # func(k2=2222,k1=111) 报错,没有k关键字,打散后对应不上
  7. func(**{"x": 111, "y": 2222, "z": 333}) # **将字典打散成关键字实参
  8. '''
  9. 111
  10. 2222
  11. 333
  12. '''
  13. # 例1:
  14. def wrapper(*args, **kwargs): # 形参中带*和**是一种汇总行为
  15. print(args)
  16. print(kwargs)
  17. wrapper(1, 2, 3, 4, 5, 6, a=1, b=2, c=3)
  18. '''
  19. (1, 2, 3, 4, 5, 6)
  20. {'a': 1, 'b': 2, 'c': 3}
  21. '''
  22. # 例2:
  23. def index(x, y, z):
  24. print(x, y, z)
  25. def wrapper(*args, **kwargs):
  26. index(*args, **kwargs)
  27. wrapper(1, y=2, z=3) # 1 2 3
  28. '''
  29. 实参中带 * 和 ** 是一种打散行为
  30. index(*(1, 2, 3, 4, 5, 6), **{"a": 1, "b": 2, "c": 3})
  31. index(1, 2, 3, 4, 5, 6, a=1, b=2, c=3)
  32. '''

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

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

  1. def register(name,age,*,sex,height):
  2. pass
  3. # register('jason',18,'male',183)
  4. register('lili',18,sex='male',height='1.8m') #正确使用
  5. """
  6. sex height在传入实参的时候必须以关键字参数的形式
  7. ps:该类型的参数几乎不用 也几乎很少能碰到
  8. """