函数
函数¶
1.函数介绍¶
- 如果在开发程序时,需要某块代码多次,但是为了提高编写的效率以及代码的重用,所以把具有独立功能的代码块组织为一个小模块,这就是函数
2.函数定义和调用¶
def demo(): # demo 函数名
pass
demo() # 调用函数
- 每次调用函数时,函数都会从头开始执行,当这个函数中的代码执行完毕后,意味着调用结束了
- 当然了如果函数中执行到了return也会结束函数
3.函数的文档说明¶
def demo(a, b):
"""求a + b 的和"""
print(a + b)
demo(10, 20)
help(demo) # 能够看到demo函数的相关说明
30
Help on function demo in module __main__:
demo(a, b)
求a + b 的和
4.函数的参数(一)¶
def add_num(a, b):
print(a + b)
add_num(10, 20) # 调用带有参数的函数时,需要在小括号中,传递数据
# 调用函数时参数的顺序
add_num(b=100, a=200) # 如果使用关键字,必须都使用。否则按顺序传。
- 定义时小括号中的参数,用来接收参数用的,称为 “形参”
- 调用时小括号中的参数,用来传递给函数用的,称为 “实参”
5.函数的返回值(一)¶
def add_num(a, b):
return a + b
result = add_num(100, 200)
print(result)
print(add_num(10, 20)) # 函数的返回值必须要打印
6.函数的嵌套调用¶
def test_a():
print(f"函数{test_a.__name__}开始")
print(f"我是{test_a.__name__}函数")
print(f"函数{test_a.__name__}结束")
def test_b():
print(f"函数{test_b.__name__}开始")
test_a()
print(f"函数{test_b.__name__}结束")
test_b()
函数test_b开始
函数test_a开始
我是test_a函数
函数test_a结束
函数test_b结束
一个函数里面又调用了另外一个函数,这就是所谓的函数嵌套调用
7.局部变量¶
g = 1000 # 全局变量
def test1():
a = 300 # 局部变量
print(f"test1修改前a={a}")
a = 200
print(f"test1修改后a={a}")
print(g - a)
def test2():
a = 400 # 局部变量
print(f"test2a={a}")
print(g - a)
test1()
test2()
test1修改前a=300
test1修改后a=200
800
test2a=400
600
- 局部变量,就是在函数内部定义的变量
- 其作用范围是这个函数内部,即只能在这个函数中使用,在函数的外部是不能使用的
- 因为其作用范围只是在自己的函数内部,所以不同的函数可以定义相同名字的局部变量
- 局部变量的作用,为了临时保存数据需要在函数中定义变量来进行存储
- 当函数调用时,局部变量被创建,当函数调用完成后这个变量就不能够使用了
8.全局变量¶
01.什么是全局变量¶
# 定义全局变量
a = 100
def test1():
print(a) # 虽然没有定义变量a但是依然可以获取其数据
def test2():
print(a) # 虽然没有定义变量a但是依然可以获取其数据
# 调用函数
test1()
test2()
- 在函数外边定义的变量叫做
全局变量
- 全局变量能够在所有的函数中进行访问
02.全局变量和局部变量名字相同问题¶
a = 100 # 全局变量
def test1():
a = 300 # 局部变量
print(f"test1修改前a={a}")
a = 200
print(f"test1修改后a={a}")
def test2():
print(f"test2a={a}")
test1()
test2()
test1修改前a=300
test1修改后a=200
test2a=100
- 当函数内出现局部变量和全局变量相同名字时,函数内部中的
变量名 = 数据
此时理解为定义了一个局部变量,而不是修改全局变量的值
03.函数中修改全局变量¶
a = 100 # 全局变量
def test1():
global a
print(f"test1修改前a={a}")
a = 200
print(f"test1修改后a={a}")
def test2():
print(f"test2a={a}")
test1()
test2()
test1修改前a=100
test1修改后a=200
test2a=200
- 如果在函数中出现
global 全局变量的名字
那么这个函数中即使出现和全局变量名相同的变量名 = 数据
也理解为对全局变量进行修改,而不是定义局部变量- 如果在一个函数中需要对多个全局变量进行修改,那么可以使用
# 可以使用一次global对多个全局变量进行声明 global a, b # 还可以用多次global声明都是可以的 # global a # global b
9.多函数程序的基本执行流程¶
一般在实际开发过程中,一个程序往往由多个函数(后面知识中会讲解类)组成,并且多个函数共享某些数据,这种场景是经常出现的,因此下面来总结下,多个函数中共享数据的几种方式
01.使用全局变量¶
g_num = 0
def test1():
global g_num
g_num = 100
def test2():
print(g_num)
test1()
test2()
100
02.使用函数的返回值、参数¶
def test1():
return 50
def test2(num):
print(num)
result = test1()
test2(result)
50
03.函数嵌套调用¶
def test1():
return 20
def test2():
result = test1()
print(result)
test2()
20
10.函数的参数(二)¶
01.多个return¶
def test1():
print('111111')
return 1
print('22222')
return 2
print('3333')
print(test1())
111111
1
- 一个函数中可以有多个return语句,但是只要有一个return语句被执行到,那么这个函数就会结束了,因此后面的return没有什么用处
- 如果程序设计为如下,是可以的因为不同的场景下执行不同的return
def test1(a):
if a > 10:
return a + 10
else:
return -1
print(test1(1))
02.一个函数返回多个数据的方式¶
def divid(a, b):
shang = a // b
yushu = a % b
return shang, yushu
result = divid(10, 5)
print(result) # 默认是元组
(2, 0)
- return后面可以是元组,列表、字典等,只要是能够存储多个数据的类型,就可以一次性返回多个数据
def function(): # return [1, 2, 3] # return (1, 2, 3) return {"num1": 1, "num2": 2, "num3": 3}
- 如果return后面有多个数据,那么默认是元组
In [1]: a = 1, 2 In [2]: a Out[2]: (1, 2) In [3]: In [3]: b = (1, 2) In [4]: b Out[4]: (1, 2) In [5]:
11.函数的返回值(二)¶
01.缺省参数¶
调用函数时,缺省参数的值如果没有传入,则取默认值。
下例会打印默认的age,如果age没有被传入:
def test(name, age=35):
print(f'name={name}')
print(f'age={age}')
test('ruiwen')
test('ruiwen', 20)
name=ruiwen
age=35
name=ruiwen
age=20
- 在形参中默认有值的参数,称之为缺省参数
- 注意:带有默认值的参数一定要位于参数列表的最后面
def test(name, age=35, sex, ):
print(f'name={name}')
print(f'age={age}')
test('ruiwen', 'nan')
test('ruiwen', 20, 'nan')
# SyntaxError: non-default argument follows default argument
02.不定长参数¶
有时可能需要一个函数能处理比当初声明时更多的参数, 这些参数叫做不定长参数,声明时不会命名。
# 基本语法
def functionname([formal_args,] *args, **kwargs):
"""函数_文档字符串"""
function_suite
return [expression]
- 加了星号(*)的变量args会存放所有未命名的变量参数,args为元组
- 而加**的变量kwargs会存放命名参数,即形如key=value的参数, kwargs为字典.
def fun(a, b, *args, **kwargs):
"""可变参数演示实例"""
print(f'a={a}')
print(f'b={b}')
print(f'args={args}')
for k, v in kwargs.items():
print(f'kwargs: k={k},v={v}')
fun(10, 20, 30, 40, 50, x=3, y=4, z=5)
a=10
b=20
args=(30, 40, 50)
kwargs: k=x,v=3
kwargs: k=y,v=4
kwargs: k=z,v=5
c = (3, 4, 5)
d = dict(m=6, n=7, p=8)
fun(1, 2, *c, **d)
a=1
b=2
args=(3, 4, 5)
kwargs: k=m,v=6
kwargs: k=n,v=7
kwargs: k=p,v=8
c = (3, 4, 5)
d = dict(m=6, n=7, p=8)
fun(1, 2, c, d, [1, 2, 3], (1, 2, 3), "123", {"a": 1})
a=1
b=2
args=((3, 4, 5), {'m': 6, 'n': 7, 'p': 8}, [1, 2, 3], (1, 2, 3), '123', {'a': 1})
03.缺省参数在*args后面¶
def test(a, *args, b=22, c=23, **kwargs):
print(a)
print(args)
print(b)
print(c)
print(kwargs)
test(100, 200, 300, 400, 500, 600, 700, b=1, c=2, mm=800, nn=900)
100
(200, 300, 400, 500, 600, 700)
1
2
{'mm': 800, 'nn': 900}
- 如果很多个值都是不定长参数,那么这种情况下,可以将缺省参数放到 *args的后面, 但如果有**kwargs的话,**kwargs必须是最后的
12.拆包、交换俩个变量的值¶
01.返回的数据直接拆包¶
def test():
a = 1
b = 2
c = 3
return a, b, c
x, y, z = test()
print(x, y, z)
print(test())
for i in test(): # 元组可以遍历
print(i)
- 拆包时要注意,需要拆的数据的个数要与变量的个数相同,否则程序会异常
- 除了对元组拆包之外,还可以对列表、字典等拆包
In [17]: a, b = (11, 22)
In [18]: a
Out[18]: 11
In [19]: b
Out[19]: 22
In [20]: a, b = [11, 22]
In [21]: a
Out[21]: 11
In [22]: b
Out[22]: 22
In [23]: a, b = {"m":11, "n":22} # 取出来的是key,而不是键值对
In [24]: a
Out[24]: 'm'
In [25]: b
Out[25]: 'n'
02.交换2个变量的值¶
a, b = 10, 20
print(a, b)
a, b = b, a
print(a, b)
13.引用(一)¶
a = 1
b = a
print(b)
a = 2
print(a)
1
2
a = [1,2]
b = a
print(b)
a.append(3)
print(a)
[1, 2]
[1, 2, 3]
在python中,值是靠引用来传递来的。
我们可以用id()来判断两个变量是否为同一个值的引用。
我们可以将id值理解为那块内存的地址标示。
>>> a = 1
>>> b = a
>>> id(a)
13033816
>>> id(b) # 注意两个变量的id值相同
13033816
>>> a = 2
>>> id(a) # 注意a的id值已经变了
13033792
>>> id(b) # b的id值依旧
13033816
>>> a = [1, 2]
>>> b = a
>>> id(a)
139935018544808
>>> id(b)
139935018544808
>>> a.append(3)
>>> a
[1, 2, 3]
>>> id(a)
139935018544808
>>> id(b) # 注意a与b始终指向同一个地址
139935018544808
- 之前为了更好的理解变量,咱们可以把
a=100
理解为变量a中存放了100,事实上变量a存储是100的引用(可理解为在内存中的一个编号)
14.可变、不可变类型¶
- 所谓可变类型与不可变类型是指:数据能够直接进行修改,如果能直接修改那么就是可变,否则是不可变
- 可变类型有: 列表、字典、集合
- 不可变类型有: 数字、字符串、元组
15.引用(二)¶
引用当做实参¶
- 可变类型与不可变类型的变量分别作为函数参数时,会有什么不同吗?
- Python有没有类似C语言中的指针传参呢?
def test1(b): # 变量b一定是一个局部变量,就看它指向的是谁?可变还是不可变
b += b # += 是直接对b指向的空间进行修改,而不是让b指向一个新的
# b = b+b # xx = xx+yyy 先把=号右边的结果计算出来,然后让b指向这个新的地方,不管原来b指向谁
# 现在b一定指向这个新的地方
# a = [11, 22]
a = 100
test1(a)
print(a)
100
- Python中函数参数是引用传递(注意不是值传递)
- 对于不可变类型,因变量不能修改,所以运算不会影响到变量自身
- 而对于可变类型来说,函数体中的运算有可能会更改传入的参数变量
16.函数的注意事项¶
01.自定义函数¶
无参数、无返回值¶
def 函数名():
语句
无参数、有返回值¶
def 函数名():
语句
return 需要返回的数值
注意:
- 一个函数到底有没有返回值,就看有没有return,因为只有return才可以返回数据
- 在开发中往往根据需求来设计函数需不需要返回值
- 函数中,可以有多个return语句,但是只要执行到一个return语句,那么就意味着这个函数的调用完成
有参数、无返回值¶
def 函数名(形参列表):
语句
注意:
- 在调用函数时,如果需要把一些数据一起传递过去,被调用函数就需要用参数来接收
- 参数列表中变量的个数根据实际传递的数据的多少来确定
有参数、有返回值¶
def 函数名(形参列表):
语句
return 需要返回的数值
函数名不能重复¶
- 如果在同一个程序中出现了多个相同函数名的函数,那么在调用函数时就会出现问题,所以要避免名字相同
- 还有一点 不仅要避免函数名之间不能相同,还要避免 变量名和函数名相同的,否则都会出现问题
- 详细的讲解在python就业班中进行学习,此阶段只要注意这些问题即可
02.函数调用方式¶
调用的方式为:
函数名([实参列表])
调用时,到底写不写 实参 如果调用的函数 在定义时有形参,那么在调用的时候就应该传递参数
调用时,实参的个数和先后顺序应该和定义函数中要求的一致 如果调用的函数有返回值,那么就可以用一个变量来进行保存这个值
03.作用域¶
在一个函数中定义的变量,只能在本函数中用(局部变量)
在函数外定义的变量,可以在所有的函数中使用(全局变量)
17.递归函数¶
01.什么是递归函数¶
一个函数可以调用其他函数。
如果一个函数在内部不调用其它的函数,而是自己本身的话,这个函数就是递归函数。
02.递归函数的作用¶
计算阶乘
def calNum(num):
i = 1
result = 1
while i <= num:
result *= i
i += 1
return result
ret = calNum(3)
print(ret)
def calNum(num):
if num >= 1:
result = num * calNum(num - 1)
else:
result = 1
return result
ret = calNum(3)
print(ret)
18.匿名函数¶
01.语法格式¶
定义的函数没有名字,这样的函数叫做**匿名函数.**
# lambda [形参1], [形参2], ... : [单行表达式] 或 [函数调用]
使用匿名函数, 上面的函数我们就可以定义为单行的函数.
# 不带参数
fun = lambda: 10 + 20
print(fun())
# 带参数
add = lambda a, b: a + b
print(add(10, 20))
02.和普通函数的区别¶
def my_function(start, end):
my_sum = 0
while start <= end:
my_sum += start
start += 1
return my_sum
- 匿名函数中**不能使用 if 语句、while 循环、for 循环**, 只能编写单行的表达式,或函数调用, 普通函数都可以.
- 匿名函数中返回结果**不需要使用 return**, 表达式的运行结果就是返回结果, 普通函数返回结果必须 return.
- 匿名函数中也可以不返回结果. 例如:
lambda : print('hello world')
03.应用场景¶
1.定义简单的单行函数¶
def my_function(a, b):
return a + b
使用 lambda 定义格式如下:
my_function = lambda a, b: a + b
2.作为函数的参数进行传递(重点、难点)¶
作为函数的参数进行传递(重点、难点)
def my_function1():
a = 100
b = 200
result = a + b
print('result:', result)
def my_function2():
a = 100
b = 200
result = a - b
print('result:', result)
def my_function3():
a = 100
b = 200
result = a * b
print('result:', result)
上面函数定义的缺点是:
函数名要定义多个, 使用起来不方便. 需要记忆, 那个函数完成什么事情.
上面函数只定义了三种计算方式: a + b、a -b、a * b, 如果用户希望 a 和 b 进行除法运算, 那么显然还需要再写函数.
解决目标: 1、提高函数的通用性 2、减少代码量
可以通过传递给 my_function 一个函数来完成.
def my_function(func):
a = 100
b = 200
# 把 cucalate_rule 当做函数来调用
result = func(a, b)
print('result:', result)
def caculate_rule(a, b):
return a / b
my_function(caculate_rule)
3.匿名函数做为函数参数¶
def my_function(func):
a = 100
b = 200
# 把 cucalate_rule 当做函数来调用
result = func(a, b)
print('result:', result)
def caculate_rule1(a, b):
return a / b
def caculate_rule2(a, b):
return a // b
def caculate_rule3(a, b):
return a % b
my_function(caculate_rule1)
my_function(caculate_rule2)
my_function(caculate_rule3)
我们为了调用 my_function 函数要写很多规则, 整个文件中可能就有很多定义的函数代码, 问题是:
- 这些函数只会用一次
- 这些函数也比较简单
就没有必要 py 文件中出现这么多无用的代码. 如何改进:
def my_function(func):
a = 100
b = 200
# 把 cucalate_rule 当做函数来调用
result = func(a, b)
print('result:', result)
my_function(lambda a, b: a / b)
my_function(lambda a, b: a // b)
my_function(lambda a, b: a % b)
- 函数可以做为参数传递给另外一个函数, 可以使得函数的实现更加通用.
- 匿名函数也可以作为参数传递给另外一个函数, 对于只需要用到一次函数, 可以通过匿名函数减少代码量
高阶函数: map reduce filter¶
1. map 用法¶
map() 会根据提供的函数对指定序列做映射.
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表.
计算每一个元素的平方值:
my_list = [1, 2, 3, 4, 5]
def f(x):
return x ** 2
result = map(f, my_list)
print(type(result), result, list(result))
输出结果:
<class 'map'> <map object at 0x000000C9729591D0> [1, 4, 9, 16, 25]
示例解释:
首字母大写:
my_list = ['smith', 'edward', 'john', 'obama', 'tom']
def f(x):
return x[0].upper() + x[1:]
result = map(f, my_list)
print(list(result))
输出结果:
['Smith', 'Edward', 'John', 'Obama', 'Tom']
2. reduce 用法¶
reduce() 函数会对参数序列中元素进行累计.
函数将一个数据集合中的所有数据进行下列操作:
- 用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作.
- 得到的结果再与第三个数据用 function 函数运算, 最后得到一个结果.
计算列表中的累加和:
import functools
my_list = [1, 2, 3, 4, 5]
def f(x1, x2):
return x1 + x2
result = functools.reduce(f, my_list)
print(result)
输出结果:
15
3. filter 用法¶
filter() 函数用于过滤序列, 过滤掉不符合条件的元素, 返回一个 filter 对象, 如果要转换为列表, 可以使用 list() 来转换.
该接收两个参数, 第一个为函数, 第二个为序列, 序列的每个元素作为参数传递给函数进行判, 然后返回 True 或 False, 最后将返回 True 的元素放到新列表中.
过滤列表中的偶数:
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def f(x):
return x % 2 == 0
result = filter(f, my_list)
print(list(result))
输出结果:
[2, 4, 6, 8, 10]
过滤列表中首字母为大写的单词:
my_list = ['edward', 'Smith', 'Obama', 'john', 'tom']
def f(x):
return x[0].isupper()
result = filter(f, my_list)
print(list(result))
输出结果:
['Smith', 'Obama']