簡體   English   中英

減少函數參數的數量

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM