【问题标题】:Capturing one of many args in a python __init__在 python __init__ 中捕获许多 args 之一
【发布时间】:2014-01-07 15:09:31
【问题描述】:

如果我有这样的课程:

class foo(object):
    def __init__(self, a, b=None, c=None, d=None):
    print a, b, c, d

还有一个像这样的派生类:

class bar(foo):
    def __init__(self, *args, **kwargs):
        if "c" in kwargs:
            kwargs['c'] = 'else'  # CHANGE C IFF IT IS PRESENT
        super(bar, self).__init__(*args, **kwargs)

当有人调用这个构造函数时,他们可以这样做:

bar('a','b','c','d')

或者他们可以这样称呼它:

bar('a', c='something')

在第二种情况下,我的构造函数按计划工作,但在第一次调用的情况下,c 在 args 数组中潜行。看起来我必须注意 args 数组和 kwargs 的长度,这似乎脆弱到无法使用的地步。除了在 bar 中枚举来自 foo 的参数之外,您还能做些什么来改善这种情况? (这种做法本身有些脆弱,但更容易识别)。

【问题讨论】:

    标签: python


    【解决方案1】:

    args 填充kwargs 怎么样?

    class bar(foo):
        def __init__(self, *args, **kwargs):
            for name, value in zip(['a', 'b', 'c', 'd'], args): # <---
                kwargs[name] = value                            # <---
            args = ()                                           # <---
            if "c" in kwargs:
                kwargs['c'] = 'else'
            super(bar, self).__init__(*args, **kwargs)
    

    更新

    使用inspect.getcallargs的替代方案:

    import inspect
    
    class bar(foo):
        def __init__(self, *args, **kwargs):
            kwargs = inspect.getcallargs(foo.__init__, self, *args, **kwargs)
            kwargs.pop('self')
            if 'c' in kwargs:
                kwargs['c'] = 'else'
            super(bar, self).__init__(**kwargs)
    

    【讨论】:

    • 这个问题的部分原因是我仍然需要知道参数的名称和顺序,如果该基类不在您的控制之下,它可能会改变。我可以很容易地枚举__init__ 中的命名参数以匹配基类的参数,然后我不需要 zip,但是如果该顺序发生变化,(这就是我所说的脆弱性)。
    • @Mark0978,您可以使用inspect.getargspec(foo.__init__).args[1:] 代替['a', 'b', 'c', 'd']
    • @Mark0978,正如 JAB 所回答的,您也可以使用 inspect.getcallargs。见ideone.com/EjD1r8
    • 是的,这就是为什么我在这个答案上而不是在 JAB 上发表评论。
    【解决方案2】:

    这比你想象的更脆弱。您是否考虑过有人传递foo.__init__ 的参数列表中不存在的关键字参数(例如bar('a', f='something'))的情况?我的建议是要求bar 仅采用关键字参数,然后过滤foo.__init__ 的参数列表中不存在的键(您可以通过使用inspect.getargspec [或较新版本的Python 的相关函数,特别是从 3.3 开始的 SignatureParameter 对象] 如果参数可能改变)。这本身就有其自身的脆弱性,当然,作为使用bar 的程序员需要知道foo 的构造函数的相关参数名称,但取决于bar 的用途,他们可能需要知道foo 无论如何都需要什么参数,并且当有人知道他们通常也知道参数的名称时。

    现在我再次查看inspect,我意识到您可以使用另一种方法:inspect.getcallargs,它可能对您更有用。有了这个,你可以做,比如说,inspect.getcallargs(super(bar, self).__init__, *[self, 1], **{'c':3, 'd':4}) 并获得以下字典:{'a': 1, 'self': &lt;bar object&gt;, 'b': None, 'c': 3, 'd': 4}。然后您可以修改该字典并将其提供为super(bar, self).__init__(**fixed_dict) 或类似的东西。您仍然会遇到foo.__init__ 的参数列表中不存在关键字参数的问题(getcallargs 在传递无效参数时会引发与foo.__init__ 相同的错误)。

    【讨论】:

      猜你喜欢
      • 2020-10-18
      • 2017-04-07
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      • 2021-11-26
      相关资源
      最近更新 更多