在python中的方法调用中可选地设置一些类属性的便捷方法

时间:2018-07-26 14:12:51

标签: python class optional-parameters syntactic-sugar

我主要将python用作数值模拟的胶合语言。

通常,我创建一些包装器类来初始化一些合理的默认参数,然后,在较大的脚本中的其他地方,我调用run()方法,该方法可以选择覆盖某些参数并执行实际的模拟。

它可能看起来像这样:

class MyCalculationClass():

    def __init__():
        # set some defaults
        self.A = 45454
        self.B = np.zeros(16,11)
        self.C = (1.0,2.5,30.0)

    def runRaw():
        return whatever(self.A,self.B,self.C)

    def run( A=None, B=None, C=None ):
        # optionally override some defaults
        if A is not None:
            self.A = A
        if B is not None:
            self.B = B
        if C is not None:
            self.C = C
        # run the actual calculation
        return self.runRaw()

mycalc1 = MyClaculationClass()
# .... Over the Hills and Far Away
mycalc1.run(B=np.zeros(11,12) )

但是我真的讨厌样板 if A is not None: self.A = A。通常有数十个参数。

这样会更好

    def run( A=self.A, B=self.B, C=self.C ):
        # optionally override some defaults
        self.A = A
        self.B = B
        self.C = C
        # run the actual calculation
        self.runRaw()

但是:

  • 它不起作用
  • 仍然太多样板

注意:我真的希望self.A等保持存储为类属性,以便能够稍后在较大的脚本中恢复我在哪个计算中使用的参数

3 个答案:

答案 0 :(得分:3)

如果您不介意丢失检查提供的一些自动魔术文档,则可以执行以下操作:

class Foo(object):
    def __init__(self):
        self._set(A=42, B=[], C="something")

    def _set(self, **kw):
        for name, val in kw.items():
            if val is not None:
                setattr(self, name, val)


   def run(self, **kw):
       self._set(**kw)
       self.runRaw()

如果您想保持run()签名不变,则有可能,但不会那么通用(因为run()必须知道它是参数):

   def run(self, A=None, B=None, C=None):
       self._set(A=A, B=B, C=C)
       self.runRaw()

还要注意,可以通过声明__call__方法来使Python对象可调用:

class NotAFunc(object):
    def __call__(self, arg):
        print("{} called with {}".format(self, arg))


f = NotAFunc()
f(42)

答案 1 :(得分:1)

您可以使用关键字参数并通过defines.inc.php方法更新对象字典(仅在变量不是类的属性时才有效。如果是这种情况,请使用update()方法)。

setattr()

打印:

class MyCalculationClass():

    def __init__(self):
        # set some defaults
        self.A = 45454
        self.B = [0, 0, 0, 0, 0]
        self.C = (1.0,2.5,30.0)

    def runRaw(self):
        print(self.A, self.B, self.C)

    def run(self, **kwargs):
        self.__dict__.update(kwargs)
        return self.runRaw()

c = MyCalculationClass()
c.run(B=[1, 1, 1])

答案 2 :(得分:0)

这也可以通过装饰器来实现:

import functools

def set_if_none(method):
    @functools.wraps(method)
    def wrapper(self, **kwargs):  # no *args without inspect.signature
        for k, v in kwargs.items():
            if v is not None and hasattr(self, k):
                setattr(self, k, v)
        result = method(self, **kwargs)  # no *args
        return result
    return wrapper

注意:上面的示例仅适用于(使用和强制执行)关键字参数。要合并位置参数,您可能需要inspect.signature

示例:

class MyClass(object):

    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b

    def __repr__(self):
        return 'MyClass(%s)' % self.__dict__

    @set_if_none
    def meth(self, a=None, b=None):
        pass

>>> mc = MyClass()

>>> mc
MyClass({'a': 1, 'b': 2})

>>> mc.meth(a=10)

>>> mc
MyClass({'a': 10, 'b': 2})

贷记this answer,以提醒您可以通过在装饰器中将self(实例)传递到wrapper()来修改类实例。