【问题标题】:Python: How To copy function parameters into object's fields effortlessly?Python:如何轻松地将函数参数复制到对象的字段中?
【发布时间】:2010-05-26 11:54:15
【问题描述】:

很多时候我都有将参数复制到对象字段中的成员函数。例如:

class NouveauRiches(object):
  def __init__(self, car, mansion, jet, bling):
    self.car = car
    self.mansion = mansion
    self.jet = jet
    self.bling = bling

有没有一种 Python 语言结构可以让上面的代码变得不那么乏味? 可以使用 *args:

def __init__(self, *args):
  self.car, self.mansion, self.jet, self.bling = args

+:不那么乏味

-: 函数签名不够暴露。需要深入了解函数代码才能知道如何使用函数

-: 在调用错误# 参数时不会引发TypeError(但会引发ValueError

还有其他想法吗? (无论您有什么建议,请确保调用该函数的代码保持简单)

【问题讨论】:

  • 我个人并不觉得第一个乏味。事实上,我更喜欢这种类型的代码,因为它更清晰。你几乎可以是一个假人,仍然能够准确地理解那里发生了什么。但也许这是“显式胜于隐式”的极端观点。虽然我确实同意有一个更漂亮但仍然明确的参数分配方式会很好。也许你应该写一个 PEP!
  • @bandana: 函数签名不应该揭示如何使用函数,你为什么不为你的函数提供一个__doc__ 字符串?如果有问题,您可以自己提出TypeError。您还可以使用关键字参数,更明确
  • @SilentGhost:这是重复的。这是一个重复的好吧。我不知道如何结束我自己的问题
  • @bandana:你看到标签下的close链接了吗?

标签: python parameters


【解决方案1】:

您可以使用辅助方法来做到这一点,如下所示:

import inspect

def setargs(func):
    f = inspect.currentframe(1)
    argspec = inspect.getargspec(func)
    for arg in argspec.args:
        setattr(f.f_locals["self"], arg, f.f_locals[arg])

用法:

class Foo(object):

    def __init__(self, bar, baz=4711):
        setargs(self.__init__)

        print self.bar # Now defined
        print self.baz # Now defined

这不是很漂亮,它应该只在原型设计时使用。如果您打算让其他人阅读,请使用显式分配。

它可能需要改进,不需要将函数作为参数,但这需要更丑陋的黑客和诡计:)

【讨论】:

  • 似乎不必要地令人费解,并且还设置了 self.self。我看到的 self.__dict__.update(locals()) 的唯一优势是,如果 Python 将 __foo__ 添加到输出中,它可能不太可能中断。另一方面,它“依赖于解释器中的 Python 堆栈帧支持,这并不保证在所有 Python 实现中都存在”,并在堆栈帧之间进行引用循环,这在文档中被警告。
【解决方案2】:

我会这样做,你也可以覆盖已经定义的属性。

class D:
  def __init__(self, **kwargs):
    self.__dict__.update(kwargs)

但我个人会走很长的路。 想想那些:

- Explicit is better than implicit.
- Flat is better than nested.
(The Zen of Python)

【讨论】:

  • 我不喜欢这个,因为它不适用于属性或其他描述符——self.__dict__.update() 跳过了您只能通过 setattr() 获得的对象的所有这些方面。
【解决方案3】:

试试类似的东西

d = dict(locals())
del d['self']
self.__dict__.update(d)

当然,它返回所有局部变量,而不仅仅是函数参数。

【讨论】:

    【解决方案4】:

    我不确定这是个好主意,但可以做到:

    import inspect
    class NouveauRiches(object):
        def __init__(self, car, mansion, jet, bling):
            arguments = inspect.getargvalues(frame)[0]
            values = inspect.getargvalues(frame)[3];
            for name in arguments:
                self.__dict__[name] = values[name]
    

    虽然我想你可以把它放在一个可重复使用的实用方法中。

    【讨论】:

    • 美胜于丑。
    • @evilpie 同意,这就是为什么我在我的建议前加上不推荐这种方法。
    • 那不是反对你,我只是喜欢引用禅宗。
    【解决方案5】:

    你可以试试这样的:

    class C(object):
        def __init__(self, **kwargs):
            for k in kwargs:
                d = {k: kwargs[k]}
                self.__dict__.update(d)
    

    或者使用 setattr 你可以这样做:

    class D(object):
        def __init__(self, **kwargs):
            for k in kwargs:
                setattr(self, k, kwargs[k])
    

    两者都可以这样调用:

    myclass = C(test=1, test2=2)
    

    所以你必须使用 **kwargs,而不是 *args。

    【讨论】:

    • 那么,调用函数的代码稍微复杂一些
    • @evilpie,我对 python 还很陌生,所以根据你的建议更新了答案 - 谢谢!
    【解决方案6】:

    我有时会为行为“类似”的类这样做,也就是说,它们有一堆可自定义的属性:

    class SuperClass(object):
        def __init__(self, **kw):
            for name, value in kw.iteritems():
                if not hasattr(self, name):
                    raise TypeError('Unexpected argument: %s' % name)
                setattr(self, name, value)
    
    class SubClass(SuperClass):
        instance_var = None # default value
    
    class SubClass2(SubClass):
        other_instance_var = True
    
        @property
        def something_dynamic(self):
            return self._internal_var
    
        @something_dynamic.setter # new Python 2.6 feature of properties
        def something_dynamic(self, value):
            assert value is None or isinstance(value, str)
            self._internal_var = value
    

    然后你可以调用SubClass2(instance_var=[], other_instance_var=False),它会在没有定义__init__的情况下工作。您也可以使用任何属性。虽然这允许您覆盖您可能不希望的方法(因为它们为 hasattr() 返回 True,就像实例变量一样)。

    如果您添加任何property 或其他描述符,它将正常工作。您可以使用它来进行类型检查;与__init__ 中的类型检查不同,它会在任何时候更新该值时应用。请注意,除非您覆盖__init__,否则您不能对这些使用任何 位置参数,因此有时自然位置参数将不起作用。 formencode.declarative 涵盖了这个问题和其他问题,可能很彻底,我不建议您尝试(回想起来,我认为这不值得)。

    请注意,任何使用 self.__dict__ 的配方都不会尊重属性和描述符,如果您将它们一起使用,您只会得到奇怪和意想不到的结果。我推荐使用setattr()来设置属性,从不self.__dict__

    此外,这个秘籍并没有给出非常有用的签名,而一些进行框架和函数自省的签名却可以。使用 some work 可以动态生成 __doc__ 以澄清论点...但我再次不确定是否值得添加更多活动部件。

    【讨论】:

      【解决方案7】:

      我是以下的粉丝

      import inspect
      
      def args_to_attrs(otherself):
          frame = inspect.currentframe(1)
          argvalues = inspect.getargvalues(frame)
      
          for arg in argvalues.args:
              if arg == 'self':
                  continue
              value = argvalues.locals[arg]
              setattr(otherself, arg, value)
      
      class MyClass:
          def __init__(self, arga="baf", argb="lek", argc=None):
              args_to_attrs(self)
      

      __init__ 的参数是显式命名的,因此很清楚正在设置哪些属性。此外,它比当前接受的答案稍微简化了一些。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-12-06
        • 2015-07-20
        • 1970-01-01
        • 2015-09-13
        • 2014-07-08
        • 2017-10-22
        • 2015-06-16
        • 2012-08-20
        相关资源
        最近更新 更多