『Python』函數的參數及其作用域

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,21,2,\cdots 層次 ii 中變量使用nonlocal 關鍵字聲明變量時,這個變量與層次 i1i-1 中的同名變量掛鉤,與層次爲 ii 的其他作用域中的同名變量無關。

還有種特殊情況:

a=10
def test():
	a = a + 1  # 調用函數時會報錯,變量未定義
	print(a)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章