减少函数参数的数量

时间:2017-02-01 13:05:13

标签: python python-3.x

我目前在程序中有一些包含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 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语句的要点,并且无法解决真正长函数调用的问题。

总的来说,它不会对我的代码造成任何问题,但如果有更好的实现方法,我想了解如何。

4 个答案:

答案 0 :(得分:1)

您可以使切换函数接受除第一个参数InstType_C之外的所有参数,# 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) ,关键字参数。这样就可以指定每次调用时要传递的值。

为了说明这一点,我已将其应用于您的示例代码。为了制作实际上可以运行的东西,已添加了许多代码中遗漏的东西,但仅用于测试目的。

这可能使它看起来并没有太大的改进,但那是因为测试时定义了一堆包含自己名字的变量。这使得对切换器功能的调用时间更长,因为每个关键字参数都被赋予了同名变量的值,因此它们的长度是实际操作中的两倍。

所有测试代码都使这看起来很长很复杂,但我希望你可以忽略它,以便了解目标的优点。

您没有遵循PEP 8 - Style Guide for Python Code命名约定这一事实也会使事情变得“繁忙”,因为它会使语法高亮显示SO适用于所显示的代码。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public struct TData
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
    public protocol string;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst =1)]
    public messageType string;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public measurement 
...
    public int getProtocol(){return Convert.ToInt32(protocol);}
...
}

public string get(){
   var strSource="03EMSTR...";
    IntPtr pbuf = Marshal.StringToBSTR(buf);
    TData data= (TData)Marshal.PtrToStructure(pbuf,typeof(TData))
}

答案 1 :(得分:0)

您可以使用默认参数和命名参数来解决问题。

但如果您需要具有这么多参数的函数,我会说您可能会使您的程序更加面向对象。

创建一个类,其实例包含__call__运算符(或只是一个方法),其中包含您最常使用的参数。

并为其提供其他方法来控制不经常更改的设置。

答案 2 :(得分:0)

使用默认参数(正如评论中指出的那样,即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]

答案 3 :(得分:-1)

您可以使用字典“参数”之类的参数,其中包含您需要的所有参数。像这样:

def FoobarFunction(parameters):
    if parameters["KnownParameter"] == ... :
        # Do Something
        ...