1

python s'appuie sur la variable __class__ pour être dans un cell pour un appel super(). Il obtient cette cellule à partir des variables free dans le premier cadre de pile.où la variable `__class__` est-elle stockée en python, ou comment le compilateur sait-il où le trouver

La chose étrange est cependant que cette variable n'est pas dans locals(), et il est lorsque vous venez de la référence à partir de la méthode __init__.

Prenez par exemple ce morceau de code:

class LogicGate: 
    def __init__(self,n): 
     print(locals()) 
     a = __class__ 
     print(locals()) 

Lorsque vous démonter l'appareil, vous pouvez le voir en quelque sorte sait que print et locals sont globals et __class__ est un LOAD_DEREF. Comment le compilateur le sait-il, avant d'exécuter le code? locals, print et __class__ ne sont que des noms de variables pour le compilateur autant que je sache. De même, __class__ est tout à coup dans le locals() avant même qu'il ne soit copié dans a.

4   10 LOAD_DEREF    0 (__class__) 

tout locals:

  2 LOAD_GLOBAL    1 (locals) 

Je demande parce que je travaille sur Skulpt un python à javascript compilateur. Et actuellement ce compilateur ne fait pas la différence entre print ou __class__ et tente de les obtenir tous les deux de la portée globale.

Comme vous pouvez le voir sur l'impression de la ast du peu au-dessus du code, l'analyseur ne fait pas de distinction entre locals ou __class__:

Module(body=[ClassDef(name='LogicGate', 
    bases=[], 
    keywords=[], 
    body=[FunctionDef(name='__init__', 
     args=arguments(args=[arg(arg='self', 
           annotation=None), 
          arg(arg='n', 
            annotation=None)], 
         vararg=None, 
         kwonlyargs=[], 
         kw_defaults=[], 
         kwarg=None, 
         defaults=[]), 
     body=[Expr(value=Call(func=Name(id='print', 
             ctx=Load()), 
               # here's the load for locals 
           args=[Call(func=Name(id='locals', 
                ctx=Load()), 
             args=[], 
             keywords=[])], 
           keywords=[])), 
       Assign(targets=[Name(id='a', 
            ctx=Store())], 
          # here's the load for __class__ 
        value=Name(id='__class__', 
           ctx=Load())), 
       Expr(value=Call(func=Name(id='print', 
             ctx=Load()), 
           args=[Call(func=Name(id='locals', 
                ctx=Load()), 
             args=[], 
             keywords=[])], 
           keywords=[]))], 
     decorator_list=[], 
     returns=None)], 
    decorator_list=[])]) 
+0

Je trouve une partie si la question, en python2 vous ne pouvez pas faire référence '__class__' de nulle part. Et ce n'est pas à un DEREF. – albertjan

+0

Utilisez-vous python 2 ou python 3? La cellule '__class__' est un hack Python 3 permettant d'appeler' super' sans arguments. – Dunes

Répondre

2

La cellule __class__ est un hack en Python 3 pour permettre super à appeler sans args. En Python 2, vous deviez appeler super avec des arguments standard (c'est-à-dire super(<current class>, self)).

La cellule __class__ elle-même est stockée dans le tuple <function>.__closure__. L'index de la cellule __class__ peut être obtenu en trouvant son index dans le tuple <function>.__code__.co_freevars. Par exemple,

>>> class A: 
    def __init__(self): 
     super().__init__() 

>>> A.__init__.__code__.co_freevars 
('__class__',) 
>>> A.__init__.__closure__ 
(<cell at 0x03EEFDF0: type object at 0x041613E0>,) 
>>> A.__init__.__closure__[ 
     A.__init__.__code__.co_freevars.index('__class__') 
    ].cell_contents 
<class '__main__.A'> 

Cependant, selon la fonction, co_freevars et __closure__ peut être None si la fonction ne pas utiliser les cellules. En outre, __class__ n'est pas garanti d'être présent. La cellule __class__ n'est présente que si une fonction appelée super est appelée sans arguments (elle ne doit pas nécessairement être super, par exemple, super = print; super() va tromper le compilateur en créant une cellule __class__) ou si __class__ est explicitement référencé et n'est pas local. Vous ne pouvez pas supposer que la cellule __class__ est toujours à l'index 0, comme suit (quoique bizarre) montre le code:

class A: 
    def greet(self, person): 
     print('hello', person) 

def create_B(___person__): 
    class B(A): 
     def greet(self): 
      super().greet(___person__) 
    return B 

B = create_B('bob') 
B().greet() # prints hello bob 

assert [c.cell_contents for c in B.greet.__closure__] == ['bob', B] 
+0

Un grand merci! Cela clarifie clairement comment tout cela collé ensemble.Et pourquoi tout d'un coup ce var "__class__' apparaît. C'est le PR sur lequel je travaille pour ajouter "super" à skulpt. https://github.com/skulpt/skulpt/pull/694 c'est une implémentation de python2 avec quelques trucs py3 ajoutés. Et trouver le type de la classe s'avère intéressant. – albertjan

+0

Comme je l'ai dit, l'implémentation de 'super' dans py 3 est très hacky. Il tourne autour du compilateur détectant qu'une fonction définie dans une classe appelle une fonction appelée «super», puis crée cette fonction avec une fermeture autour de la classe en cours de définition. – Dunes

+0

'__class__' (et par la suite' super() 'appels) semblent rompre lors de l'emballage des classes avec des décorateurs qui renvoient de nouvelles classes. – Coburn