【问题标题】:Inheriting behaviours for set and frozenset seem to differset 和 freezeset 的继承行为似乎不同
【发布时间】:2011-06-18 12:48:15
【问题描述】:

有人可以解释以下行为吗:

class derivedset1(frozenset):
    def __new__(cls,*args):
        return frozenset.__new__(cls,args)  

class derivedset2(set):
    def __new__(cls,*args):
        return set.__new__(cls,args)    

a=derivedset1('item1','item2') # WORKS 
b=derivedset2('item1','item2') # DOESN'T WORK

Traceback (most recent call last):
  File "inheriting-behaviours.py", line 12, in <module>
    b=derivedset2('item1','item2') # DOESN'T WORK
TypeError: derivedset2 expected at most 1 arguments, got 2

令我惊讶的是,您可以更改冻结集的构造函数,而可变集的构造函数却不可能。

【问题讨论】:

  • 有趣的数据点:b=derivedset2(['item1','item2']) 有效。

标签: python inheritance immutability built-in-types


【解决方案1】:

来自Python documentation

如果__new__() 返回cls 的实例,则新实例的__init__() 方法将像__init__(self[, ...]) 一样被调用,其中self 是新实例,其余参数与之前相同传递给__new__()

set.__init__ 只接受一个参数,一个指定初始集合内容的可迭代对象。因此,您应该添加自己的初始化程序,该初始化程序接受所有附加参数并将它们作为初始设置值提供:

class derivedset2(set):
    def __new__(cls,*args):
        return set.__new__(cls,*args)

    def __init__(self, *initial_values):
        set.__init__(self, initial_values)

请注意,您应该覆盖 __init__ 并避免实现 __new__,除非您想实现对象缓存、单例或类似的奇怪东西。您的子类化适用于 frozenset 正是因为 frozenset 确实 从对象缓存中受益,即 Python 解释器只需要一个 frozenset 实例来处理两个具有相同内容的 frozenset 对象。

一般来说,您应该避免对内置类进行子分类,尤其是在您的语义不兼容时(在这种情况下,set([])derivedset2([]) 返回完全不同的结果)。

【讨论】:

  • frozenset 使用__new__ 的原因不是缓存,而是因为它是不可变的。如果元素被__init__ 消耗,则该类必须在某种程度上是可变的。然后fs = frozenset.__new__(frozenset) 将创建一个空的frozenset,可以用fs.__init__([1, 2, 3]) 填充(变异)。每次在子类化期间都会发生这种情况。
  • 不可变对象的值必须在创建时建立——在__new__()方法中——正是因为它不能在以后的__init__()方法中完成。跨度>
猜你喜欢
  • 2013-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-01
  • 1970-01-01
  • 2020-10-19
  • 2017-01-29
  • 2013-05-15
相关资源
最近更新 更多