Есть ли модуль Python для обработки адресов объектов Python?

(Когда я говорю "адрес объекта", я имею в виду строку, которую вы вводите в Python для доступа к объекту. Например, 'life.State.step', В большинстве случаев все объекты до последней точки будут пакетами / модулями, но в некоторых случаях они могут быть классами или другими объектами.)

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

  1. Учитывая объект, получить его адрес.
  2. По заданному адресу, получить объект, импортируя любые необходимые модули в пути.
  3. Сократите адрес объекта, избавившись от избыточных промежуточных модулей. (Например, 'life.life.State.step' может быть официальным адресом объекта, но если 'life.State.step' указывает на тот же объект, я бы хотел использовать его, потому что он короче.)
  4. Сократите адрес объекта, "укоренив" указанный модуль. (Например, 'garlicsim_lib.simpacks.prisoner.prisoner.State.step' может быть официальным адресом объекта, но я предполагаю, что пользователь знает, где prisoner пакет, поэтому я хотел бы использовать 'prisoner.prisoner.State.step' как адрес.)

Есть ли модуль / каркас, который обрабатывает такие вещи? Я написал несколько служебных модулей для этих целей, но если кто-то уже написал более зрелый модуль, который делает это, я бы предпочел использовать это.

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

ОБНОВЛЕНИЕ: Когда я говорю "объект", я в основном имею в виду классы, модули, функции, методы и тому подобное. Извините, что не разъяснил это раньше.

4 ответа

Решение

Я выпустил address_tools модуль, который делает именно то, что я просил.

Вот код Вот тесты.

Это часть GarlicSim, так что вы можете использовать его, установивgarlicsimи делать from garlicsim.general_misc import address_tools, Его основными функциями являются describe а также resolve, которые параллельны repr а также eval, Строки документации объясняют все о том, как работают эти функции.

Существует даже версия Python 3 для Python 3 в GarlicSim. Установите его, если хотите использовать address_tools на коде Python 3.

Краткий ответ: Нет. То, что вы хотите, невозможно.

Длинный ответ заключается в том, что то, что вы называете "адресом" объекта, совсем не так. life.State.step это всего лишь один из способов получить ссылку на объект в это конкретное время. Тот же "адрес" на более позднем этапе может дать вам другой объект, или это может быть ошибкой. Более того, ваш "адрес" зависит от контекста. В life.State.stepконечный объект зависит не только от того, что life.State а также life.State.step есть, но какой объект имени lifeотносится к этому пространству имен.

Конкретные ответы на ваши запросы:

  1. Конечный объект не имеет никакого способа узнать, как вы на него ссылались, и ни один из них не имеет кода, которому вы передаете объект. "Адрес" - это не имя, это не привязка к объекту, это просто произвольное выражение Python, которое приводит к ссылке на объект (как и все выражения.) Вы можете сделать это только с конкретными объектами, которые не ' Ожидается, что будет двигаться, например, классы и модули. Тем не менее, эти объекты могут перемещаться и часто перемещаться, так что то, что вы пытаетесь, может сломаться.

  2. Как уже упоминалось, "адрес" зависит от многих вещей, но эта часть довольно проста: __import__() а также getattr() могу дать вам эти вещи. Однако они будут чрезвычайно хрупкими, особенно когда речь идет о чем-то большем, чем просто доступ к атрибутам. Он может только удаленно работать с вещами, которые находятся в модулях.

  3. "Сокращение" имени требует рекурсивного изучения каждого возможного имени, то есть всех модулей и всех локальных имен, а также всех их атрибутов. Это был бы очень медленный и трудоемкий процесс, и чрезвычайно хрупкий перед лицом чего-либо с __getattr__ или же __getattribute__ метод, или со свойствами, которые делают больше, чем возвращают значение.

  4. это то же самое, что 3.

Для пунктов 3 и 4, я думаю, что вы ищете такие объекты, как

from life import life  # life represents life.life
from garlicsim_lib.simpacks import prisoner

Однако это не рекомендуется, так как вам или людям, которые читают ваш код, будет сложнее быстро понять, что prisoner представляет (откуда модуль? откуда? Вы должны взглянуть на начало кода, чтобы получить эту информацию).

Для пункта 1 вы можете сделать:

from uncertainties import UFloat

print UFloat.__module__  # prints 'uncertainties'

import sys
module_of_UFloat = sys.modules[UFloat.__module__]

Для пункта 2, учитывая строку 'garlicsim_lib.simpacks.prisoner', вы можете получить объект, на который он ссылается:

obj = eval('garlicsim_lib.simpacks.prisoner')

Это предполагает, что вы импортировали модуль с

import garlicsim_lib  # or garlicsim_lib.simpacks

Если вы даже хотите, чтобы это было автоматически, вы можете сделать что-то вроде

import imp

module_name = address_string.split('.', 1)[0]
mod_info = imp.find_module(module_name)
try:
    imp.load_module(module_name, *mod_info)
finally:
    # Proper closing of the module file:
    if mod_info[0] is not None:
        mod_info[0].close()

Это работает только в самых простых случаях (garlicsim_lib.simpacks должны быть доступны в garlicsim_lib, например).

Кодирование вещей таким способом, однако, весьма необычно.

Twisted имеет номер 2 в виде twisted/python/refle.py. Вам нужно что-то подобное для создания системы конфигурации на основе строк, например, с конфигурацией Django urls.py.

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

Другие вещи, которые вы ищете, накладывают достаточно ограничений на среду Python, чтобы не было такого понятия, как решение общего назначения.

Вот кое-что, что в некоторой степени реализует ваш #1

>>> import pickle
>>> def identify(f):
...   name = f.__name__
...   module_name = pickle.whichmodule(f, name)
...   return module_name + "." + name
... 
>>> identify(math.cos)
'math.cos'
>>> from xml.sax.saxutils import handler
>>> identify(handler)
'__main__.xml.sax.handler'
>>> 

Ваш № 3 недоопределен. Если я сделаю

__builtin__.step = path.to.your.stap

тогда код поиска должен найти его как "шаг"?

Самая простая реализация, о которой я могу подумать, это просто поиск во всех модулях и поиск элементов верхнего уровня, которые именно то, что вы хотите

>>> import sys
>>> def _find_paths(x):
...   for module_name, module in sys.modules.items():
...     if module is None:
...         continue
...     for (member_name, obj) in module.__dict__.items():
...       if obj is x:
...         yield module_name + "." + member_name
... 
>>> def find_shortest_name_to_object(x):
...   return min( (len(name), name) for name in _find_paths(x) )[1]
... 
>>> find_shortest_name_to_object(handler)
'__builtin__._'
>>> 5
5
>>> find_shortest_name_to_object(handler)
'xml.sax.handler'
>>> 

Здесь вы можете видеть, что "обработчик" был на самом деле в _ из предыдущего возврата выражения, что делает его самым коротким именем.

Если вам нужно что-то еще, например, рекурсивный поиск всех членов всех модулей, просто закодируйте его. Но, как показывает пример "_", будут сюрпризы. Кроме того, это нестабильно, поскольку импорт другого модуля может сделать доступным и более короткий путь к другому объекту.

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

А что касается вашего #4, как в мире будет какой-либо общий пакет для удовлетворения этих потребностей именования?

В любом случае вы написали

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

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

И, следовательно, ответ на ваш вопрос "нет, таких модулей нет".

Что делает ваш вопрос еще более запутанным, так это то, что реализация Python на C уже определяет "адрес объекта". Документы для id() говорят:

Детали реализации CPython: это адрес объекта.

То, что вы ищете, это имя или путь к объекту. Не "адрес объекта Python".

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