2017-09-11 8 views
3

J'utilise le paquet python attrs, en combinaison avec l'héritage et les slots. Je souhaite appeler la méthode de la classe parente à l'intérieur de la méthode dérivée. Le problème est démontré ci-dessous:Quelle est la différence entre super() et super explicite (Cl, self) (avec __slots__ et attrs)

import attr 

@attr.s(slots=True) 
class Base(): 
    def meth(self): 
     print("hello") 

@attr.s(slots=True) 
class Derived(Base): 
    def meth(self): 
     super().meth() 
     print("world") 

d = Derived() 
d.meth() 

je reçois:

TypeError: super(type, obj): obj must be an instance or subtype of type

Le problème semble être déclenché par une combinaison de attrs (classes undecorated avec un travail explicite __slots__=()), machines à sous (@attr.s régulières des classes -decorated travail) et l'appel plain super() (super(Derived, self) fonctionne).

Je voudrais comprendre comment super() se comporte différemment de l'explicite la version super(Derived, self), puisque le documentation dit qu'ils « font la même chose »

+0

Cela a depuis été corrigé dans attrs: https://github.com/python-attrs/attrs/pull/226 – Niobos

Répondre

1

super() repose normalement sur le compilateur de fournir une cellule de fermeture __class__, qui est lié à l'objet de classe sur lequel une méthode est dérivée. La fermeture est créé au moment où vous utilisez le nom super() dans une méthode (ou si vous avez utilisé __class__):

>>> class Foo(object): 
...  def bar(self): 
...   super() # just using super or __class__ is enough 
... 
>>> Foo.bar.__closure__[0].cell_contents 
<class '__main__.Foo'> 
>>> Foo.bar.__closure__[0].cell_contents is Foo 
True 

Cette fermeture permet de travailler sans super() arguments (l'argument self est tiré de l'espace de noms local).

Toutefois, attr génère un nouvel objet de classe lorsque vous spécifiez que vous souhaitez utiliser __slots__; vous ne pouvez pas ajouter d'emplacements à une classe après le fait, donc un new class object is created qui remplace celui que vous avez décoré.

La fermeture attachée à meth est la classe pré-décoration originale et non le même objet de classe comme la nouvelle classe générée:

>>> Derived.meth.__closure__[0].cell_contents 
<class '__main__.Derived'> 
>>> Derived.meth.__closure__[0].cell_contents is Derived 
False 

Cela brise les attentes super() a, ce qui rend impossible d'utiliser le 0 arguments une variante. La variante super(Derived, self) semble explicitement le nom Derived comme global lorsqu'il est appelé, trouver la nouvelle classe générée, donc des œuvres.

je vais dans un peu plus en détail sur la façon dont super() sans arguments fonctionne, et pourquoi, dans Why is Python 3.x's super() magic?

Cela a été rapporté comme issue #102 dans le suivi et fixe en modifiant la fermeture avec un certain ctypes hackery. Ce correctif fera partie de la prochaine version 17.3.

+0

Et la version explicite fonctionne parce qu'elle évalue seulement «Derived» lors de l'exécution du code au lieu de générer le classe? – Niobos

+0

@Niobos: exactement, vous avez explicitement appelé 'Derived', qui est considéré comme global à chaque fois que la méthode est appelée, et qui est liée à la nouvelle classe générée. –