Python `__init __. Py` и инициализация объектов в коде - PullRequest
4 голосов
/ 05 марта 2012

Я прочитал документацию о __init__.py файлах и некоторые хорошие вопросы здесь, на SO, но я все еще не уверен относительно ее правильного использования.

Context

У меня есть кодсо многими пакетами и подпакетами.Я определил много классов, некоторые из которых мне нужно создать один (и только один) экземпляр для всей пользовательской сессии.Эти новые объекты затем используются в разных частях кода, так что каждый раз, когда я (или пользователь) обновляю данные / информацию в этих объектах, они будут использоваться во всем коде без необходимости что-либо менять.Чтобы быть более понятным, позвольте мне показать вам базовую схему того, о чем я говорю.

Код имеет слишком упрощенную структуру, такую ​​как:

root/
    __init__.py
    tools/
        __init__.py
        ... (some modules)
        aliases.py (which defines the class Aliases)
    physics/
        __init__.py
        ... (some modules)
        constants/
            ... (some modules)
            constants.py (which defines the class Ctes)
        units.py (which defines the class Units)

В коде мне нужноуправлять псевдонимами, единицами и константами.Я нашел способ справиться с этим - создать по одному экземпляру каждого и использовать его во всем коде.С помощью этого метода, я уверен, например, что если псевдоним будет добавлен во время выполнения кода, его можно будет использовать в любом месте кода, поскольку существует только один общий экземпляр псевдонимов.Это то, что мне нужно (то же самое относится и к единицам, и константы между прочим).

Текущее состояние

На данный момент, я думаю, что я делаю это нелучшее.Действительно, я создаю экземпляр, скажем, псевдонимов, сразу после объявления класса, в том же файле:

в root/tools/aliases.py

import ... (stuff

class Aliases(object):
    """The Aliases manager"""
    ...

ALIASES = Aliases()

И затем в любом файлеМне нужно использовать псевдоним, я делаю:

в any_file.py (в любом месте кода)

from root.tools.aliases import ALIASES

ALIASES.method1() # possibly in functions or other classes
ALIASES.method2() # ... etc

А для некоторых других классов я даже использую файл __init__.pyв корне кода:

в root/__init__.py

# CTES is the instance of Ctes() created in root/physics/constants/constants.py
from root.physics.constants.constants import CTES
CTES.add(...) # add a new constant that needs to be known

(конечно, CTES не просто хранит некоторые константы, я определяю некоторые методы для их использования, поэтому имеет смыслчтобы они были в этом классе вместо того, чтобы просто определять их как обычные константы Python в модуле)

Вопросы

Мне интересно, правильно ли я это делаю (вероятно, нет).Может быть, лучше использовать файлы __init__.py и инициировать в нем общие экземпляры.Но тогда это приносит некоторые проблемы (например, циклы зависимости или увеличение использования памяти ...)?Кроме того, как использовать созданные экземпляры в другом месте кода?Примерно так:

в root/tools/__init__.py

import root.tools.aliases as Al
ALIASES = Al.Aliases()
# should I delete the imported module: del Al ??

, а затем в any_file.py

from root.tools import ALIASES
ALIASES.method(...)

Или все эти экземпляры лучше включить в файл (например, root/shared.py), который я импортирую в root/__init__.py, так что я уверен, что он инициирован?

Я читал много раз, лучше держать __init__.py файлы пустыми (что имеет местопрямо сейчас в моем коде, кроме, конечно, root/__init__.py).Как вы думаете?

Я немного растерялся (вы, вероятно, могли видеть это из того факта, что мне не очень ясно).Любая помощь / совет приветствуется.Я бы хотел избежать любых непитоновых решений или решений, которые могут запутать пользователя или сделать вещи небезопасными.

Ответы [ 2 ]

1 голос
/ 05 марта 2012

То, что вы делаете, когда создаете отдельный экземпляр в своем модуле, это FINE. Нет ничего плохого в использовании глобальных переменных (за исключением случаев, когда они не подходят, но это относится к любой языковой функции). Единственный потенциальный недостаток - если вы будете импортировать этот модуль без его использования и тратить ресурсы на инициализацию этого класса. Если ваш класс не делает что-то действительно тяжелое во время создания, в чем я очень сомневаюсь, этот эффект незначителен. Единственное, что я хотел бы предложить, это то, что вы меняете свое имя. Ваш класс, вероятно, должен начинаться с подчеркивания, чтобы пользователи вашего модуля знали, что его не нужно трогать, а ваш экземпляр должен быть в нижнем регистре. Метод, который вы используете, не вызовет побочных эффектов.

Никто не говорит, что вы ДОЛЖНЫ поместить все ваши классы в их собственный файл, или что вы ДОЛЖНЫ использовать классы вообще. Может быть, модуль псевдонима с функциями в нем и «приватным» состоянием в глобальных модулях модуля имеет больше смысла для вас. Поскольку вы не используете объектную систему для получения нескольких экземпляров с независимым состоянием, единственная причина создать класс - это то, как вы предпочитаете организовывать свой код и свой API. Самое главное, что вы довольны тем, как организован ваш код и как будут использоваться ваши модули. Вы гораздо чаще путаете пользователей, используя слишком много объектно-ориентированных методов, чем слишком мало. Просто убедитесь, что вы делаете урок, когда он вам действительно нужен.

Существует множество других подходов к синглетонам. Это первый раз, когда я услышал о «паттерне Борга», и он звучит для меня расточительно и глупо, но я думаю, что это, по крайней мере, сработает технически, а потери, по крайней мере, незначительны. Вы можете написать функцию, которая создает экземпляр класса при первом его вызове, сохраняет его в глобальном и возвращает глобальный в последующих вызовах (но тогда вам нужно беспокоиться о потоке, что не является проблемой, как вы уже делаете Это). Вы можете создать класс, чей метод __init__ вызывает TypeError, чтобы его вообще нельзя было создать, и делать все, используя методы класса или static, сохраняя все необходимое состояние для самого класса. Можно даже создать класс, который будет возвращать один и тот же экземпляр каждый раз. Результатом всего этого является то, что вы извлекаете объект из вашего модуля, который затем используете в своем коде, и то, как вы будете его использовать после этого, будет выглядеть одинаково.

Я думаю, что вы должны просто создать экземпляр класса inline в одном из ваших модулей, как в коде, который вы опубликовали, потому что он работает, это обычный шаблон, он разумный и имеет смысл для вас.

Нет причин удалять импортированный модуль. Это ничего не спасет. Модуль существует навсегда после импорта (если вы активно не делаете какие-то странные вещи, чтобы избавиться от него), и глобальные ресурсы - это не тот драгоценный ресурс, который вам нужно сохранить.

Вы не будете вводить циклическую зависимость, импортируя свои модули внутри __init__.py,, пока вы обращаетесь к своим модулям внутренне с относительным именем (то есть псевдонимами вместо root.tools.aliases). Круговые зависимости - реальная проблема, так что будьте осторожны с этим. Пока ваши модули импортируют только те вещи, которые находятся глубже в дереве, и которые вы считаете «низкоуровневыми» (то есть эти низкоуровневые модули не будут импортировать высокоуровневые вещи, которые могут их использовать), ты должен быть в порядке. Я бы предостерег от размещения какого-либо существенного кода в __init__.py,, но я думаю, что создание экземпляров ваших классов - это хорошо, если вы хотите, чтобы экземпляры жили.

1 голос
/ 05 марта 2012

Используйте шаблон Борг для реализации всех ваших общих классов в коде, а затем сохраняйте их в своих собственных модулях (или в связке) везде, где это может показаться подходящим.

Тогда простоимпортируйте их, где это необходимо, и создайте экземпляр класса.Все они будут использовать один и тот же контекст, и вы даже сможете точно настроить поведение экземпляров для каждого экземпляра.

Если они изолированы в своих собственных модулях, у вас не будет циклических зависимостей, и вы будете иметьминимизировать время синтаксического анализа для интерпретатора во время импорта.Лучше всего вы не будете загрязнять свое пространство имен, импортируя глобальные переменные в другое место.Когда ваша функция выходит из области видимости, экземпляр будет помечен для удаления, но состояние будет сохранено с определением класса.

...