【问题标题】:Python - Iterating over Arguments passed to a FunctionPython - 迭代传递给函数的参数
【发布时间】:2017-01-17 19:24:21
【问题描述】:

假设我有以下示例:

class foo:
   ...
   def bar(self, w, x, y, z, ...):
      self.w = w
      self.x = x
      self.y = y
      self.z = z
      ...

我希望将bar() 中的 n 个属性分配行减少为使用setattr() 循环通过参数设置的一个分配行。为此目的,是否有一种好方法可以循环遍历所述参数?

我希望保留定义的参数名称,以限制传递给函数的参数数量以及传递它们的顺序。我也明白functions can be handled like objects;那么是否可以获取已定义参数的列表作为函数的属性并对其进行迭代?

【问题讨论】:

标签: python arguments variable-assignment setattr


【解决方案1】:

__setattr__ 属性一次只分配一个属性,如果你想分配多个属性,你可以在你的函数头中使用**kwargs 并且为了限制参数的数量你可以简单地检查@987654324 的长度@ 在你的函数中。并为每个参数一一调用__setattr__。这个秘籍的一个很好的理由是,由于很多原因,基本上不考虑任何事情的情况下将属性分配给对象并不是一项正确和理想的工作。因此,您必须考虑所有必需的条件,一次为每个属性分配一个。

您也可以通过更新实例字典手动执行此操作,但您也应该处理异常。

In [80]: class foo:                      
    def bar(self, **kwargs):
        if len(kwargs) != 4:
            raise Exception("Please enter 4 keyword argument")
        for k, v in kwargs.items():
            foo.__setattr__(self, k, v)
   ....:             

In [81]: f = foo()

In [82]: f.bar(w=1, x=2, y=3, z=4)

In [83]: f.w
Out[83]: 1

In [84]: f.bar(w=1, x=2, y=3, z=4, r=5)
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-84-758f669d08e0> in <module>()
----> 1 f.bar(w=1, x=2, y=3, z=4, r=5)

<ipython-input-80-9e46a6a78787> in bar(self, **kwargs)
      2     def bar(self, **kwargs):
      3         if len(kwargs) != 4:
----> 4             raise Exception("Please enter 4 keyword argument")
      5         for k, v in kwargs.items():
      6             foo.__setattr__(self, k, v)

Exception: Please enter 4 keyword argument

通过使用__setatter__,它会自动处理异常:

In [70]: f.bar(1, 2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-70-07d1f3c9e27f> in <module>()
----> 1 f.bar(1, 2)

<ipython-input-65-1049e26120c1> in bar(self, *args)
      2     def bar(self, *args):
      3         for item in args:
----> 4             foo.__setattr__(self, item, item)
      5 

TypeError: attribute name must be string, not 'int'

【讨论】:

  • 这不会按要求强制执行特定的参数名称和数量。
  • @DavidHeyman 我没有看到这样的问题。你能详细说明一下吗?
  • 最后一段的第一行,我写这个:“我希望保留定义的参数名称,以限制传递给函数的参数数量以及它们传递的顺序”
  • 使用**kwargs 进行操作会保留名称,但仍不强制执行数量,并且无法执行订单。
  • @Kasramvd “我希望保留定义的参数名称,以限制传递给函数的参数数量以及传递它们的顺序。”所以,如果我使用 *args,那么函数可以接受任意数量的参数并将它们分配为属性。我希望能够严格执行一定数量的属性(在本例中为 4 个属性)分配给self。我希望它们被提供的名称标记。我想 **kwargs 可能会解决标签问题,但不能解决数量问题。
【解决方案2】:

使用locals(),您可以获得所有参数(以及任何其他局部变量):

class foo:
    def bar(self, w, x, y, z):
        argdict = {arg: locals()[arg] for arg in ('w', 'x', 'y', 'z')}
        for key, value in argdict.iteritems():
            setattr(self, key, value)
        ...

也许可以更有效地做到这一点,如果你更喜欢更少的行而不是可读性,或者发现它更具可读性,你可以内联 argdict。

【讨论】:

  • 所以locals(),在这种情况下会返回传递给函数bar() 的参数的字典,其中的值键为名称? locals() 是否只在函数bar() 的范围内运行?
  • 是的。更具体地说,locals() 返回所有局部变量 - 如果您在调用 locals() 之前插入行 q = 73,则 dict 将包含该对。字典确实包含self,这就是为什么我将它复制到新字典而不是直接使用它的原因 - 它允许仅限制预期的变量,即使您以后需要将其向下移动,也意味着您不要尝试设置self.self = self,这充其量是没有帮助的。
  • 好的。因此,如果我在函数bar() 的最开始调用argdict = locals(),它只会包含作为参数传递给 bar 的元素?这就是我要找的。​​span>
  • 差不多。如果bar() 是一种方法而不是函数(如您的示例中所示),它仍将包括self,尽管-self 仍然是一个参数,而不是您明确传递的参数。您可以通过argdict = dict(locals()); del argdict['self'] 更有效地减少它。如果您想从argdict 中删除某些内容而不实际完全删除它,则不能直接执行argdict = locals()
  • 在函数中硬编码参数名称不是正确的方法。
【解决方案3】:

因此您不必实际命名参数显式使用:

class foo:
    def __init__(self, w, x, y, z):
        args = locals()# gets a dictionary of all local parameters
        for argName in args:
            if argName!='self':
                setattr(self, argName, args[argName])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-23
    • 1970-01-01
    • 2013-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多