2017-08-04 2 views
6

Je comprends que __new__ est une méthode statique et super() peut être appelé depuis qu'il crée un nouvel objet, comme suit:Pourquoi super() ne fonctionne pas avec des méthodes statiques autres que __new__?

>>> class A: 
...  def __new__(cls): 
...   print('__new__ called') 
...   return super().__new__(cls) 
... 
>>> a = A() 
__new__ called 

Pourquoi pas le travail d'appel super avec d'autres méthodes statiques? Pourquoi le suivant échoue?

>>> class B: 
...  @staticmethod 
...  def funcB(): 
...   print('funcB called') 
... 
>>> class C(B): 
...  @staticmethod 
...  def funcC(): 
...   print('funcC called') 
...   super().funcB() 
... 
>>> c = C() 
>>> c.funcC() 
funcC called 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in funcC 
RuntimeError: super(): no arguments 
+1

https://bugs.python.org/issue15753. 'super (C, C) .funcC()' fonctionnerait dans votre cas. –

Répondre

3

super() sans argument en Python 3 est essentiellement un hack sur sa version à base d'arguments.

Lorsque super() reçoit aucun argument, il va chercher le premier argument i.e. la classe en utilisant une variable de cellule spéciale nommée __class__ et pour la deuxième argument, il obtiendra la première variable locale de la pile (qui va être le premier argument de la fonction).

Dans le cas de __new__ il peut obtenir les deux (__class__ et cls) et fonctionne très bien.

Mais dans ce cas, par exemple, il n'y a pas de deuxième variable disponible en dehors de __class__, d'où il échoue.

class A: 
    @staticmethod 
    def func(): 
    super().func() # super(__class__, <missing>).func() 


A().func() # RuntimeError: super(): no arguments 

Maintenant, si nous changeons d'accepter un argument alors les choses changent:

class A: 
    @staticmethod 
    def func(foo): 
    super().func() 


# This fails because super(B, 1).func() doesn't make sense. 
A().func(1) # TypeError: super(type, obj): obj must be an instance or subtype of type 
# Works! But as there's no parent to this class with func() it fails as expected. 
A().func(A()) # AttributeError: 'super' object has no attribute 'func' 

D'où la seule solution est de faire les choses explicites avec super() dans votre cas:

super(C, C).funcC() 

En général, je ne suis pas sûr pourquoi l'implémentation en cas de staticmethod ne peut pas faire une exception et utilisez __class__ pour les deux arguments pour le faire fonctionner.


CPython code connexes:

static int 
super_init(PyObject *self, PyObject *args, PyObject *kwds) 
{ 
    superobject *su = (superobject *)self; 
    PyTypeObject *type = NULL; 
    PyObject *obj = NULL; 
    PyTypeObject *obj_type = NULL; 

    if (!_PyArg_NoKeywords("super", kwds)) 
     return -1; 
    if (!PyArg_ParseTuple(args, "|O!O:super", &PyType_Type, &type, &obj)) 
     return -1; 

    if (type == NULL) { 
     /* Call super(), without args -- fill in from __class__ 
      and first local variable on the stack. */ 
     PyFrameObject *f; 
     PyCodeObject *co; 
     Py_ssize_t i, n; 
     f = PyThreadState_GET()->frame; 
     if (f == NULL) { 
      PyErr_SetString(PyExc_RuntimeError, 
          "super(): no current frame"); 
      return -1; 
     } 
     co = f->f_code; 
     if (co == NULL) { 
      PyErr_SetString(PyExc_RuntimeError, 
          "super(): no code object"); 
      return -1; 
     } 
     if (co->co_argcount == 0) { 
      PyErr_SetString(PyExc_RuntimeError, 
          "super(): no arguments"); 
      return -1; 
     } 
     ... 
+0

Merci de clarifier, 'super (C, C) .funcB()' fonctionne. Une chose: quand nous écrivons 'super (type, type2)' alors, selon la documentation 'type2' devrait être une sous-classe de' type'. Mais ici les deux types sont identiques, c'est-à-dire 'C'. – debashish

+1

@Deb Chaque type est un sous-type de lui-même. –

0

Vous avez raison, mais __new__ est une méthode statique (spécial placé dans un étui de sorte que vous ne devez pas déclarer comme tel) qui prend la classe dont une instance a été demandé comme premier argument. Son comportement idéal est un peu comme un classmethod.

Mais lorsque vous appelez une méthode staticmethod, la méthode ne reçoit rien, et n'a aucun moyen de savoir à partir de quel objet ou classe elle a été appelée. C'est la raison pour laquelle vous ne pouvez pas accéder à super dans un staticmethod.

Par conséquent, nouveau a par définition un argument de classe et peut utiliser super avec lui. Pour votre logique, vous devriez faire un appel pour instater votre objet et appeler la fonction que vous souhaitez appeler à partir de là.

+1

'super()' ne se soucie pas de savoir qui a appelé une fonction, 'super()' utilise la variable '__class__' pour déterminer la classe de la fonction en cours. –

0

__new__ est traité comme un cas particulier par l'interpréteur Python (à peu près toutes les méthodes "dunder" sont). Un aspect de cette gestion de cas spéciaux est de permettre à super d'avoir accès à l'objet de classe sous-jacent alors qu'un staticmethod ne le fait normalement pas. Vous pouvez rechercher __new__ dans the source code for the type object pour jeter un coup d'oeil à ce qui se passe sous le capot.

Pour résoudre ce que vous essayiez de faire avec l'héritage, vous voulez probablement un classmethod.

>>> class B: 
...  @classmethod 
...  def funcB(cls): 
...   print('funcB called') 
... 
>>> class C(B): 
...  @classmethod 
...  def funcC(cls): 
...   print('funcC called') 
...   super().funcB() 
... 
>>> c = C() 
>>> c.funcC() 
funcC called 
funcB called 
+2

['__new__'] (https://docs.python.org/3/reference/datamodel.html#object.__new__) est une méthode statique. –

+1

@AshwiniChaudhary Je me rends compte qu'ils disent méthode statique là, mais je ne serais pas surpris si c'est une erreur de documentation. Je vais creuser dans ça. Il se comporte comme une méthode de classe car il lui transmet l'objet de classe. – SethMMorton

+0

@AshwiniChaudhary On dirait que vous avez raison ... [c'est dans le code source C] (https://github.com/python/cpython/blob/3.6/Objects/typeobject.c#L2597). Il semble que la raison en soit parce que «nouveau» a été traité spécialement par l'interprète. – SethMMorton