python - Schrödinger의 변수:__class__ 셀이 존재하는지 확인하려면 마술처럼 나타납니다.




python-3.x closures python-datamodel (3)

놀랍습니다.

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

__class__ 대한 단순한 언급이 파서가 명시 적으로 확인한 것처럼 보입니다. 그렇지 않으면 우리는

NameError: name '__class__' is not defined

실제로 키 대신 만 확인하도록 수정하면 (즉, '__class__' in locals() 확인하는 경우 예상대로 범위 내에 있음).

이 변수가 마술처럼 범위 안으로 주입되는 것은 어떻게됩니까? 내 추측이 super 와 관련이있다.하지만 super 사용하지 않았기 때문에 컴파일러가 암시 적 클로저 참조를 만들지 않는 이유는 무엇인가?


Answers

이것은 Python 3의 인수없는 super 구현에서 이상한 상호 작용입니다. 메소드에서 super 에 액세스하면 메소드를 정의하는 클래스를 참조하는 숨겨진 __class__ 클로저 변수가 추가로 트리거됩니다. 구문 분석기는 메서드의 심볼 테이블에 __class__ 을 추가하여 메서드에서 이름 super 의 특수 사례를로드 한 다음 나머지 코드는 모두 super 대신 __class__ 를 찾습니다. 그러나 __class__ 직접 액세스하려고하면 __class__ 찾는 모든 코드 __class__ 코드를보고 super 처리를 수행해야한다고 생각합니다!

다음은 super 가 보이는 경우 심볼 테이블에 __class__ 라는 이름을 추가하는 위치입니다 .

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;

다음은 drop_class_free 를 설정하는 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;
}

ste_needs_class_closure 를 검사하고 암시 적 셀을 만드는 컴파일러 섹션 입니다.

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

더 관련성 높은 코드가 있지만 모든 코드를 포함하는 것은 너무 어렵습니다. Python/compile.cPython/symtable.c 는 더 많이보고 싶다면 보게됩니다.

__class__ 라는 변수를 사용하려고하면 이상한 버그가 생길 수 있습니다 :

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

Foo().f()

산출:

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

__class__ 대한 할당은 __class__ 이 클로저 변수 대신 로컬 변수이므로 클로저 셀 super() 필요가 없다는 것을 의미합니다.

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

    Foo().f()

f()

산출:

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

둘러싸는 범위에 실제 __class__ 변수가 있어도 __class__ 의 특수 대소 문자는 변수를 둘러싸는 범위의 변수 값 대신 클래스를 얻는다는 것을 의미합니다.


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

__class__ 는 클래스 본문의 메서드 중 __class__ 또는 super를 참조하는 경우 컴파일러에서 만든 암시 적 클로저 참조입니다. 이것은 렉시 컬 스코핑을 기반으로 정의 된 클래스를 올바르게 식별 할 수 있도록 제로 인수 형태의 super() 를 허용하는 반면 현재 호출을하는 데 사용 된 클래스 또는 인스턴스는 메소드에 전달 된 첫 번째 인수를 기반으로 식별됩니다.


나는 프레임이 그들 자신의 '그리드 공간'

그것은 올바른 가정입니다.

녹색 프레임의 오른쪽에있는 입력 필드 중 하나를 볼 수 있습니다. 왜 거기 가니?

문제는 여기에서 시작됩니다.

top_frame = Frame(root, ...).grid(row=0, ...)

파이썬에서 x = y().z() 는 항상 x.z() 의 결과로 설정합니다. top_frame = Frame(...).grid(...) 경우 grid(...) 항상 None 반환하므로 top_frameNone 됩니다. 이로 인해 최상위 프레임으로 들어가는 모든 위젯이 루트 창에 실제로 들어가게됩니다.

솔루션 개요

일반적으로 위젯을 작성하는 명령문의 일부로 grid , pack 또는 place 를 호출해서는 안됩니다 . 부분적으로는이 정확한 동작에 대한 것입니다.하지만 코드 작성이 어려워지며 시간이 지남에 따라 유지 관리하기가 더 어려워지기 때문입니다.

위젯 생성과 위젯 레이아웃은 두 가지 다른 것들입니다. 내 경험에 의하면 레이아웃 명령을 함께 그룹화하면 레이아웃 문제가 상당히 쉽게 디버깅됩니다.

또한 그리드를 사용할 때 일관성을 유지하고 레이아웃을 더 쉽게 시각화 할 수 있도록 항상 동일한 순서로 옵션을 배치해야합니다. 그리고 마지막으로 grid 를 사용할 때 항상 sticky 옵션을 지정하는 습관에 빠져 있어야하며 항상 각 프레임에 0이 아닌 가중치를 한 행과 한 열을 지정하십시오.

솔루션 예제

다음은 코드를 작성하는 방법입니다. 훨씬 더 길지만 이해하기가 훨씬 쉽습니다.

from Tkinter import *

root = Tk()
root.title('Model Definition')
root.geometry('{}x{}'.format(460, 350))

# create all of the main containers
top_frame = Frame(root, bg='cyan', width=450, height=50, pady=3)
center = Frame(root, bg='gray2', width=50, height=40, padx=3, pady=3)
btm_frame = Frame(root, bg='white', width=450, height=45, pady=3)
btm_frame2 = Frame(root, bg='lavender', width=450, height=60, pady=3)

# layout all of the main containers
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)

top_frame.grid(row=0, sticky="ew")
center.grid(row=1, sticky="nsew")
btm_frame.grid(row=3, sticky="ew")
btm_frame2.grid(row=4, sticky="ew")

# create the widgets for the top frame
model_label = Label(top_frame, text='Model Dimensions')
width_label = Label(top_frame, text='Width:')
length_label = Label(top_frame, text='Length:')
entry_W = Entry(top_frame, background="pink")
entry_L = Entry(top_frame, background="orange")

# layout the widgets in the top frame
model_label.grid(row=0, columnspan=3)
width_label.grid(row=1, column=0)
length_label.grid(row=1, column=2)
entry_W.grid(row=1, column=1)
entry_L.grid(row=1, column=3)

# create the center widgets
center.grid_rowconfigure(0, weight=1)
center.grid_columnconfigure(1, weight=1)

ctr_left = Frame(center, bg='blue', width=100, height=190)
ctr_mid = Frame(center, bg='yellow', width=250, height=190, padx=3, pady=3)
ctr_right = Frame(center, bg='green', width=100, height=190, padx=3, pady=3)

ctr_left.grid(row=0, column=0, sticky="ns")
ctr_mid.grid(row=0, column=1, sticky="nsew")
ctr_right.grid(row=0, column=2, sticky="ns")

root.mainloop()

결과:





python python-3.x closures python-datamodel