2016-05-03 28 views
13

Có một bất ngờ ở đây:Biến Schrödinger: ô __class__ xuất hiện một cách kỳ diệu nếu bạn đang kiểm tra sự hiện diện của nó?

>>> 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 

Nó có vẻ như chỉ đề cập đến các __class__ được kiểm tra một cách rõ ràng bằng cách phân tích cú pháp? Nếu không chúng tôi sẽ nhận được một cái gì đó giống như

NameError: name '__class__' is not defined 

Thật vậy, nếu bạn sửa đổi để chỉ kiểm tra phím thay vào đó, ví dụ: kiểm tra '__class__' in locals(), sau đó chúng tôi chỉ có self trong phạm vi như mong đợi.

Biến số này được tiêm ma thuật vào phạm vi như thế nào? Đoán của tôi là đây là một cái gì đó để làm với super - nhưng tôi đã không sử dụng super, vậy tại sao trình biên dịch tạo ra một tham chiếu đóng cửa ngầm ở đây nếu nó không cần thiết?

Trả lời

12

Đây là một tương tác kỳ lạ trong việc thực hiện không có đối số của Python 3 super. Quyền truy cập vào super trong một phương pháp kích hoạt việc thêm biến số đóng ẩn __class__ ẩn tham chiếu đến lớp xác định phương thức. Trường hợp đặc biệt của trình phân tích cú pháp tải trọng tên super trong một phương pháp bằng cách thêm __class__ vào bảng biểu tượng của phương thức và sau đó phần còn lại của mã có liên quan đều tìm kiếm __class__ thay vì super. Tuy nhiên, nếu bạn cố gắng tự mình truy cập vào __class__, tất cả mã tìm kiếm __class__ sẽ thấy nó và cho rằng cần xử lý super!

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; 

Dưới đây là drop_class_free, đặt 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; 
} 

Các compiler section để kiểm tra ste_needs_class_closure và tạo ra các tế bào tiềm ẩn:

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; 
    } 
    ... 

Có nhiều lại mã levant, nhưng nó quá nhiều để bao gồm tất cả. Python/compile.cPython/symtable.c là nơi cần tìm nếu bạn muốn xem thêm.

Bạn có thể nhận được một số lỗi lạ nếu bạn cố gắng sử dụng một biến có tên __class__:

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

Foo().f() 

Output:

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 

Việc chuyển nhượng để __class__ nghĩa __class__ là một biến địa phương thay vì đóng cửa biến, do đó, các tế bào đóng cửa super() nhu cầu không có.

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

    Foo().f() 

f() 

Output:

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

Mặc dù có một __class__ biến thực tế trong phạm vi kèm theo, các chuyên vỏ của __class__ có nghĩa là bạn có được lớp thay vì giá trị biến phạm vi bao quanh của.

7

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

__class__ là một tài liệu tham khảo đóng cửa ngầm được tạo ra bởi trình biên dịch nếu có phương pháp trong một cơ thể lớp tham khảo hoặc __class__ hoặc siêu. Điều này cho phép dạng đối số 0 là super() để xác định chính xác lớp đang được xác định dựa trên phạm vi từ vựng, trong khi lớp hoặc cá thể được sử dụng để thực hiện cuộc gọi hiện tại được xác định dựa trên đối số đầu tiên được truyền cho phương thức.

+0

Đây là tài liệu *? Vâng, bây giờ tôi cảm thấy ngớ ngẩn về đào bới thông qua nguồn cho việc này. Đây là một câu trả lời tốt hơn tôi. – user2357112

+1

Câu trả lời không thực sự giải thích cho tôi tại sao tài liệu tham khảo cần phải được tạo khi siêu không được sử dụng. – wim

+1

@wim: Không cần tạo. Nó chỉ là anyway. Tôi đoán việc thực hiện chỉ xảy ra để làm việc theo cách đó và sau đó họ ghi lại nó bởi vì nó không phải là một thỏa thuận đủ lớn để có giá trị thay đổi, thay vì điều này là do thiết kế. – user2357112

Các vấn đề liên quan