Python基础复习07_函数

函数

Outline

·1.作用
·2.使用步骤
·3.参数
·4.返回值
·5.说明文档
·6.函数嵌套
·7.变量作用域
·8.多函数程序执行流程
·9.返回值进阶
·10.参数进阶
·11.拆包和交换两个变量的值
·12.引用
·13.可变与不可变类型
·14.递归
·15.lambda表达式
·16.高阶函数

1. 函数的作用

·函数是将 一段具有独立功能的代码块整合到一个整体并命名,在需要的位置 调用这个名称即可完成对应的需求
·函数在开发过程中,可以高效地实现 代码重用

2. 函数的使用步骤

2.1 定义函数

·语法:

def 函数名(参数):
   代码1
   代码2
   ......

2.2 调用参数

·语法:

    函数名(参数)

·注意:
1.对于不同的需求,参数可有可无
2.在Python中函数必须 先定义后使用
·实现功能时可以学习的地方:先搭建整体框架(复现需求),再补充需求内容

print('密码正确登录成功')

# 显示"选择功能"界面

print('查询余额完毕')

# 显示"选择功能"界面

print('取了2000元钱')

# 显示"选择功能"界面
# 需求:复现ATM取钱功能

def select_fun():
    # 功能:查询余额、存款、取款
    
    print("--------------------")
    print("请选择要办理的业务")
    print("查询余额")
    print("存款")
    print("取款")
    print("--------------------")

    
print("密码正确!登录成功!")
select_fun()
print("余额查询完毕")
select_fun()
print("取了2000元")
密码正确!登录成功!
--------------------
请选择要办理的业务
查询余额
存款
取款
--------------------
余额查询完毕
--------------------
请选择要办理的业务
查询余额
存款
取款
--------------------
取了2000元

3. 函数的参数

·思考:若定义了如下函数完成1+2地运算,如果想要这个函数变得更灵活,可以计算任何用户指定的两个数字的和,如何书写程序?

# 定义函数
def add_num1():
    result = 1 + 2
    print(result)


# 调用函数
add_num1()

·分析:用户要在调用函数的时候指定具体数字,那么在定义函数的时候就需要接收用户指定的数字。函数调用时候指定的数字和定义函数时候接收的数字即是函数的参数。

# 实现任意两数相加
def add_2num(a,b):
    print(f"{a} + {b} = {a+b}")
    
add_2num(99,1)
99 + 1 = 100

3.1 参数传递

·在python中,类型是属于对象的,而变量是没有类型的:

a = [1,2,3]

a = 'lcj'

·在以上代码中,[1,2,3]是一个list类型,'lcj’是一个string类型,而变量a是没有类型的,他仅仅只是一个对象的引用(可以理解为一个指针),可以是指向list类型对象,也可以是指向string类型对象

3.1.1 可变类型对象及其参数传递

在python中,list、dict、set等是可以修改的对象。

·变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

参数传递:

·类似 C++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响

3.1.2 不可变类型对象及其参数传递

在python中,string、tuple、numbers是不可变数据类型

·变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变 a 的值,相当于新生成了 a。

参数传递:

·类似 C++ 的值传递,如整数、字符串、元组。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。

# 不可变类型对象参数传递
def change(a):
    print(id(a))   # 指向的是同一个对象
    a=10
    print(id(a))   # 一个新对象
 
a=1
print(id(a))
change(a)

# 可变类型对象参数传递
# 可写函数说明
def changeme( mylist ):
   "修改传入的列表"
   mylist.append([1,2,3,4])
   print ("函数内取值: ", mylist)
   return
 
# 调用changeme函数
mylist = [10,20,30]
changeme( mylist )
print ("函数外取值: ", mylist)
140713493473056
140713493473056
140713493473344
函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
函数外取值:  [10, 20, 30, [1, 2, 3, 4]]

4. 函数的返回值

·在函数中,如果需要返回结果给用户,则需要使用函数返回值。

# 计算任意两数之和,并保存结果
def sum_num(a,b):
    return a + b

result = sum_num(10,99)
print(result)
109

5. 函数的说明文档

·语法:

def 函数名(参数):
    """ 说明文档的位置 """
    代码
    ......

·查看函数的说明文档

help(函数名)
def test_fun():
    """
    本函数用来测试函数说明文档作用
    换行
    """

help(test_fun)
Help on function test_fun in module __main__:

test_fun()
    本函数用来测试函数说明文档作用
    换行

6.函数的嵌套调用

·函数的嵌套调用是指一个函数中又调用了另个函数

def testA():
    print('---- testA start ----')
    print("这是函数A执行的代码块")
    print('---- testA done ----')

def testB():
    print('---- testB start ----')
    testA()
    print('---- testB done ----')
    
testB()
---- testB start ----
---- testA start ----
这是函数A执行的代码块
---- testA done ----
---- testB done ----

7. 变量作用域

·变量作用域指的是变量生效的范围。主要分为两类:局部变量全局变量

7.1 局部变量

·定义:局部变量是定义在函数体内部的变量,即只在函数体内部生效
·作用:在函数体内部,临时保存数据,即当函数调用完成后,销毁局部变量

# 局部变量
def test1():
    A = 100
    print(A)

test1()
print(A)  # 会报错
100



---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

 in 
      5 
      6 test1()
----> 7 print(A)  # 会报错


NameError: name 'A' is not defined

7.2 全局变量

·全局变量是指在函数体内、外都可以生效的变量

# 定义全局变量
B = 100

def test2():
    print(B) # 访问全局变量B,并打印其存储的数据
    
def test3():
    print(B) # 访问全局变量B,并打印其存储的数据
    
test2()
test3()
100
100

·思考:如果test3需要修改变量B为200,应该如何修改?

# 修改全局变量--1
B = 100
listx = [10,20,30]

def test2():
    print(B) # 访问全局变量B,并打印其存储的数据
    
def test3():
    B = 200
    listx.append([1,2,3])
    print(B) # 访问全局变量B,并打印其存储的数据
    
test2()
test3()
print(f"全局变量B的值:{B}")
print(listx)
100
200
全局变量B的值:100
[10, 20, 30, [1, 2, 3]]

我们发现这样修改全局变量其实并没有生效,最后打印出来的全局变量B的值仍然为100;
这里我们添加一个列表作为对照,可以很清楚的看到,列表在函数中的修改中是生效了的,这就是我们上文提到的可变与不可变数据类型的区别,作为指向不可变数据对象的B变量,在test3中试图修改时,其实是函数在内部创建了一个名字也叫B的局部变量;而作为可变数据类型的列表,则是将列表对象直接传入test3函数中修改。
最后,我们如何在函数体内修改全局变量呢?
→使用global关键字

# 修改全局变量--2
B = 100

def test2():
    print(B) # 访问全局变量B,并打印其存储的数据
    
def test3():
    # 使用global关键字声明B是全局变量
    global B
    B = 200
    print(B) # 访问全局变量B,并打印其存储的数据
    
test2()
test3()
print(f"全局变量B的值:{B}")
100
200
全局变量B的值:200

8. 多函数程序执行流程

·在实际开发过程中,一个程序往往由多个函数组成,并且多个函数可能会共享某些数据

8.1 共用全局变量

# 定义全局变量
glo_num = 100

def test1():
    global glo_num
    glo_num = 1  # 修改全局变量
    
def test2():
    print(glo_num) # 调用全局变量

test2() # 打印修改前的全局变量
test1() # 修改全局变量
test2() # 打印修改后的全局变量
100
1

8.2 返回值作为参数传递

def test1():
    return 99

def test2(num):
    print(num)
    
result = test1()  # 保存test1的返回值
test2(result) # 将返回值作为参数传递到test2中
99

9. 返回值进阶

·思考:如果一个函数有多个return语句,执行结果会怎样?
Answer:只会执行第一个,因为return会退出当前函数,导致第一个return后面的代码不会再执行
·如何返回多个值?

# 多个返回值函数
def return_2():
    return 1,2,3

result = return_2()
print(result)
print(type(result))
(1, 2, 3)

注意:
·return a,b写法在返回多个数据的时候,默认为元组类型
·return后面可以连接列表、元组或字典,以返回多个值

10. 参数进阶

10.1 位置参数

·定义:调用函数时根据函数定义的参数位置来传参
·注意:传递和定义参数的顺序及个数必须一致!

# 位置参数
def fun_location(name,age,gender):
    print(f"Your name : {name}\nYour age : {age}\nYour gender : {gender}")
    
fun_location('lee',20,'male')
Your name : lee
Your age : 20
Your gender : male

10.2 关键字参数

·通过“key=value”形式加以指定。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
·注意:函数调用时,如果有位置参数时,位置参数必须在关键字参数的前面,但关键字参数之间不存在先后顺序。

# 关键字参数
def fun_keyword(name,age,gender):
    print(f"Your name : {name}\nYour age : {age}\nYour gender : {gender}")
    
fun_keyword('Lee',age=20,gender='male')
fun_keyword(age=21,gender='male',name='jcl')
Your name : Lee
Your age : 20
Your gender : male
Your name : jcl
Your age : 21
Your gender : male

10.3 默认参数

·也叫缺省参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值
·注意:
1.所有位置参数必须出现在默认参数之前,包括函数定义和调用
2.函数调用时,如果为默认参数传值则修改默认参数值,否则使用默认值

# 默认参数
def fun_tolerant(name,age,gender='男'):
    print(f"Your name : {name}\nYour age : {age}\nYour gender : {gender}")
    
fun_tolerant('TOM',21)
fun_tolerant('Rose',19,'female')
Your name : TOM
Your age : 21
Your gender : 男
Your name : Rose
Your age : 19
Your gender : female

10.4 不定长参数

·也叫可变参数。用于不确定调用的时候会传递多少个参数(也可以不传参)的场景。此时可用包裹(packing)位置参数,或者包裹关键字参数,来进行参数传递。
·加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。

# 包裹位置传递
def user_info(*args):
    print(args)

user_info('Tom')
user_info('Tom',18,'male')
('Tom',)
('Tom', 18, 'male')

·注意:传进的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元组(tuple),args是元组类型,这就是包裹位置传递。

# 包裹关键字传递
def user_info(**kvargs):
    print(kvargs)
    
user_info(name='lee',age=18,gender='male')
{'name': 'lee', 'age': 18, 'gender': 'male'}

11. 拆包和交换变量值

11.1 拆包

# 拆包:元组
def return_num():
    return 1,99

num1,num2 = return_num()
print("元组拆包")
print(num1)
print(num2)
print('-'*10)


# 拆包:字典
dict1 = {
     'name':'lee','age':20}
a,b = dict1

print("字典拆包")   # 字典拆包,取出来的是字典的key值
print(a) 
print(b)

print(dict1[a])
print(dict1[b])
元组拆包
1
99
----------
字典拆包
name
age
lee
20

11.2 交换变量值

·需求:变量a = 10,b = 20,交换两个变量的值

# 常规方法:借助中间变量实现交换

a,b = 10,20
c = 0 # 定义中间变量

c = a # 将a的值储存到c

a = b # 将b的值赋给a

b = c # 将a的值赋给b

print(f"a的值:{a}\nb的值:{b}")
a的值:20
b的值:10
# 方便方法!
a, b = 1, 2
print(f"交换前:\na的值:{a}\nb的值:{b}")
a, b = b, a
print(f"交换后:\na的值:{a}\nb的值:{b}")
交换前:
a的值:1
b的值:2
交换后:
a的值:2
b的值:1

12. 引用

12.1 了解引用

·python中,值是靠引用来传递的
我们可以用id()来判断两个变量是否为同一个值的引用
id可以理解为那块内存的地址标识

# 1. 不可变数据类型:以int为例
a = 1
b = a
print(b)
print(f"a的地址:{id(a)}\nb的地址:{id(b)}")

a = 2
print(b) # 说明int为不可变数据类型
print(f"a的地址:{id(a)}\nb的地址:{id(b)}")
1
a的地址:140713493473056
b的地址:140713493473056
1
a的地址:140713493473088
b的地址:140713493473056
# 2. 可变数据类型:以list为例
aa = [10,20]
bb = aa
print(bb)
print(f"aa的地址:{id(aa)}\nbb的地址:{id(bb)}")

aa.append(99)
print(bb) # bb的值也变化,说明列表为可变数据类型
print(f"aa的地址:{id(aa)}\nbb的地址:{id(bb)}")
[10, 20]
aa的地址:1658821373440
bb的地址:1658821373440
[10, 20, 99]
aa的地址:1658821373440
bb的地址:1658821373440

12.2 引用当作实参

def test1(a):
    print(f"a值为:{a}\na地址为:{id(a)}")
    
    a += a
    
    print(f"a值为:{a}\na的地址:{id(a)}")
          
# int
b = 100
test1(b)
          
# list
c = [11,22]
test1(c)
a值为:100
a地址为:140713493476224
a值为:200
a的地址:140713493479424
a值为:[11, 22]
a地址为:1658825582336
a值为:[11, 22, 11, 22]
a的地址:1658825582336

13. 可变与不可变数据类型

·可变数据类型:列表、字典、集合
·不可变数据类型:Numbers、字符串、元组

14. 递归

14.1 应用场景

·递归是一种编程思想:
1.在我们日常开发中,如果要遍历一个文件夹下面所有的文件,通常会使用递归来实现;
2.在后续的算法课程中,很多算法都离不开递归,例如:快速排序。
·特点:
1.函数内部自己调用自己
2.必须有出口

# 递归应用:累加

def sum_num(num):
    # 判断:如果为1则直接返回1-----递归的出口
    if num == 1:
        return 1
    # 如果不是1,则递归求解
    return num + sum_num(num-1)

result = sum_num(10)  # 1-10累加
print(result)
55

15. lambda表达式

·也叫匿名函数:

所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。  
lambda 只是一个表达式,函数体比 def 简单很多。  
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。  
lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。  
虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。  

·应用场景:如果一个函数有一个返回值,且只有一句代码,则可以使用lambda表达式简化。
·语法:

lambda 参数列表 : 表达式

·注意事项:
1.lambda表达式的参数可有可无,函数的参数在lambda表达式中完全适用。
2.lambda表达式能接收任何数量的参数但只能返回一个表达式的值。
3.直接打印lambda表达式,输出的是此lambda的内存地址

# lambda表达式计算a+b
fn = lambda a, b: a + b
print(fn)
print(fn(1,2))
 at 0x00000182399A00D0>
3
# lambda的参数形式

# 无参
fn1 = lambda:100
print(fn1())

# 一个参数
fn2 = lambda a: a
print(fn2("Hello,World!"))

# 默认参数
fn3 = lambda a, b, c=100: a + b + c
print(fn3(5,3))

# 可变参数:*args
fn4 = lambda *args: args
print(fn4(1,2,3))

# 可变参数:**kvargs
fn5 = lambda **kvargs: kvargs
print(fn5(name='lee',age=20))
100
Hello,World!
108
(1, 2, 3)
{'name': 'lee', 'age': 20}

·注意:可变参数*args传入lambda后返回值为元组

# lambda表达式应用1---带判断的lambda
fn1 = lambda a, b: a if a > b else b
print(fn1(40,99))
99
# 数据按字典key的值排序

students = [
    {
     'name': 'TOM', 'age': 20},
    {
     'name': 'ROSE', 'age': 19},
    {
     'name': 'Jack', 'age': 22}
]

# 按name升序排列
students.sort(key=lambda x: x['name'])
print(students)

# 按name降序排列
students.sort(key=lambda x: x['name'],reverse=True)
print(students)

# 按age值升值排序
students.sort(key=lambda x: x['age'])
print(students)
[{'name': 'Jack', 'age': 22}, {'name': 'ROSE', 'age': 19}, {'name': 'TOM', 'age': 20}]
[{'name': 'TOM', 'age': 20}, {'name': 'ROSE', 'age': 19}, {'name': 'Jack', 'age': 22}]
[{'name': 'ROSE', 'age': 19}, {'name': 'TOM', 'age': 20}, {'name': 'Jack', 'age': 22}]

16. 高阶函数

·把函数作为参数传入,这样的函数称为高阶函数,高阶函数是函数式编程的体现。函数式编程就是指这种高度抽象的编程范式。

"""
abs():求数字的绝对值
round():对数字四舍五入
需求:任意两个数字,按照指定要求整理数字后求和
"""

# 方法1.
def add_num(a,b):
    return abs(a)+abs(b)

result = add_num(-1,9)
print(result)

# 方法2.
def sum_num(a,b,f):
    return f(a)+f(b)

result2 = sum_num(-1,9,abs)
print(result2)
result3 = sum_num(1.2,9.4,round)
print(result3)
10
10
10

16.1 内置高阶函数

16.1.1 map()

·map(func, lst),将传入的函数变量func作用到lst变量的每个元素中,并将结果组成新的迭代器(Python3)返回。

# 需求:计算list序列中各个数字的2次方
list1 = [1,2,3,4,5]

func = lambda x: x**2

result = map(func,list1)
print(result)
print(list(result))

[1, 4, 9, 16, 25]
16.1.2 reduce()

·reduce(func,lst),其中func必须有两个参数。每次func计算的结果继续和序列的下一个元素做累积计算。
·注意:reduce()传入的参数func必须接收两个参数

# 需求:计算list1序列中各个数字的累加和
import functools

list1 = [1,2,3,4,5]

func = lambda a, b: a + b

result = functools.reduce(func,list1)

print(result)
15

16.1.3 filter()

·filter(func, lst)函数用于过滤序列, 过滤掉不符合条件的元素, 返回一个 filter 对象。如果要转换为列表, 可以使用 list() 来转换。

# filter()

list1 = [1,2,3,4,5,6,7,8,9,10]

func = lambda x: x % 2 == 0

result = filter(func,list1)

print(result)
print(list(result))

[2, 4, 6, 8, 10]

你可能感兴趣的:(Python)