第 18 條:用數(shù)量可變的位置參數(shù)減少視覺雜訊
先看這樣一個函數(shù):
def log(message, values):
if not values:
print(message)
else:
values_str = ''.join(str(x) for x in values)
print('%s: %s' % (message, values_str))
log('My number are', [1, 2])
log('No number', [])
>>>
My number are: 12
No number
這個函數(shù)接受一個字符串和一個列表闽巩,用于打印丰包,只是如果列表沒有值時爆价,就像第二個傳值垦巴,仍然要去傳一個空列表媳搪。這是因為 log
函數(shù)的參數(shù)是固定的,只能按照固定的參數(shù)來傳值骤宣。
在使用的時候秦爆,當(dāng)然希望如果第二個參數(shù)是空值,最好不需要傳空列表進去憔披,函數(shù)也能正常運行等限。在 Python 中,如果把后面的位置參數(shù)加一個 * 芬膝,就可以實現(xiàn)望门。
def log(message, *values): # 這里加了個 *
if not values:
print(message)
else:
values_str = ''.join(str(x) for x in values)
print('%s: %s' % (message, values_str))
log('My number are', [1, 2])
log('No number') # 這時不需要再傳入空列表
>>>
My number are: 12
No number
在使用帶有變長參數(shù)的函數(shù)時,總是先轉(zhuǎn)換成元組锰霜。所以怒允,變長參數(shù)對應(yīng)的參數(shù)個數(shù)應(yīng)該足夠少,否則就會消耗大量內(nèi)存锈遥,尤其是當(dāng)傳入的可變參數(shù)是帶有 *
號的生成器。還有一個問題就是在可變參數(shù)前添加新的參數(shù)時勘畔,函數(shù)就必須要修改所灸,否則就是發(fā)生混亂。
第 19 條:用關(guān)鍵字參數(shù)來表達(dá)可選的行為
Python 函數(shù)在傳參時炫七,可以使用關(guān)鍵字來指定參數(shù)進行傳值爬立。
def remainer(number, divisor):
return number % divisor
assert remainer(20, 7) == 6
remainer(20, divisor=7)
remainer(number=20, divisor=7)
remainer(divisor=7, number=20)
直接傳值和指定關(guān)鍵字來傳值都是可以的。但是不能把位置參數(shù)放在關(guān)鍵字參數(shù)的前面万哪,
remainer(number=20, 7)
>>>
SyntaxError: positional argument follows keyword argument
每個參數(shù)只能指定一次侠驯,
remainer(20, number=7)
>>>
TypeError: remainer() got multiple values for argument 'number'
第 20 條:動態(tài)默認(rèn)值參數(shù)
有時候如果一個函數(shù)的參數(shù)需要動態(tài)的默認(rèn)值,那么就想當(dāng)然會在參數(shù)中指定一個默認(rèn)值奕巍,
def log(message, when=datetime.now()):
print('%s: %s' % (message, when))
log('first time')
sleep(1)
log('second time')
>>>
first time: 2018-05-21 07:54:09.361192
second time: 2018-05-21 07:54:09.361192
可以看到吟策,運行結(jié)果和我們認(rèn)為的有出入,時間并沒有相差一秒的止,而是完全相同檩坚!
這里涉及到 Python 代碼的執(zhí)行, 在 Python 中诅福,參數(shù)的默認(rèn)值是在每個模塊加載的時候就求出的匾委,而模塊又是在程序啟動時加載的,所以也就是說參數(shù)的默認(rèn)值是在模塊加載時就已經(jīng)固定不變了氓润。
def log(message, when=None):
when=datetime.now() if when is None else when
print('%s: %s' % (message, when))
log('first time')
sleep(1)
log('second time')
>>>
first time: 2018-05-21 08:00:44.378427
second time: 2018-05-21 08:00:45.378766
如上赂乐, Python 中如果想要實現(xiàn)參數(shù)的動態(tài)默認(rèn)值,只能在函數(shù)中實現(xiàn)咖气“ご耄可以把 參數(shù)默認(rèn)為 None挖滤,然后在函數(shù)中根據(jù)參數(shù)的值來判斷是否為 None,再進行求值运嗜。而如果參數(shù)是可變類型壶辜,如列表,字典等担租,一定要使用 None 傳入砸民,否則使用的一直是同一個可變類型對象。
第 21 條:使用只能以關(guān)鍵字形式指定的參數(shù)確保代碼清晰
Python3 中奋救,可以定義一種只能以關(guān)鍵字形式來指定的參數(shù)岭参,這些參數(shù)必須以關(guān)鍵字來提供,而不能按位置提供尝艘。
def safe_division(number, divisor, *, ignore_overflow=False, ignore_zero_division=False):
try:
return number / divisor
except OverflowError:
if ignore_overflow:
return 0
else:
raise
except ZeroDivisionError:
if ignore_zero_division:
return 0
else:
raise
safe_division(5, 3, True, False)
>>>
TypeError: safe_division() takes 2 positional arguments but 4 were given
在上面的例子中演侯,在參數(shù)列表中使用*
號表示位置參數(shù)已經(jīng)結(jié)束,后續(xù)參數(shù)只能以關(guān)鍵字的方式來指定背亥。如果沒有去指定關(guān)鍵字參數(shù)的話秒际,使用位置參數(shù)就會報錯。
print(safe_division(1, 10*500, ignore_overflow=True, ignore_zero_division=False))
>>>
0.0
使用指定關(guān)鍵字參數(shù)后正常運行狡汉。
在 Python2 中娄徊,沒有這個方法,但是可以使用另一種方式來實現(xiàn)只能使用指定關(guān)鍵字參數(shù)盾戴。
def safe_division_d(number, divisor, **kwargs):
ignore_overflow = kwargs.pop('ignore_overflow', False)
ignore_zero_division = kwargs.pop('ignore_zero_division', False)
if kwargs:
raise TypeError('Unexcepted **kwargs')
try:
return number / divisor
except OverflowError:
if ignore_overflow:
return 0
else:
raise
except ZeroDivisionError:
if ignore_zero_division:
return 0
else:
raise
print(safe_division_d(1, 10**500000, True, False))
print(safe_division_d(1, 10**500000, ignore_overflow=True, ignore_zero_division=False))
print(safe_division_d(1, 10**500000, unexpected=True))
>>>
TypeError: safe_division_d() takes 2 positional arguments but 4 were given
0.0
TypeError: Unexcepted **kwargs
使用**kwargs
這個參數(shù)寄锐,可以很方便的判斷傳參有沒有以指定的方式,并且指定的參數(shù)是不是正確尖啡。