Наследование поведения для набора и замороженного набора, кажется, отличается

Может кто-нибудь объяснить следующее поведение:

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

Меня удивляет, что вы можете изменить конструктор замороженного набора, тогда как это невозможно для конструктора изменяемого набора.


person Alain    schedule 31.01.2011    source источник
comment
Интересная точка данных: b=derivedset2(['item1','item2']) работает.   -  person Marcelo Cantos    schedule 31.01.2011


Ответы (1)


Из документации Python:

Если __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([]) возвращают совершенно разные результаты).

person phihag    schedule 31.01.2011
comment
Причина, по которой frozenset использует __new__, заключается не в кэшировании, а в том, что он неизменяем. Если бы элементы потреблялись __init__, класс должен был бы быть несколько изменяемым. Затем fs = frozenset.__new__(frozenset) создаст пустой frozenset, который можно будет заполнить (мутировать) fs.__init__([1, 2, 3]). Это будет происходить каждый раз во время подкласса. - person Rosh Oxymoron; 31.01.2011
comment
Значение для неизменяемых объектов должно быть установлено в момент создания — в методе __new__() — именно потому, что это нельзя сделать позже в методе __init__(). - person martineau; 31.01.2011