[英]Reducing the number of a function's parameters
我的程序中目前有一些包含15個或更多參數的函數。 但是,永遠不會同時需要所有15個參數。
我有一些使用字典的switch語句函數來執行所需的相關功能。 我最極端的例子如下:
def Instrument_Type_Calcs_C(InstType_C, raw_data_LP, maxcounts_C, Measurement_Min,
Measurement_Max, Measurement_Type, SG, Tank_Shape, area, dish,
diameter, length, Strapping_Table, Poly_Data, Tank_Number,
LevelOffset):
switcher = {
'8AI': lambda: inst.FourTwenty(raw_data_LP, maxcounts_C, Measurement_Min,
Measurement_Max, Measurement_Type, SG, Tank_Shape,
area, dish, diameter, length, Strapping_Table,
Poly_Data, Tank_Number, LevelOffset),
'Magprobe': lambda: inst.SGMagprobe(raw_data_LP,
Tank_Shape, area, dish, diameter, length,
Strapping_Table, Poly_Data, Tank_Number,
LevelOffset),
'TLS': lambda: inst.TLS(raw_data_LP),
}
return switcher.get(InstType_C, lambda : None)()
現在我已經嘗試研究*args
和**kwargs
但是我不確定它們是否會幫助我。 如果使用*args
定義函數,則在調用函數時仍需要定義所有參數。 但是,在if: elif:
,我將需要三個不同的函數調用if: elif:
聲明正確數量的參數的語句。 即:
if type == '8AI':
volume = Instrument_Type_Calcs_C(InstType_C, raw_data_LP, maxcounts_C, Measurement_Min,
Measurement_Max, Measurement_Type, SG, Tank_Shape, area,
dish, diameter, length, Strapping_Table, Poly_Data,
Tank_Number, LevelOffset)
elif type == 'Magprobe':
volume = Instrument_Type_Calcs_C(InstType_C, raw_data_LP, SG, Tank_Shape, area, dish,
diameter, length, Strapping_Table, Poly_Data,
Tank_Number, LevelOffset)
elif type == 'TLS':
volume = Instrument_Type_Calcs_C(InstType_C, raw_data_LP)
顯然,這只會破壞switch語句的目的,並不能解決真正長函數調用的問題。
總體而言,它不會對我的代碼造成任何問題,但是,如果有更好的實現方法,我想學習如何。
您可以使交換函數接受除第一個參數InstType_C
所有參數, InstType_C
關鍵字參數。 這樣就可以指定您希望在每次調用中傳遞哪些值。
為了說明這一點,我將其應用於您的示例代碼。 為了使某些東西實際上是可運行的,已添加了一些代碼中未包含的東西,但僅用於測試目的。
這可能看起來好像沒有太大的改進,但這是因為為了測試定義了許多包含其自身名稱的變量。 這使對switcher函數的調用時間更長,因為每個關鍵字參數都被分配了相同名稱的變量的值,因此它們的長度是實際中的兩倍。
所有測試代碼都使這看起來很長且很復雜,但是我希望您可以忽略它,以了解目標用途的優點。
您沒有遵循PEP 8-Python代碼命名約定的樣式指南這一事實,也會使事情看起來“很忙”,因為它使語法高亮的SO不適用於所顯示的代碼。
# For testing define and create a class with methods to call.
# Each method will just print out the values of the arguments it was passed.
class Inst(object):
def FourTwenty(self, *args):
print('called FourTwenty({})\n'.format(args))
def SGMagprobe(self, *args):
print('called SGMagprobe({})\n'.format(args))
def TLS(self, *args):
print('called TLS({})\n'.format(args))
inst = Inst() # create instance for testing
# For testing create variables to pass as arguments, each variable will contain
# the variable's name as its value. i.e. some_variable = 'some_variable'.
arguments = [arg.strip() for arg in (
"raw_data_LP, maxcounts_C, Measurement_Min, Measurement_Max, "
"Measurement_Type, SG, Tank_Shape, area, dish, diameter, length, "
"Strapping_Table, Poly_Data, Tank_Number, LevelOffset").split(',')]
for arg in arguments:
globals()[arg] = arg
#### End of testing scaffold.
class AttrDict(dict):
"""Allows use of dot notation to access dictionary's contents."""
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
def Instrument_Type_Calcs_C(InstType_C, **kwargs):
kwargs = AttrDict(kwargs) # make them easier to access
switcher = {
'8AI': lambda: inst.FourTwenty(kwargs.raw_data_LP, kwargs.maxcounts_C,
kwargs.Measurement_Min,
kwargs.Measurement_Max, kwargs.Measurement_Type,
kwargs.SG, kwargs.Tank_Shape, kwargs.area, kwargs.dish,
kwargs.diameter, kwargs.length, kwargs.Strapping_Table,
kwargs.Poly_Data, kwargs.Tank_Number, kwargs.LevelOffset),
'Magprobe': lambda: inst.SGMagprobe(kwargs.raw_data_LP, kwargs.Tank_Shape, kwargs.area,
kwargs.dish, kwargs.diameter, kwargs.length,
kwargs.Strapping_Table, kwargs.Poly_Data,
kwargs.Tank_Number, kwargs.LevelOffset),
'TLS': lambda: inst.TLS(kwargs.raw_data_LP),
}
return switcher.get(InstType_C, lambda: None)()
volume = Instrument_Type_Calcs_C('8AI', raw_data_LP=raw_data_LP,
maxcounts_C=maxcounts_C, Measurement_Min=Measurement_Min,
Measurement_Max=Measurement_Max,
Measurement_Type=Measurement_Type, SG=SG,
Tank_Shape=Tank_Shape, area=area, dish=dish,
diameter=diameter, length=length,
Strapping_Table=Strapping_Table, Poly_Data=Poly_Data,
Tank_Number=Tank_Number, LevelOffset=LevelOffset)
volume = Instrument_Type_Calcs_C('Magprobe', raw_data_LP=raw_data_LP, SG=SG,
Tank_Shape=Tank_Shape, area=area, dish=dish,
diameter=diameter, length=length,
Strapping_Table=Strapping_Table, Poly_Data=Poly_Data,
Tank_Number=Tank_Number, LevelOffset=LevelOffset)
volume = Instrument_Type_Calcs_C('TLS', raw_data_LP=raw_data_LP)
您可以使用默認參數和命名參數來解決您的問題。
但是,如果您需要帶有這么多參數的函數,我想您可能可以使程序更加面向對象。
創建一個類,該類的實例具有__call__
運算符(或只是一個方法),該運算符具有您最常用的參數。
並提供其他方法來控制通常不會更改的設置。
請使用默認參數(如注釋中所指出,即def func(a, optional=None, optional2=None ...):
或也可以執行以下操作:
def Instrument_Type_Calcs_C(InstType_C, **kwargs):
# You can also simply added a proper list of strings, I was merely to lazy to write on up
allowed_kwargs = ('raw_data_LP, maxcounts_C, Measurement_Min,
Measurement_Max, Measurement_Type, SG, Tank_Shape, area,
dish, diameter, length, Strapping_Table, Poly_Data,
Tank_Number, LevelOffset').strip(' ').split(',')
wrong_kwargs = [k for in kwargs if k not in allowed_kwargs]
if wrong_kwargs:
raise ValueError("Invalid Kwarg passed! %s" wrong_kwargs)
# rest of your code
...
kwargs
是包裝在dict中的所有命名參數的字典,因此您可以在函數中訪問它,就像訪問字典一樣。
但正如已經指出的那樣,你或許應該考慮oop
荷蘭國際集團代碼。 上面的解決方案只是清理函數頭,而不是清理函數的可讀性,也沒有達到更好的簡單性。
順便說一句,您還可以在函數中使用加星號的表達式( **
和*
)-它們本質上是對列表( *
)或字典( **
)進行解壓縮。 例如,在列表理解或解壓縮到其他變量時,這很有用:
l = ['hello', 1, 2]
s, *nums = *l
s # 'hello'
nums # [1, 2]
您可以使用像參數這樣的字典“ parameter”,在其中放置所需的所有參數。 像這樣:
def FoobarFunction(parameters):
if parameters["KnownParameter"] == ... :
# Do Something
...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.