Я пишу симуляцию робототехники, используя 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
кажется неправильным.
Есть ли какой-то шаблон, который будет соблюдать следующие ограничения:
omni.whatever
должен происходить только после создания экземпляра SimulationApp
(в идеале в Sim.__init__
)omni.whatever
должен быть доступен каждой функции в классе Sim
.SimulationApp
создается только тогда, когда это необходимо, т. е. когда создается Sim
.Возможно, создание Sim
как класса не является правильным решением, и я открыт для других предложений, хотя имейте в виду, что мне нужно обработать некоторые параметры для создания экземпляра SimulationApp
.
Обновлено: параметры для SimulationApp
задаются в коде на основе других частей системы (модуль не обязательно будет запускаться как основной скрипт) и могут быть неизвестны на уровне модуля. Создание SimulationApp
запускает тяжелый симулятор, и мне нужен некоторый контроль над тем, когда это произойдет, поскольку сначала должна произойти какая-то другая инициализация. Создание этого экземпляра на уровне модуля непрактично и потребует импорта содержащего его модуля в середине кода инициализации.
просто выполните импорт и создайте экземпляр SimulationApp
на уровне модуля, затем передайте app
в качестве параметра Sim
, чтобы это было что-то вроде Sim(app)
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
. Любой последующий вызов свойства получит уже импортированный модуль. Я надеюсь, что этот подход поможет вам решить проблему.
Откуда взялся
params
? Конфигурационный файл? Среда? Аргументы командной строки? Ответ на этот вопрос важен для того, как вам следует это структурировать.