Juan-Carlos Gandhi (
juan_gandhi) wrote2008-05-14 12:56 pm
![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
dynamic vs static
Я не собираюсь вступать в эту нонешнюю дискуссию. Просто хочу заметить, что речь идёт о "гарвардской архитектуре" супротив "архитектуры фон Неймана".
В первой данные отделены от кода, и задача программиста состоит в том, чтобы состыковать, хоть и через посредников, данные и код, который их обрабатывает; вот и приходится катать всякие конфигурации, эксэмэли, ентити бинзы; всё для того, чтобы уберечь код от данных. Тогда код можно статически проверить и отлить в бронзе. Чтоб не сломался.
Во второй что данные, что код, без разницы; код - это вид данных, а данные могут на определённом уровне интерпретироваться как код, или строить код, который эти данные сынтепретирует (как, помню, была какая-то база, которая строила классы для доступа прямо при открытии таблицы: взял таблицу - вот и классы загрузились; естественно, что динамически.
Ну а так, наверное, стоит почитать, конечно: Егге устраивает разгром в столице статической типизации
В первой данные отделены от кода, и задача программиста состоит в том, чтобы состыковать, хоть и через посредников, данные и код, который их обрабатывает; вот и приходится катать всякие конфигурации, эксэмэли, ентити бинзы; всё для того, чтобы уберечь код от данных. Тогда код можно статически проверить и отлить в бронзе. Чтоб не сломался.
Во второй что данные, что код, без разницы; код - это вид данных, а данные могут на определённом уровне интерпретироваться как код, или строить код, который эти данные сынтепретирует (как, помню, была какая-то база, которая строила классы для доступа прямо при открытии таблицы: взял таблицу - вот и классы загрузились; естественно, что динамически.
Ну а так, наверное, стоит почитать, конечно: Егге устраивает разгром в столице статической типизации
no subject
Where is your God now?
no subject
В наезде выше, разумеется, под "компиляцией" имеются в виду преобразования c разрешением ссылок (early binding; в этом смысле финальный этап компиляции доделывает линкер), как это происходит в, условно говоря, наследниках алгола. Тут я выразился неверно. Разумеется, компиляция собственно исполнимого кода в более простую форму (типа байткода) возможна и практикуется и в динамических языках.
no subject
Если закомментить присваивание, то ничего подобного не происходит, естественно.
Это означает, что при определении функции питон аккуратно _компилирует_ её тело, строя табличку локальных переменных, направляя обращения в эту табличку и всё такое. Если бы питон был чисто интерпретируемым, у него, конечно, не было бы возможности кинуть эксепшен на строчке "print i" ДО ТОГО, как выполнилась строчка "i = 2", определяющая i как локальную переменную.
no subject
Это было введено в питоне 2.0.
"An attempt has been made to alleviate one of Python's warts, the often-confusing NameError exception when code refers to a local variable before the variable has been assigned a value. For example, the following code raises an exception on the print statement in both 1.5.2 and 2.0; in 1.5.2 a NameError exception is raised, while 2.0 raises a new UnboundLocalError exception."
Несомненно, питоновский код компилируется в некий (высокоуровневый) байт-код, потому что это эффективнее, чем ходить всё время по дереву разбора. При этом, однако, имена *не* разрешаются окончательно. Хотя есть оптимизация, позволяющая более дёшево получать доступ к именам, про которые мы знаем, что они локальные (потому что раньше по ходу компиляции делали их инициализацию) — инструкция LOAD_FAST.
Вовсе *не компилятор* ругается на эту функцию (как он поругался бы на синтаксическую ошибку). Exception возникает при *исполнении* готового объекта (что, надо сказать, иногда бывает неприятным сюрпризом). Это LOAD_FAST, вставленный компилятором, ругается, afaict. Но так как он умный и понимает, что не найти он мог только локальную переменную, ругается он при помощи более конкретного UnboundLocalError, а не просто NameError.
Тот факт, что компилятор способен прийти к выводу о локальности переменной, говорит о том, что некоторый небольшой (imho, вполне тривиальный) анализ корректного кода он таки выполняет. Мой point в том, что компиляция эта тривиальна — примерно такую компиляцию исполнял и интерпретатор бейсика на 8-битных машинах моего детства, который тоже ключевые слова в байт-коды превращал и скобочные выражения раскладывал во что-то более быстро вычислимое.
no subject
Несомненно, питоновский код компилируется в некий (высокоуровневый) байт-код
---
U miss da point, bro. Байткод совершенно не высокоуровенен, да его может и вовсе не быть. Дело в спеках, которые ясно говорят, что локальной считается любая переменная, в которую было (точнее, может быть будет) выполнено присваивание, неважно где. Это, ещё раз повторю, на всякий случай, означает, что питоновский интерпретатор, когда ему скармливают очередную функцию, совершает действия, которые я называю компиляцией. Обработку кода без его исполнения. Довольно сложную обработку.
И очень плохо, что не настолько сложную, насколько это возможно, так что у Стиви появляется повод восторгаться очередным мастерски произведённым удалением гланд через задний проход.
Напоминаю, весь пойнт моего первого коммента заключался в том, что компайл-тайм обработка (в частности, статическая типизация) это бенефит, это подъём над бейзлайном, она доставляет.
То, что питон кидает эксепшен не во время компиляции, а во время интерпретации, это очень, очень плохо. У него во время компиляции уже есть все данные, он мог бы сообщить об этом тогда, но нет, он зачем-то пытается прикинуться чисто интерпретируемым, неудачно причём.
(в данном конкретном случае у него есть возможность, но она есть не всегда. Однако лично я могу сказать, что аналогичная шарповая проверка не раз спасала меня и я даже привык писать код так, чтобы она сработала, если я вдруг что-то забуду. Если у меня есть out переменная и развесистое дерево вычислений, то я никогда не присваиваю ей дефолтное значение в начале, нет, если вычисления удались, присваиваю результат и выхожу, если нет, присваиваю дефолтное значение и выхожу, это правильно, так надо.)
no subject
Байткод питона куда более "высокоуровнев", чем, скажем, у JVM млм CLR.
"If a name is bound in a block, it is a local variable of that block" — да-да. Но энфорсится это не совсем в момент компиляции, как видим. А проверяется компилятором тривиально. И именно тривиальность компилятора и мешает ему отсечь ошибку. (Ещё можно вспомнить, что связанные с этим определением проблемы решают в 3.0 введением слова nonlocal.)
Несомненно, статический анализ полезен! Беда в том, что он не бесплатен. Статическая типизация решает многие проблемы — и создаёт некоторые проблемы. Динамическая — то же самое, одно лечим, другим расплачиваемся. Например, писанием бесконечных вариантов одной функции с разными типами параметров и практически идентичным телом. Или type cast-ами в некоторых тонких местах, где можно проглядеть ошибку типизпции и влететь в неё в runtime (чего, казалось бы, "не может быть").
В C#, кстати, вносят много полезных фишек, позволяющих, с одной стороны, легче использовать некоторые динамические подходы, с другой — не писать лишних слов.
no subject
тут произошло некоторое смешение, тыкскыть, агенд.
требование явного объявления переменных перед использованием, к примеру (не типов, а имён!), не превращает язык из "динамического" в "статический", но весьма улучшает как компиляцию, так и диагностику.
no subject
no subject
>>> i = 1
no subject
В качестве самостоятельного упражнения оставляю исполнение "python b.py" и "import b" из интерпретатора (да, оба способа работают, но печатают разное).
no subject
Чудное открытие становится сделать легче, если дважды подряд позвать import b. Функция импорта умная и кэширует импортированные модули, а повторно импорт того же самого не исполняет (что кажется естественным). Потому и в вашем примере, кстати, не возникает бесконечной рекурсии импорта.
no subject
Ну вот и тут. Реально получаемый результат весьма забавен и заставляет задуматься о том, что же всё-таки кэшируется и как это всё работает.
no subject
Не вижу, где получается результат, отличный от ожидаемого.
Мы видим, как сначала исполнился импорт модуля a, идущий первой строчкой в модуле b. Он (a) занялся импортом модуля b. Но так как мы прямо вот сейчас уже заняты импортом модуля b, повторно (рекурсивно) его импортировать не надо. Осталось напечатать "a". На этом импорт модуля a завершился, и продолжился импорт (т.е исполнение) модуля b; исполнилась его вторая строчка, напечатавшая "b".
Не вижу, что тут неинтуитивного.
Второй пример разобрать? :) Или что я упускаю?
no subject
no subject
Картина та же самая. Будем писать имя файла и строку с комментарием.
b.py:1 — import a, поехали исполнять a.py и записали себе что импортируем a.
a.py:1 — import b, поехали исполнять b.py, ведь мы ни разу не пробовали его импортировать.
b.py:1 — import a, но у нас записано, что мы уже исполняем импорт a, потому второй раз не надо, едем дальше
b.py:2 — print "b", напечатали на экране первое "b".
Тут кончился b.py, который мы исполняли по просьбе import b из a.py, едем по нему дальше.
a.py:2 — print "a", напечатали "a".
Тут кончился a.py, который мы звали по просьбе import a, продолжился b.py, который мы звали из командной строки.
b.py:2 — print "b", напечатали вторую "b".
Тут кончился самый внешний b.py, сказочке конец, интерпретатор вышел.
Как-то так я себе это представляю. Может быть, я где-то серьёзно ошибаюсь, я ещё пороюсь в этом (тем более, надо по работе), но по крайней мере мне моя версия кажется правдоподобной :)