『Python』函数的参数及其作用域

本文深入解析Python函数的六种参数类型:位置参数、默认参数、不定长参数、关键字参数、命名关键字参数及参数组合,同时介绍了参数传递机制与变量作用域。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 参数类型

1.1 位置参数

显然对位置和顺序有要求,形参和实参必须一一对应,不可或缺

def show(msg):
    print(msg)

s = "I love you!"
print(s)  # I love you!

def sum(a,b):
    return a+b
    
print(sum(3,4))  # 7

1.2 默认参数

def power(x, n=2):
	s = 1
	while n > 0:
		n = n - 1
		s = s * x
	return s

power(5)  # 25
  • 在使用缺省参数后,对于函数的必填参数必须在前,默认参数在后
  • 默认参数在一个函数中可以有多个
  • 当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变
    化小的参数就可以作为默认参数
  • 函数调用时,优先传入位置参数,然后剩下的默认参数允许不按照定义顺序,但是必须指明传入的形参的标识符,比如函数resign(name, gender, age = 6, city= 'Beijing'),可以这样调用:resign('yee','male',city='Nanjing',age=8)
  • 默认参数慎用可变对象,因为每次调用对默认参数值的修改都会得到保
    存,而不是新创建对象。Default values are computed once, then re-used.
# # # # # # # # # # # # # #
# 默认参数为可变对象的后果  #
# # # # # # # # # # # # # #
def func(n,l=[]):
    l.append(n)
    return l

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

1.3 不定长参数

在Python 函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1 个、2 个到任意个,还可以是0 个

def sum(*num):
	total = 0
	for n in num:
		total += n
	return total

sum(1)  # 1
sum(2,3,4,5,6)  # 20
sum()  # 0

在定义函数时,在参数名前加“*”就表示该参数是不定长参数,加了“*”的参数会以元组(tuple)的形式导入。

def f(*para):
	print(para)
f(1,2,3,4)  # (1, 2, 3, 4)
f()  # ()

如果已经有一个list 或者tuple,要调用一个可变参数怎么办?可以这样做:

num = [2,4,6,7,9]
sum(num[0],num[1],num[2],num[3],num[4])  # 28

# 只要传入时实参前加上“*”
sum(*num)  # 28

*num 表示把num 这个list 的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。

1.4 关键字参数

之前说过,可变参数允许你传入0 个或任意个参数,这些可变参数在函数调用时自动组装
为一个tuple;而关键字参数允许你传入0 个或任意个含参数名的参数,这些关键字
参数在函数内部自动组装为一个dict。

def person(name, age, **kw):
	print('name:', name, 'age:', age, 'other:', kw)

person('yee',26,city='Nanjing')  # name: yee age: 26 other: {'city': 'Nanjing'}

person('yee',26,city='Nanjing',job='student')  # name: yee age: 26 other: {'city': 'Nanjing', 'job': 'student'}

关键字参数有什么用?它可以扩展函数的功能。比如,在person 函数里,我们保证能接收到 name 和 age 这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

注意一点,传入的key 自动变成str 了

和可变参数类似,也可以先组装出一个dict,然后,把该dict 转换为关键字参数传进去

extra = {'city': 'Beijing', 'job': 'Engineer'}

person('Jack', 24, city=extra['city'], job=extra['job'])  # name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

person('Jack', 24, **extra)  # name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

**extra 表示把 extra 这个 dict 的所有 key-value 用关键字参数传入到函数的**kw 参数,kw 将获得一个dict,注意kw 获得的dict 是extra 的一份拷贝,对 kw 的改动不会影响到函数外的 extra此处注意,如果是普通参数传的是字典这种可变对象,则形参和实参可是共用一个对象的,函数内对形参更改的话会影响外部实参。

1.5 命名关键字参数

对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw 检查,但是调用者仍可以传入不受限制的关键字参数。

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job 作为关键字参数。这种方式定义的函数如下:

def person(name, age, *, city, job):
	print(name,age,city,job)

和关键字参数**kw 不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数,如果不加,将被视为位置参数。调用方式如下:

person('Jack', 24, city='Beijing', job='Engineer')  # Jack 24 Beijing Engineer

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符“*”了

def person(name, age, *args, city, job):
	print(name, age, args, city, job)

命名关键字参数必须传入参数名,如 person 函数要传入city=xxx,这和位置参数不同,如果没有传入参数名,调用将报错。

命名关键字参数可以有缺省值,从而简化调用:

def person(name, age, *, city='Beijing', job):
	print(name, age, city, job)
	
person('Jack', 24, job='Engineer')  # Jack 24 Beijing Engineer

1.6 参数组合

在Python 中定义函数,这5 种参数都可以组合使用,但是请注意,参数定义的
顺序必须是:

  1. 位置参数
  2. 带默认值位置参数
  3. 可变参数
  4. 命名关键字参数
  5. 带默认值的命名关键字参数
  6. 关键字参数
def f1(a, b, c=0, *args, **kw):
	print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
	print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

最神奇的是,通过一个 tuple 和 dict,你也可以调用上述函数:

args = (1, 2, 3, 4)
kw = {'d': 99, 'x': '#'}
f1(*args, **kw)  # a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}

args = (1, 2, 3)
kw = {'d': 88, 'x': '#'}
f2(*args, **kw)  # a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

所以,对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

2. 参数传递

一般而言,可变对象是by cite不可变对象是by value,有一个例外是关键字参数,字典本来是可变对象,但是此处仍然 by value

3. 变量作用域

Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量
是在哪里赋值的。

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用
域一共有4 种,分别是:

  • L(Local) 局部作用域
  • E(Enclosing) 嵌套作用域
  • G(Global) 全局作用域
  • B(Built-in) 内置作用域(内置函数所在模块的范围)

以L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。

g_count = 0 # 全局作用域
def outer():
	o_count = 1 # 作用域在outer 中
	def inner():
		i_count = 2 # 作用域在inner 中

Python 中只有模块(module)类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if/elif/else/、try/except、for/while 等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问。

还有一种情况,当全局作用域和局部作用域变量名相同时:

total = 0
def sum(m, n):
	total = m + n
	return total
	
sum(10,20)  # 30
print(total)  # 0
  • global 关键字
    如果在局部作用域中只是使用全局变量,而不做修改,则可以直接调用,但是,如果需要对全局变量进行修改的话,必须在修改前,用global 关键字声明,注意只是声明,不能初始化,如:global arg(√)global arg = 1(×)
  • nonlocal 关键字
    nonlocal 关键字用来在函数或其他作用域中使用外层(非全局)变量。简单说吧,这是针对嵌套作用域情况,假设作用域层次从外到内编号为 1 , 2 , ⋯ 1,2,\cdots 1,2 层次 i i i 中变量使用nonlocal 关键字声明变量时,这个变量与层次 i − 1 i-1 i1 中的同名变量挂钩,与层次为 i i i 的其他作用域中的同名变量无关。

还有种特殊情况:

a=10
def test():
	a = a + 1  # 调用函数时会报错,变量未定义
	print(a)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值