set和frozenset的继承行为似乎有所不同

Ala*_*ain 7 python inheritance immutability built-in-types

有人可以解释以下行为:

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
Run Code Online (Sandbox Code Playgroud)

令我惊讶的是,您可以更改冻结集的构造函数,而对于可变集的构造函数则不可能.

phi*_*hag 4

来自Python文档

\n\n
\n

如果__new__()返回 的实例,则将像 一样调用clsnew instance\xe2\x80\x99s () 方法,其中是新实例,其余参数与传递给 的参数相同。__init____init__(self[, ...])self__new__()

\n
\n\n

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

\n\n
class derivedset2(set):\n    def __new__(cls,*args):\n        return set.__new__(cls,*args)\n\n    def __init__(self, *initial_values):\n        set.__init__(self, initial_values)\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n

  • `frozenset` 使用 `__new__` 的原因不是缓存,而是因为它是不可变的。如果元素被“__init__”消耗,则该类必须是可变的。然后 `fs = freezeset.__new__(frozenset)` 将创建一个空的 `frozenset`,可以用 `fs.__init__([1, 2, 3])` 填充(变异)。每次子类化期间都会发生这种情况。 (3认同)