Почему местность определяется во время компиляции?

Это немного зависит от этого вопроса.

  • Почему локальность определяется во время компиляции, а не во время выполнения? Это чисто для производительности?
  • Существуют ли языки, которые ищут свои переменные во время выполнения? То есть каждый раз, когда к переменной обращаются, эта переменная сначала выполняется в локальной области, а затем поиск проходит через все охватывающие области?
  • Как языки ECMA справляются с этим?

Чтобы поставить вопрос 2 другими словами: Существуют ли языки, где работает следующий код (в необходимом синтаксисе):

def f(): print ('f')

def g():
    f()
    f = 42

g()

Ответ 1

Это поведение называется лексическим охватом. То, что вы описываете в # 2, называется динамическим охватом, хотя словесное описание вводит в заблуждение (вы говорите "охватывающие области", где "вызывающие функции" или "записи активации" были бы более точными).

Практически для каждого используемого языка используется лексическое охват, а не только Python. Это включает С# и ECMAScript/JavaScript (запрет eval и with), который, как я предполагаю, означает то, что вы подразумеваете под "ECMA-языками". Были языки, и некоторые из них все еще используются. Emacs Lisp является одним, а Perl предлагает как лексическое, так и динамическое масштабирование.

Обоснование лексического охвата не только для производительности (полное лексическое охват, включая закрытие, фактически имеет стоимость исполнения, см. проблемы с funcarg), это для простоты и надежности. Хотя это может быть удивительно, когда вы сначала изучаете язык, правила на самом деле мертвы, поэтому опытный программист может мгновенно сказать, какое использование идентификатора относится к какой области. Можно понять функции изолированно, поскольку на выполнение не влияет тот, кто называет эти функции и как они решили назвать свои переменные.