2016-05-03 5 views
13

Es ist eine Überraschung hier:Schrödingers Variable: Die Zelle __class__ erscheint magisch, wenn Sie nach ihrer Anwesenheit suchen?

>>> class B: 
...  print(locals()) 
...  def foo(self): 
...   print(locals()) 
...   print(__class__ in locals().values()) 
...   
{'__module__': '__main__', '__qualname__': 'B'} 
>>> B().foo() 
{'__class__': <class '__main__.B'>, 'self': <__main__.B object at 0x7fffe916b4a8>} 
True 

Es scheint, wie die bloße Erwähnung von __class__ wird ausdrücklich vom Parser überprüft? Ansonsten sollten wir so etwas wie

NameError: name '__class__' is not defined 

Tat erhalten, wenn Sie nur ändern den Schlüssel überprüfen statt, das heißt überprüfen '__class__' in locals(), dann nur wir self in Umfang haben wie erwartet.

Wie kommt es, dass diese Variable magisch in den Bereich injiziert wird? Meine Vermutung ist, dass dies etwas mit super zu tun hat - aber ich habe super nicht verwendet, also warum erstellt der Compiler hier eine implizite Schließung Verweis, wenn es nicht benötigt wird?

Antwort

12

Dies ist eine seltsame Interaktion in Python 3-Implementierung von No-Argument super. Ein Zugriff auf super in einer Methode löst das Hinzufügen einer versteckten __class__ Abschlussvariable aus, die sich auf die Klasse bezieht, die die Methode definiert. Der Parser sperrt eine Methode mit dem Namen super in einer Methode, indem er auch __class__ der Symboltabelle der Methode hinzufügt, und dann sucht der Rest des relevanten Codes nach __class__ statt super. Wenn Sie jedoch versuchen, auf __class__ selbst zuzugreifen, sieht der gesamte Code, der nach __class__ sucht, es und denkt, dass es die super Behandlung tun sollte!

Here's where it adds the name __class__ to the symbol table if it sees super:

case Name_kind: 
    if (!symtable_add_def(st, e->v.Name.id, 
          e->v.Name.ctx == Load ? USE : DEF_LOCAL)) 
     VISIT_QUIT(st, 0); 
    /* Special-case super: it counts as a use of __class__ */ 
    if (e->v.Name.ctx == Load && 
     st->st_cur->ste_type == FunctionBlock && 
     !PyUnicode_CompareWithASCIIString(e->v.Name.id, "super")) { 
     if (!GET_IDENTIFIER(__class__) || 
      !symtable_add_def(st, __class__, USE)) 
      VISIT_QUIT(st, 0); 
    } 
    break; 

Hier drop_class_free, das setzt ste_needs_class_closure:

static int 
drop_class_free(PySTEntryObject *ste, PyObject *free) 
{ 
    int res; 
    if (!GET_IDENTIFIER(__class__)) 
     return 0; 
    res = PySet_Discard(free, __class__); 
    if (res < 0) 
     return 0; 
    if (res) 
     ste->ste_needs_class_closure = 1; 
    return 1; 
} 

Die compiler section die ste_needs_class_closure und schafft die implizite Zelle überprüft:

if (u->u_ste->ste_needs_class_closure) { 
    /* Cook up an implicit __class__ cell. */ 
    _Py_IDENTIFIER(__class__); 
    PyObject *tuple, *name, *zero; 
    int res; 
    assert(u->u_scope_type == COMPILER_SCOPE_CLASS); 
    assert(PyDict_Size(u->u_cellvars) == 0); 
    name = _PyUnicode_FromId(&PyId___class__); 
    if (!name) { 
     compiler_unit_free(u); 
     return 0; 
    } 
    ... 

Es gibt mehr re levant-Code, aber es ist zu viel, um alles mit einzubeziehen. Python/compile.c und Python/symtable.c sind wo Sie suchen, wenn Sie mehr sehen möchten.

Sie können einige seltsame Fehler erhalten, wenn Sie versuchen, eine Variable __class__ Namen zu verwenden:

class Foo: 
    def f(self): 
     __class__ = 3 
     super() 

Foo().f() 

Ausgang:

Traceback (most recent call last): 
    File "./prog.py", line 6, in <module> 
    File "./prog.py", line 4, in f 
RuntimeError: super(): __class__ cell not found 

Die Zuordnung zu __class__ bedeutet __class__ ist eine lokale Variable anstelle eines Verschlusses Variable, so dass die Schließzelle super() braucht nicht da ist.

def f(): 
    __class__ = 2 
    class Foo: 
     def f(self): 
      print(__class__) 

    Foo().f() 

f() 

Ausgang:

<class '__main__.f.<locals>.Foo'> 

Obwohl gibt es ein tatsächlichen __class__ Variable in dem umschließenden Umfang, das für eine spezielles Gehäuse __class__ bedeutet, dass Sie die Klasse erhalten anstelle der Anwendungsbereichs des Variablenwert umschließt.

7

https://docs.python.org/3/reference/datamodel.html#creating-the-class-object

__class__ ist eine implizite vom Compiler erstellt Verschlussreferenz wenn irgendwelche Methoden in einer Klasse Körper entweder __class__ oder Super beziehen. Dadurch kann die Nullargumentform von super() die Klasse, die auf der Basis von lexikalischem Scoping definiert wird, korrekt identifizieren, während die Klasse oder Instanz, die zum Ausführen des aktuellen Aufrufs verwendet wurde, anhand des ersten an die Methode übergebenen Arguments identifiziert wird.

+0

Dies ist * dokumentiert *? Nun, jetzt fühle ich mich dumm, die Quelle dafür zu durchforsten. Das ist eine bessere Antwort als meine. – user2357112

+1

Keine der beiden Antworten erklärt mir wirklich, warum die Referenz erstellt werden muss, wenn Super nicht verwendet wurde. – wim

+1

@wim: Es muss nicht erstellt werden. Es ist einfach so. Ich nehme an, dass die Implementierung einfach so funktioniert und dann haben sie es dokumentiert, weil es nicht groß genug war, um sich zu ändern, anstatt dass es sich um ein Design handelt. – user2357112