Hoe een OrderedDict te subclasseren?

Het subclasseren van een Python dict werkt zoals verwacht:

>>> class DictSub(dict):
...     def __init__(self):
...         self[1] = 10
...         
>>> DictSub()
{1: 10}

Hetzelfde doen met een collections.OrderedDict werkt echter niet:

>>> import collections
>>> class OrdDictSub(collections.OrderedDict):
...     def __init__(self):
...         self[1] = 10
...         
>>> OrdDictSub()
(…)
AttributeError: 'OrdDictSub' object has no attribute '_OrderedDict__root'

De implementatie van OrderedDict maakt dus gebruik van een private __ root atribute, die voorkomt dat de subklasse OrdDictSub zich gedraagt ​​als de DictSub -subklasse. Waarom? Hoe kan iemand erven van een OrderedDict?

26
Waarom het downvote?
toegevoegd de auteur EOL, de bron

2 antwoord

U moet OrderedDict .__ init __ aanroepen van uw __ init __ :

class OrdDictSub(collections.OrderedDict):
    def __init__(self):
        super(OrdDictSub, self).__init__()

U hebt OrderedDict geen kans gegeven om zichzelf te initialiseren. Technisch gezien wil je dit ook doen voor je dict -subklasse, omdat je een volledig geïnitialiseerd dict wilt. Het feit dat dict zonder werkt, is gewoon geluk.

34
toegevoegd
Bedankt. Mijn slechte, inderdaad. Het is waar dat ik werd beïnvloed door dictaat. Ik heb zelfs een tijd geleden een vraag gesteld over dict ( stackoverflow.com/questions/2033150/… )!
toegevoegd de auteur EOL, de bron
wil je geen args en kwargs in init? def __init __ (self, * args, ** kwargs): super (OrdDictSub, self) .__ init __ (* args, ** kwargs)
toegevoegd de auteur hobs, de bron

Probeer de superklasse te initialiseren met de methode __ init __ :

def __init__(self):
    collections.OrderedDict.__init__(self)
    self[1] = 10

Dit is de normale manier om een ​​subklasse te initialiseren. U hebt niet om de superklasse's __ init __ -methode in het algemeen te gebruiken, maar als u geen kennis heeft van de implementatie van de superklasse, moet u echt __ init __ aanroepen .

2
toegevoegd
@DietrichEpp: een goed antwoord, maar het gebruik van super() is geen kwestie van stijl: het is belangrijk om het te gebruiken (in plaats van een expliciete superklasse te gebruiken) voor het geval uw eigen klasse een subklasse heeft. Voorbeeld van verwijzing: rhettinger.wordpress.com/2011/05/26/super-overwogen-super . Bovendien maakt het gebruik van super() het eenvoudiger om de code te onderhouden (het is gemakkelijker om de naam van de klasse te wijzigen).
toegevoegd de auteur EOL, de bron
@JohnY: Overeengekomen, over super() dat niet handiger is in het geval van wijzigingen van klassennaam in Python 2, maar alleen in Python 3.
toegevoegd de auteur EOL, de bron
@astynax: beide manieren werken, dus het is een kwestie van stijl.
toegevoegd de auteur Dietrich Epp, de bron
@JohnY: Nou, ik heb het getest en het gebruik van zelf .__ class __ klopt niet, omdat self .__ class __ altijd de eigenlijke klasse geeft! Hier is een demo die u zelf kunt uitproberen, deze zal een RuntimeError gooien wanneer deze de maximale recursiediepte overschrijdt: gist.github.com/depp/87a5f1079556eb0ed07e
toegevoegd de auteur Dietrich Epp, de bron
@JohnY: Maar je kunt alleen weten dat het veilig is als je weet dat je klasse nooit zal worden geclasseerd, wat volgens mij niet altijd een goede veronderstelling is.
toegevoegd de auteur Dietrich Epp, de bron
@JohnY: Het is onjuist om te zeggen dat als je super() gebruikt, anderen worden genaaid als ze dat niet doen. Je kunt super() mixen en baseclass-calls prima richten, zolang je dit niet doet in een klasse met meerdere paden naar een basisklasse (in dat geval heb je om super() te gebruiken). De enige die altijd voorkomt in veelvoorkomende zaken is super (self .__ class__, self) , daarom zou je het niet moeten gebruiken.
toegevoegd de auteur Dietrich Epp, de bron
@JohnY: Dit uitstekende antwoord van Martijn Pieters behandelt het: stackoverflow.com/a/18208725/82294 - als je denkt Martijn heeft ongelijk, ga daar ruzie maken.
toegevoegd de auteur Dietrich Epp, de bron
Gebruik super() om methoden van superklasse aan te roepen
toegevoegd de auteur astynax, de bron
@EOL: Ik kan accepteren dat super() de juiste keuze is, maar ik ben het niet eens met uw "bovendien": ik zou zeggen dat het zes van de ander is, in Python 2 . Als u de naam van uw OrdDictSub -klasse wilt wijzigen maar toch hetzelfde bovenliggende item wilt behouden, betekent het gebruik van super() dat u het op twee plaatsen moet doen in plaats van slechts één . Aan de andere kant, als u de naam OrdDictSub wilt behouden maar het bovenliggende item wilt wijzigen, betekent niet met super() dat u het moet doen het op twee plaatsen in plaats van één. Gelukkig is dit opgelost in Python 3 (gewoon super() aanroepen zonder parameters).
toegevoegd de auteur John Y, de bron
@DietrichEpp: OK, eerlijk genoeg. Het is inderdaad een erg leuke uitleg van Martijn. Ik heb enkele van mijn opmerkingen hier verwijderd, omdat ik geloof dat ze niet behulpzaam zijn voor toekomstige lezers, zelfs niet in een historische hoedanigheid (behalve om te dienen als bewijs van mijn onwetendheid).
toegevoegd de auteur John Y, de bron