«Глобальный» импорт на уровне класса Python

Я пишу симуляцию робототехники, используя IsaacSim, которую я хотел бы инкапсулировать в специальный класс, чтобы биты симуляции были отделены от остальной части моей логики. Однако API Python IsaacSim имеет механизм «расширения», который требует следующей структуры импорта:

from isaacsim import SimulationApp
app = SimulationApp(some_params)
# other isaacsim imports
import omni.isaacsim.whatever

Где весь дальнейший импорт (omni.isaacsim.whatever) можно найти только после создания экземпляра SimulationApp. Создание этого экземпляра занимает некоторое время (поскольку он запускает симулятор IsaacSim) и требует параметров, которые мне нужно установить динамически. Моя текущая структура кода выглядит следующим образом:

class Sim(object):
  def __init__(self, params):
      from isaacsim import SimulationApp
      self.app = SimulationApp(params)
      import omni.isaacsim.whatever
      # etc.
  
  def some_function(self):
      # using omni.XY import

  def other_func(self):
      # also using omni.XY import

if __name__=='__main__':
  # figure out initialization and params
  ...
  sim = Sim(params)
  # rest of the logic
  sim.some_function()
  ...

Моя проблема заключается в том, чтобы выяснить, куда правильно поместить дальнейший импорт omni.XY. Они нужны мне в нескольких функциях класса Sim, однако импорт их в __init__ с помощью import omni.XY не раскрывает их в других функциях Sim, а размещение оператора импорта для каждого модуля omni.whatever в каждой функции Sim кажется неправильным. Есть ли какой-то шаблон, который будет соблюдать следующие ограничения:

  1. весь импорт omni.whatever должен происходить только после создания экземпляра SimulationApp (в идеале в Sim.__init__)
  2. этот импорт omni.whatever должен быть доступен каждой функции в классе Sim.
  3. (редактировать) Экземпляр SimulationApp создается только тогда, когда это необходимо, т. е. когда создается Sim.

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


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

Откуда взялся params? Конфигурационный файл? Среда? Аргументы командной строки? Ответ на этот вопрос важен для того, как вам следует это структурировать.

wim 20.08.2024 18:49

просто выполните импорт и создайте экземпляр SimulationApp на уровне модуля, затем передайте app в качестве параметра Sim, чтобы это было что-то вроде Sim(app)

juanpa.arrivillaga 20.08.2024 18:59
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

Как выяснилось, оператор import просто присваивает переменную в той области видимости, где он встречается. Либо import modulename (эквивалент modulename = __import__("modulename") ), либо from module import name (name = __import__("module").name)

Итак, все, что вам нужно, чтобы импорт внутри __init__ вел себя так же, как импорт верхнего уровня, — это объявить имя самого верхнего модуля как глобальную переменную перед импортом.

И.Э.:

class Sim(object):
  def __init__(self, params):
      global omni
      from isaacsim import SimulationApp
      self.app = SimulationApp(params)
      import omni.isaacsim.whatever

Простое добавление объявления global omni сделает переменную omni, назначенную оператором import ниже, доступной для всех функций и методов в том же модуле. Внутренние модули и пакеты являются внутренними атрибутами объекта omni, поэтому они просто будут работать.

Показываем еще один пример с простыми модулями stdlib в сеансе Python REPL:



In [67]: def blah():
    ...:     import math
    ...:     ...
    ...:

In [68]: math
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[68], line 1
----> 1 math

NameError: name 'math' is not defined

In [69]: blah()

In [70]: math
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[70], line 1
----> 1 math

NameError: name 'math' is not defined

In [71]: def blah():
    ...:     global math
    ...:     import math
    ...:     ...
    ...:

In [72]: blah()

In [73]: math
Out[73]: <module 'math' from '/home/jsbueno/.pyenv/versions/3.11.9/lib/python3.11/lib-dynload/math.cpython-311-x86_64-linux-gnu.so'>


Ограничение доступности omni атрибутами экземпляра:

В противном случае, если вам интересно, что в других методах вы можете получить доступ к импортированным модулям, выполнив self.omni.XY, можно обмануть оператор import, чтобы заполнить пространство имен self, запустив его внутри exec - что позволяет указать пространство имен, в котором выполняется командный блок. Так:

class Sim(object):
  def __init__(self, params):
      from isaacsim import SimulationApp
      self.app = SimulationApp(params)
      exec("import omni.isaacsim.whatever", vars(self))
      ...
  
  def some_function(self):
      # using omni.XY import:
      self.omni.isaacsim.whatever.func(...)
  ...

Вот как вы можете справиться с проблемами импорта в своем пользовательском классе Sim для IsaacSim.

class Sim(object):
  def __init__(self, params):
      from isaacsim import SimulationApp
      self.app = SimulationApp(params)
      self._omni_xy = None

  @property
  def omni_xy(self):
      if self._omni_xy is None:
          import omni.isaacsim.whatever as omni_xy
          self._omni_xy = omni_xy
      return self._omni_xy

  def some_function(self):
      # Access omni.XY functions using self.omni_xy
      self.omni_xy.some_function()

  def other_func(self):
      # Access omni.XY functions using self.omni_xy
      self.omni_xy.another_function()

В приведенном выше коде мы определили _omni_xy как закрытый атрибут, который будет содержать импортированный модуль omni.isaacsim.whatever. Чтобы получить к нему доступ, мы можем использовать свойство omni_xy, используя отложенную загрузку. При первом доступе к свойству модуль omni.isaacsim.whatever импортируется и сохраняется в _omni_xy. Любой последующий вызов свойства получит уже импортированный модуль. Я надеюсь, что этот подход поможет вам решить проблему.

Другие вопросы по теме