Порядок инициализации модуля Python? - PullRequest
14 голосов
/ 21 июня 2010

Я новичок в Python из C ++. Хотя я знаю, что не Pythonic пытаться найти подходящую концепцию, используя мои старые знания C ++, я думаю, что этот вопрос все еще является общим вопросом:

В C ++ существует хорошо известная проблема, называемая фиаско порядка инициализации глобальных / статических переменных, из-за неспособности C ++ решить, какая глобальная / статическая переменная будет инициализироваться первой через единицы компиляции, таким образом, глобальная / статическая переменная зависит от другой. в разных единицах компиляции они могут быть инициализированы раньше, чем их аналоги зависимостей, а когда зависимый начнет использовать сервисы, предоставляемые объектом зависимости, у нас будет неопределенное поведение. Здесь я не хочу углубляться в то, как C ++ решает эту проблему. :)

В мире Python я вижу использование глобальных переменных, даже в разных файлах .py, и один типичный случай использования, который я видел, был: инициализация одного глобального объекта в одном файле .py, а в других файлах .py - код просто безбоязненно начинает использовать глобальный объект, предполагая, что он, должно быть, был инициализирован где-то еще, что в C ++ само по себе совершенно неприемлемо из-за указанной выше проблемы.

Я не уверен, является ли приведенный выше вариант использования распространенной практикой в ​​Python (Pythonic), и как Python решает этот тип проблемы порядка инициализации глобальной переменной в целом?

Большое спасибо!

Lin

Ответы [ 2 ]

12 голосов
/ 21 июня 2010

Python import выполняет новые модули Python от начала до конца.Последующий импорт приводит только к копии существующей ссылки в sys.modules, даже если он все еще находится в середине импорта модуля из-за циклического импорта.Атрибуты модуля («глобальные переменные» фактически находятся в области видимости модуля), которые были инициализированы до того, как будет существовать циклический импорт.

main.py:

import a

a.py:

var1 = 'foo'
import b
var2 = 'bar'

b.py:

import a
print a.var1 # works
print a.var2 # fails
10 голосов
/ 21 июня 2010

В C ++ существует хорошо известная проблема, называемая фиаско порядка инициализации глобальных / статических переменных, из-за неспособности C ++ решить, какая глобальная / статическая переменная будет инициализироваться первой в единицах компиляции,

Я думаю, что это утверждение подчеркивает ключевое различие между Python и C ++: в Python нет такой вещи, как разные модули компиляции.Я имею в виду, что в C ++ (как вы знаете) два разных исходных файла могут быть скомпилированы полностью независимо друг от друга, и, таким образом, если вы сравните строку в файле A и строку в файле B, вам нечего сказатьВы, который будет размещен первым в программе.Это похоже на ситуацию с несколькими потоками: вы не можете сказать, будет ли выполняться конкретный оператор в потоке 1 до или после определенного оператора в потоке 2. Можно сказать, что программы на C ++ компилируются параллельно.

Внапротив, в Python выполнение начинается в верхней части одного файла и продолжается в четко определенном порядке через каждую инструкцию в файле, переходя в другие файлы в точках, где они импортированы.Фактически, вы могли почти думать о директиве import как о #include, и таким образом вы могли бы определить порядок выполнения всех строк кода во всех исходных файлах в программе.(Ну, это немного сложнее, поскольку модуль действительно выполняется только при первом импорте и по другим причинам.) Если программы на C ++ компилируются параллельно, программы на Python интерпретируются последовательно.

Ваш вопрос также затрагивает более глубокое значение модулей в Python.Модуль Python - это все, что находится в одном файле .py, - это реальный объект.Все, что объявлено в «глобальной» области видимости в одном исходном файле, на самом деле является атрибутом этого объекта модуля.В Python нет настоящей глобальной области видимости.(Программисты Python часто говорят «глобальный», и на самом деле в языке есть ключевое слово global, но оно всегда действительно относится к верхнему уровню текущего модуля.) Я мог видеть, что это немного странная концепция, чтобы получитьпривыкли исходить из фона C ++.Мне потребовалось некоторое привыкание, пришедшее из Java, и в этом отношении Java намного больше похожа на Python, чем C ++.(В Java также нет глобальной области видимости)

Я упомяну, что в Python совершенно нормально использовать переменную, не имея представления, была ли она инициализирована / определена или нет.Ну, может быть, это не нормально, но, по крайней мере, приемлемо при соответствующих обстоятельствах.В Python при попытке использовать неопределенную переменную возникает NameError;вы не получаете произвольного поведения, как в C или C ++, поэтому вы легко можете справиться с ситуацией.Вы можете увидеть этот шаблон:

try:
    duck.quack()
except NameError:
    pass

, который ничего не делает, если duck не существует.На самом деле чаще всего вы видите

try:
    duck.quack()
except AttributeError:
    pass

, который ничего не делает, если у duck нет метода с именем quack.(AttributeError - это тип ошибки, которую вы получаете, когда пытаетесь получить доступ к атрибуту объекта, но у объекта нет атрибута с таким именем.) Это то, что проходит проверку типа в Python: мы считаем, что есливсе, что нам нужно для утки, это крякать, мы можем просто попросить ее крякать, и если это так, нам все равно, действительно ли это * утка или нет.(Это называется утка набрав; -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...