Python-12-函数进阶

本文详细探讨了Python中的函数概念,包括变量作用域(局部与全局)、如何在全局中访问和修改局部变量、多函数执行流程、返回值的处理方式以及参数的使用(位置参数、关键字参数、默认值、不定长参数)。此外,还讲解了递归函数的工作原理和注意事项,并介绍了lambda表达式以及高阶函数的应用。内容涵盖了函数设计的重要方面,有助于深化对Python函数理解。
摘要由CSDN通过智能技术生成

函数进阶

一、变量的作用域

在Python脚本中,每个变量都自己作用域,根据作用范围不同分为:局部变量全局变量

1.1 局部变量

局部变量,在函数体中定义的变量,作用范围只在函数中。

1.1.1 在全局访问局部变量
  • 不可变数据类型
# 定义函数,在函数体定义一个变量,变量的类型为不可变
def func():
    global a			# 声名局部变量为全局变量
    a = 0
    print(f'局部a={a};a的ID={id(a)}')
    
# 调用函数
func()			# 在全局访问局部前,必须调用函数
print(f'全局a={a};a的ID={id(a)}')

''' 输出结果:
局部a=0;a的ID=140736419202704
全局a=0;a的ID=140736419202704
'''

在全局访问局部变量,必须先调用函数,创建局部变量,如此才可以在全局访问这个局部变量!

  • 可变数据类型
def func():
    global lst			# 声名局部变量为全局
    lst = [1, 2]
    print(f'局部lst={lst};lst的ID={id(lst)}')

func()
print(f'全局lst={lst};lst的ID={id(lst)}')

''' 输出结果:
局部lst=[1, 2];lst的ID=2039571804800
全局lst=[1, 2];lst的ID=2039571804800
'''
1.1.2 在全局中修改局部变量
  • 不可变数据类型
# 定义函数
def func():
    global a
    a = 0
    print(f'局部a={a};a的ID={id(a)}')
    
func()

print(f'修改之前的全局a={a};a的ID={id(a)}')
# 在全局修改局部变量
a = 1
print(f'修改之后的全局a={a};a的ID={id(a)}')

image-20210428164253678

  • 可变数据类型
# 定义函数
def func():
    global lst
    lst = [1, 2]
    print(f'局部lst={lst};lst的ID={id(lst)}')
    
func()

print(f'修改之前的全局lst={lst};lst的ID={id(lst)}')
# 在全局修改局部变量
lst.append(3)
print(f'修改之后的全局lst={lst};lst的ID={id(lst)}')

image-20210428164335052

全局修改局部变量:

  • 如果全局在原有内存空间上进行修改,那么局部会重新创建一个原有变量的新空间;

  • 如果全局创建一个新的空间,那么局部保持不变。

1.2 全局变量

全局变量是指定在整个脚本中可以任意访问的变量,定义在函数之外。

1.2.1 在局部访问全局变量
  • 不可变数据类型
# 定义函数,在函数中访问全局变量
def func1():
    print(f'局部a={a};a的ID={id(a)}')

# 定义变量
a = 0
func1()
  • 可变数据类型
# 定义函数,在函数中访问全局变量
def func1():
    print(f'局部a={a};a的ID={id(a)}')

# 定义变量
lst = [1, 2]
func1()

在局部访问全局变量,不需要加上global,局部可以直接访问!

1.2.2 在局部修改全局变量
  • 不可变数据类型
# 定义函数,在函数中访问全局变量
def func1():
    global a			# 需要加上global
    print(f'修改全局之前,局部a={a};a的ID={id(a)}')
    a = 1
    print(f'修改全局之后,局部a={a};a的ID={id(a)}')

# 定义变量
a = 0
print(f'调用之前,全局a={a};a的ID={id(a)}')
func1()
print(f'调用之后,全局a={a};a的ID={id(a)}')

image-20210428164415652

  • 可变数据类型
# 可变数据类型
def func1():
    print(f'修改全局之前,局部lst={lst};lst的ID={id(lst)}')
    lst.append(3)
    print(f'修改全局之后,局部lst={lst};lst的ID={id(lst)}')

# 定义变量
lst = [1, 2]
print(f'调用之前,全局lst={lst};lst的ID={id(lst)}')
func1()
print(f'调用之后,全局lst={lst};lst的ID={id(lst)}')

image-20210428164439985

局部修改全局变量:

  • 如果全局变量的指向改变,则需要加上global
  • 如果全局变量的指向不改变,则不加global

二、多函数的执行流程

  • 函数之间共享全局变量
# 修改全局变量
def update():
    lst.append(3)
    print(f'修改后lst={lst}')

# 访问全局变量
def access():
    print(f'访问lst={lst}')


# 全局变量
lst = [1, 2]
# 调用修改函数
update()
# 调用访问函数
access()

''' 输出结果:
修改后lst=[1, 2, 3]
访问lst=[1, 2, 3]
'''

函数之间是共享全局变量的!

  • 返回值传入到另一个函数中
# 定义函数,且返回100
def func1():
    return 100


# 定义函数,定义形参
def func2(arg):
    print(arg)

# 将第一个函数的返回值传入到第二个函数中
func2(func1())				# 100

三、函数的返回值

  • 没有return
# 定义函数,不写return
def func():
    pass

print(func())			# None
  • return多个值
# 定义函数,return多个值
def func():
    return 11, 'a', [22]

# 调用函数,并将函数的返回值保存到变量中
a = func()

print(a, type(a))			# (11, 'a', [22]) <class 'tuple'>
  • 多个return:函数的return会跳出函数体,return之后的代码不会执行
def func():
    return 1
    return 2
    return 3

a = func()
print(a)			# 1
  • if中的retrun

案例:判断两个数的大小,将较大的数字返回。

def func(x, y):
    if x > y:
        return x
    else:
        return y

a = func(1, 2)
print(a)			# 2

b = func(5, 3)
print(b)			# 5

四、函数的参数

函数的参数分为两种:实际参数形式参数

4.1 实参

实参指的是在函数调用时传入的参数

  • 位置实参
def func(a, b, c):
    print(f'a={a}, b={b}, c={c}')

func(1, 2, 3)
  • 位置实参必须按传参的顺序传入
  • 位置实参的传入个数必须与形参个数相同
  • 关键字实参
def func(a, b, c):
    print(f'a={a}, b={b}, c={c}')

func(1, b=2, c=3)
func(1, c=3, b=2)
func(1, 3, b=3, c=3)			# 报错,调用一次函数,参数只能传入一次
func(a=1, 2, c=3)				# 位置实参只能在关键字实参之前传入
func(a=1, b=2, 3)				# 位置实参只能在关键字实参之前传入
  • 关键字实参必须在位置实参之后传入
  • 关键字实参传入与传入的顺序无关
  • 序列转为位置实参
def func(a, b, c):
    print(f'a={a}, b={b}, c={c}')
    
# 1. 字符串
str1 = 'yes'
func(*str1)				# a=y, b=e, c=s

# 2. 列表
lst = [11, 22, 33]
func(*lst)				# a=11, b=22, c=33

# 3. 元组
t = 22, 'a', [1]
func(*t)				# a=22, b=a, c=[1]
  • 字典转为关键字实参
def func(a, b, c):
    print(f'a={a}, b={b}, c={c}')

# 字典的key必须与形参一致
d = dict(
    a=11,
    b=22,
    c=33
)

func(*d)			# a=a, b=b, c=c			# 只传入字典的key
func(**d)			# a=11, b=22, c=33

4.2 形参

形参是指函数定义创建的参数。

  • 位置形参
def func(a, b, c):
    print(f'a={a}, b={b}, c={c}')
    
func(1, 2, 3)				# a=1, b=2, c=3
func(a=1, b=2, c=3)			# a=1, b=2, c=3
func(1, 2, c=3)				# a=1, b=2, c=3
func(1, b=2, c=3)			# a=1, b=2, c=3
  • 仅位置形参:Python3.8中新加入的功能,/之前的形参必须使用位置实参传入。
def func(a, b, /):
    print(f'a={a};b={b}')
    
func(1, 2)
func(1, b=2)		# 报错,使用了关键字实参

设计的目的:

  • 限制调用者使用关键字实参传入;
  • 因为创建者考虑到后续会修改形参,因此限制调用者使用关键字实参传入;
  • 默认值形参
  1. 全部都是默认值形参
def func(a=1):
    print(a)
    
func()			# 1。使用形参的默认值
func(2)			# 2。使用位置实参
func(a=3)		# 3。使用关键字实参
  1. 与位置形参同时创建:位置形参必须在默认值形参之前定义
def func(a, b=1):
    print(f'a={a};b={b}')

func(1)
func(1, 2)
func(1, b=3)
func(a=1, b=2)
func(a=1, 2)
  1. 默认值形参只计算一次
i = 5

def func(arg=i):
    print(arg)

i = 6

func()					# 5
  1. 默认值形参的值必须为不可变数据类型

需求:定义函数,将一个元素追加列表,函数的参数为元素和空列表。空列表使用默认值形参定义

# 错误演示
def func(arg, lst=[]):
    lst.append(arg)
	print(lst)

func(1)			# [1]
func(2)			# [1, 2]
func(3)			# [1, 2, 3]


# 修改后代码
def func(arg, lst=None):
    if lst is None:
        lst = []
    lst.append(arg)
    print(lst)

func(1)			# [1]
func(2)			# [2]
func(3)			# [3]
  • 仅关键字形参:*之后的形参必须使用关键字实参传入
def func(*, a, b):
    print(f'a={a};b={b}')

func(a=1, b=2)			# a=1;b=2
func(1, 2)				# 报错,*之后必须使用关键字实参传入
  • 不定长位置形参:可以传入0个或者多个,相当于将实参组包传入到args
def func1(*args):
    print(args)			# 这个args为实参

func1()					# ()
func1(1)				# (1,)
func1(2, 3, 5)			# (2, 3, 5)


def func2(*args):
    print(args)
    print(*args)		# 这个args为实参

func2(2, 3, 5)

'''	输出结果:
(2, 3, 5)
2 3 5
'''
  • 不定长关键字形参
def func1(**kwargs):
    print(kwargs)

func()					# {}
func(a=1)				# {'a': 1}
func(a=1, b=2)			# {'a': 1, 'b': 2}

4.3 形参的总结

  • 位置形参:def func(a, b, c)
  • 仅位置形参:/
  • 默认值形参:a=默认值
  • 仅关键字形参:*
  • 不定长位置形参:*args
  • 不定长关键字形参:**kwargs
  1. 仅位置形参与仅关键字形参之间的关系
  • 关系图
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |             |                  |
        |        Positional or keyword   |
        |                                - Keyword only
         -- Positional only
  • 代码
def func(a, / , b, *, c):
    print(f'a={a};b={b};c={c}')

func(1, 2, c=3)
func(1, b=2, c=3)
func(1, 2, 3)				# 报错
func(a=1, b=2, c=3)			# 报错
  1. 所有形参的关系
def func(a, /, b, *, *args, **kwargs): pass			# 报错。*args
def func(*args, **kwargs): pass						# 可以
def func(*args, **kwargs, a=1): pass				# 报错
  • 仅关键字形参 不能与 不定长位置形参 同时使用
  • 不定长位置形参必须在不定长关键字形参之前
  • 不定长形参必须在所有形参之后

五、递归

递归是一种编程思想。

递归的定义:在函数中调用函数本身。

递归的两个要素:

  • 自调用
  • 递归的出口
  • 原理的解释
def func(n):
    print(f'n={n};ID={id(n)}')
    if n < 4:
    	func(n+1)
    print(f'n={n};ID={id(n)}')

func(1)

递归的特性:

  • 递归函数先调用后执行
  • 所有的循环都可以使用递归来实现,但反过来不一定
  • 递归在内存使用栈桢来保存(先进后出)
  • 递归的逻辑简单,但是占用内存大
  • 案例:使用递归计算n到1的和。比如:4 + 3 + 2 + 1 = ?
'''
4 + 3 + 2 + 1			# func(4) = 4 + func(3)
3 + 2 + 1				# func(3) = 3 + func(2)
2 + 1					# func(2) = 2 + func(1)
1						# func(1)
'''


def func(n):
    if n == 1:
        return 1
    return n + func(n-1)

func(4)
  • 练习:求一个整数的阶乘。4!
def func(n):
    if n == 1:
        return 1
    return n * func(n-1)

func(4)

六、lambda

6.1 定义

定义匿名函数的关键字叫做lambda,没有函数的名的函数。

当函数只有一句代码,且只有一个返回值 ,就可以使用匿名函数来定义。

6.2 语法

lambda 参数1, 参数2, ...,参数n: 表达式

lambda的参数与普通函数的参数用法一致,可以有多个,也可以没有

def func():
    return 200

print(func)         # <function func at 0x000001FF0E551280>
print(func())		# 200


# 使用匿名来改写
f = lambda: 200
print(f)			# <function <lambda> at 0x000001FF0E551310>
print(f())			# 200

6.3 案例

使用lambda定义函数,求两个数的和

def sum_(x, y):
    return x + y

# 使用lambada来实现
f = lambda x, y: x + y
print(f(2, 3))			# 5

6.4 参数

  • 无参数
print((lambda: 200)())
  • 一个参数:传入什么就返回什么。打印”Hello,Python!“
print((lambda x: x)('Hello,Python!'))
  • 包含默认值形参:计算y=kx+b
def func(x, k=1, b=2):
    return k*x+b

print((lambda x, k=1, b=2: k*x+b)(2))				# 4
print((lambda x, k=1, b=2: k*x+b)(2, k=2, b=3))		# 7
  • 不定长位置形参
print((lambda *args: args)(1, 2, 3))				# (1, 2, 3)
print((lambda *args: args)((1, 2, 3)))				# ((1, 2, 3),)
print((lambda *args: args)(*(1, 2, 3)))				# (1, 2, 3)
  • 不定长关键字形参
print((lambda **kwargs: kwargs)(a=1, b=2))				# {'a':1, 'b':2}
print((lambda **kwargs: kwargs)(**{'a':1, 'b':2}))		# {'a':1, 'b':2}

七、高阶函数

将一个函数的引用作为实参传入到另一个函数,那么后者就是高阶函数

Python是支持函数式编程范式。

def func1(x):
    """ 将对象转换为str类型 """
    return str(x)

def func2(y):
    """ 将序列转换为列表 """
    return list(y)

def func3(z):
    """ 将序列转换为元组 """
    return tuple(z)


# 使用一个高阶函数来实现以上三个函数的功能
def func(obj, function):
    return function(obj)

print(func(11, str))				# '11'
print(func('Yes', list))			# ['Y', 'e', 's']
print(func('123', tuple))			# ('1', '2', '3')

lambda args: args)((1, 2, 3))) # (1, 2, 3)


* 不定长关键字形参

```python
print((lambda **kwargs: kwargs)(a=1, b=2))				# {'a':1, 'b':2}
print((lambda **kwargs: kwargs)(**{'a':1, 'b':2}))		# {'a':1, 'b':2}

七、高阶函数

将一个函数的引用作为实参传入到另一个函数,那么后者就是高阶函数

Python是支持函数式编程范式。

def func1(x):
    """ 将对象转换为str类型 """
    return str(x)

def func2(y):
    """ 将序列转换为列表 """
    return list(y)

def func3(z):
    """ 将序列转换为元组 """
    return tuple(z)


# 使用一个高阶函数来实现以上三个函数的功能
def func(obj, function):
    return function(obj)

print(func(11, str))				# '11'
print(func('Yes', list))			# ['Y', 'e', 's']
print(func('123', tuple))			# ('1', '2', '3')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值