Skip to content

函数

函数

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
  1. 匿名函数中**不能使用 if 语句、while 循环、for 循环**, 只能编写单行的表达式,或函数调用, 普通函数都可以.
  2. 匿名函数中返回结果**不需要使用 return**, 表达式的运行结果就是返回结果, 普通函数返回结果必须 return.
  3. 匿名函数中也可以不返回结果. 例如: 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 + ba -ba * 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 函数要写很多规则, 整个文件中可能就有很多定义的函数代码, 问题是:

  1. 这些函数只会用一次
  2. 这些函数也比较简单

就没有必要 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)
  1. 函数可以做为参数传递给另外一个函数, 可以使得函数的实现更加通用.
  2. 匿名函数也可以作为参数传递给另外一个函数, 对于只需要用到一次函数, 可以通过匿名函数减少代码量

高阶函数: 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() 函数会对参数序列中元素进行累计.

函数将一个数据集合中的所有数据进行下列操作:

  1. 用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作.
  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']