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 种参数都可以组合使用,但是请注意,参数定义的
顺序必须是:
- 位置参数
- 带默认值位置参数
- 可变参数
- 命名关键字参数
- 带默认值的命名关键字参数
- 关键字参数
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 i−1 中的同名变量挂钩,与层次为 i i i 的其他作用域中的同名变量无关。
还有种特殊情况:
a=10
def test():
a = a + 1 # 调用函数时会报错,变量未定义
print(a)