【问题标题】:extending built-in python dict class扩展内置 python dict 类
【发布时间】:2013-08-26 16:04:20
【问题描述】:

我想创建一个可以扩展 dict 功能的类。到目前为止,这是我的代码:

class Masks(dict):

    def __init__(self, positive=[], negative=[]):
        self['positive'] = positive
        self['negative'] = negative

我想在构造函数中有两个预定义的参数:正负掩码列表。当我执行以下代码时,我可以运行

m = Masks()

并创建了一个新的 mask-dictionary 对象 - 这很好。但我希望能够像使用 dicts 一样创建这个遮罩对象:

d = dict(one=1, two=2)

但是使用面具失败了:

>>> n = Masks(one=1, two=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'two'

我应该在 Masks 的某个地方调用父构造函数 initinit 可能。我尝试使用**kwargs 并将它们传递给父构造函数,但仍然 - 出了点问题。有人可以指出我应该在这里添加什么吗?

【问题讨论】:

  • @IgnacioVazquez-Abrams 感谢此链接 - 它解释了默认参数绑定。但我也很感激有关如何处理我的 Masks.__init__ 的提示;)
  • 嗯,这就是 kwargs 的用途。

标签: python dictionary inheritance monkeypatching


【解决方案1】:

必须调用超类__init__ 方法。如果您希望能够使用Masks(one=1, ..) 语法,那么您必须使用**kwargs

In [1]: class Masks(dict):
   ...:     def __init__(self, positive=(), negative=(), **kwargs):
   ...:         super(Masks, self).__init__(**kwargs)
   ...:         self['positive'] = list(positive)
   ...:         self['negative'] = list(negative)
   ...:         

In [2]: m = Masks(one=1, two=2)

In [3]: m['one']
Out[3]: 1

一般注意事项:不要继承内置函数!!!似乎是一种扩展它们的简单方法,但它有很多陷阱在某些时候咬你。

一种更安全的扩展内置函数的方法是使用委托,它可以更好地控制子类的行为,并且可以避免继承内置函数的许多陷阱。 (请注意,实现__getattr__ 可以避免显式地重新实现许多方法)

当您想要将对象传递到执行显式isinstance 检查的某些代码中时,应将继承作为最后的手段。

【讨论】:

  • 以这种方式使用or时要小心;可能有可以传递给它的有效错误值。考虑使用私有标记对象并使用is
  • @IgnacioVazquez-Abrams 我删除了它们。我相信() 的默认值和到list 的显式转换更容易理解(并且允许参数是任何可迭代的),即使它的语义略有不同。
  • 子类化内置函数并不比子类化其他任何东西更危险,而且通常非常有用。 python 内置函数经过精心设计以支持子类化,尤其是dict。虽然我不相信 dict 的子类一定是 tdelaney 的最佳选择,但这个全面的建议与我自己的经验相矛盾。 -1.
  • @TokenMacGuy 我不同意你的说法。根据我的经验,每次我尝试子类化一个内置函数时,我发现使用委托(或其他完全不同的解决方案)要更简单且不易出错问题)。的确,问题不在于内置函数本身,而在于继承,但对于内置函数尤其如此,因为它们倾向于大量使用特殊方法,如果你不是这样的话,这可能会给你带来很多惊喜了解它们的确切语义以及解释器如何准确地调用它们。
  • FWIW 我强烈同意@TokenMacGuy,并发现内置子类化非常有用。当它首次被引入 Python 时,许多人认为它是一项重大改进——参见 Guido van Rossum 的Unifying types and classes in Python 2.2。 Bakuriu,如果您自己遇到了将它们子类化的问题,也许是由于您对如何/何时使用它们以及您自己不使用它们的理解有限......而且不一定会普遍适用于其他所有人。
【解决方案2】:

由于您只需要一个带有预定义条目的常规 dict,因此您可以使用工厂函数。

def mask(*args, **kw):
    """Create mask dict using the same signature as dict(),
    defaulting 'positive' and 'negative' to empty lists.
    """
    d = dict(*args, **kw)
    d.setdefault('positive', [])
    d.setdefault('negative', [])

【讨论】:

  • OP 从来没有提到他想做的就是提供一些默认值。他只是在编写它的子类时偶然发现了一个问题。他可能确实想在其子类中添加更多方法或数据,而在这种情况下使用函数将无法正常工作。
  • @Bakuriu hmmm...我一定不明白我想在构造函数中有两个预定义参数:是什么意思。
  • 您能否指出 OP 在哪里说这是他想要的唯一东西? AFAICT 只是对上面代码的一个(非常没用,因为它没有在代码上添加信息)注释,而不是他说他想扩展 dict 时的意思的形式化。
  • @Bakuriu 我想要一个带有默认条目的字典。还有一匹小马。
猜你喜欢
  • 2011-01-20
  • 2010-09-26
  • 2011-10-08
  • 1970-01-01
  • 1970-01-01
  • 2010-10-24
  • 2011-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多