【问题标题】:creating a partial-like object with dynamic arguments创建具有动态参数的类部分对象
【发布时间】:2018-09-19 13:18:50
【问题描述】:

我正在尝试创建一个偏函数,但具有存储为类属性并相应更改的动态参数。类似于以下代码:

from functools import partial

def foo(*args, msg):
    print(msg)

class Bar:
    def __init__(self, msg):
        self.msg = msg
        self.functions = dict()
        self.functions['foo'] = partial(foo, msg=self.msg)

    def foo_method(self, *args):
        return self.functions['foo'](*args)

b =Bar('1')
b.foo_method()
b.msg = '2'
b.foo_method()

当然,两个语句都会打印“1”,因为部分对象修复了参数。我发现的唯一选择是将属性更改为属性并使用 setter 手动更改部分属性:

class Bar:
    def __init__(self, msg):
        self._msg = None
        self.functions = dict()
        self.functions['foo'] = partial(foo)
        self.msg = msg

    def foo_method(self, *args):
        return self.functions['foo'](*args)

    @property
    def msg(self):
        return self._msg

    @msg.setter
    def msg(self, msg):
        self._msg = msg
        self.functions['foo'].keywords['msg'] = msg

我想知道是否有更“pythonic”/有效的方法来做到这一点,因为除了这个解决方法之外我真的不需要使用属性。

【问题讨论】:

    标签: python python-3.x


    【解决方案1】:

    仅存储对传递函数的引用并在现场构造调用有什么问题?即:

    class Bar:
    
        def __init__(self, msg):
            self.msg = msg
            self.foo = foo  # a reference to foo, not needed here but used as an example
    
        def foo_method(self, *args):
            return self.foo(*args, msg=self.msg)  # or just: foo(*args, msg=self.msg)
    

    【讨论】:

    • 我没有在原始问题中指定它,但问题是 foo_method 是一个超类方法。这个想法是保持类之间的方法签名(即函数['foo'] 应该始终采用 lambda *args: result 的形式),因此任何调整都应该在 init 中进行
    • @Mstaino - 这听起来不是一个好主意,我建议在进一步复杂化之前重新考虑您的设计。话虽如此,如果有正当理由这样做,请使用 lambdas,如 blhsing's answer
    【解决方案2】:

    您可以使用lambda 而不是partial 对参数进行延迟(或通常称为“惰性”)评估,以便在调用函数之前不会评估self.msg

    class Bar:
        def __init__(self, msg):
            self.msg = msg
            self.functions = dict()
            self.functions['foo'] = lambda *args: foo(*args, msg=self.msg)
    
        def foo_method(self, *args):
            return self.functions['foo'](*args)
    

    【讨论】:

      【解决方案3】:

      我能想到的最简单的方法就是构建一个 dict 并将其双星传递给要解包的函数。

      类似:

      def some_func(msg, some_arg=None):
          print("Hello world") # ignore the msg for now
      
      call_args = {}
      call_args['some_arg'] = 2 # single field
      call_args.update({'msg': 1, 'stuff': [2,3,4]}) # multiple at once
      
      some_func(**call_args)
      

      现在, some_func 将抛出 TypeError ,因为我们传递的参数比函数需要的多。您可以通过让函数在签名中接受 **kwargs、修剪您不期望的参数或其他方法来解决此问题。

      现在,继续上一个会话:

      call_args = {'msg': 'abc'} # let's get rid of those extra args
      some_func(**call_args) # => prints 'Hello world'
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-25
        • 1970-01-01
        • 2011-09-29
        • 2020-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多