Как выбрать функции/классы, определенные в __main__ (python)

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

У меня есть следующее решение, которое работает, есть ли причина, по которой этого не следует делать?

В myscript.py есть следующее:

import myscript
import pickle

if __name__ == "__main__":               

    print pickle.dumps(myscript.myclass())

else:

    class myclass:
        pass

изменить: распаковка будет выполняться в скрипте/модуле, который имеет доступ к myscript.py и может выполнять import myscript. Цель состоит в том, чтобы использовать такое решение, как parallel python, для удаленного вызова функций и иметь возможность чтобы написать короткий автономный скрипт, содержащий функции/классы, к которым можно получить удаленный доступ.


person andrew    schedule 08.08.2012    source источник
comment
Разве эта структура не противоречит цели объединения скрипта и модуля? Можно просто сделать отдельные файлы.   -  person Davis Herring    schedule 11.06.2019


Ответы (3)


Пикл, кажется, смотрит на область main для определений классов и функций. Изнутри модуля, из которого вы распаковываете, попробуйте следующее:

import myscript
import __main__
__main__.myclass = myscript.myclass
#unpickle anywhere after this
person Andrew    schedule 30.12.2014

Вы можете лучше управлять глобальными объектами, импортировав __main__ и используя методы, доступные в этом модуле. Это то, что dill делает для сериализации почти всего в python. По сути, когда укроп сериализует интерактивно определенную функцию, он использует некоторое искажение имени на __main__ как на стороне сериализации, так и на стороне десериализации, что делает __main__ допустимым модулем.

>>> import dill
>>> 
>>> def bar(x):
...   return foo(x) + x
... 
>>> def foo(x):
...   return x**2
... 
>>> bar(3)
12
>>> 
>>> _bar = dill.loads(dill.dumps(bar))
>>> _bar(3)
12

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

Или, если вы хотите, чтобы весь сеанс интерпретатора был отправлен как «изображение Python», укроп также может это сделать.

>>> # continuing from above
>>> dill.dump_session('foobar.pkl')
>>>
>>> ^D
dude@sakurai>$ python
Python 2.7.5 (default, Sep 30 2013, 20:15:49) 
[GCC 4.2.1 (Apple Inc. build 5566)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('foobar.pkl')
>>> _bar(3)
12

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

На самом деле я использую укроп для сериализации объектов и отправки их по параллельным ресурсам с помощью параллельного Python, многопроцессорности и mpi4py. Я удобно сворачиваю их в пакет pathospyina для MPI), который обеспечивает единый map интерфейс для различных серверных частей параллельной пакетной обработки.

>>> # continued from above
>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> Pool(4).map(foo, range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>
>>> from pyina.launchers import MpiPool
>>> MpiPool(4).map(foo, range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Существуют также неблокирующие и итерационные карты, а также непараллельные соединения труб. У меня тоже есть пафосный модуль для pp, однако он несколько нестабилен для функций, определенных в __main__. Я работаю над улучшением этого. Если хотите, разветвите код на github и помогите улучшить pp для функций, определенных в __main__. Причина, по которой pp плохо обрабатывается, заключается в том, что pp выполняет свои трюки с сериализацией, используя временные файловые объекты и читая историю сеанса интерпретатора... поэтому он не сериализует объекты так же, как многопроцессорность или mpi4py. У меня есть модуль укропа dill.source, который без проблем выполняет тот же тип травления, что и pp, но он довольно новый.

person Mike McKerns    schedule 17.10.2013
comment
Но это не работает, если вы загружаете объект укропа или сеанс укропа на компьютер без доступа к импортированным объектам в сеансе или используется в сохраненном объекте. например ``` from foo import bar x = dict(bar=bar) with open('test.pkl', 'wb') as fp: dill.dump(x, fp) ``` Затем на компьютере или сеансе w/ доступ к вашему PYTHONPATH, если вы делаете: ``` с open('test.pkl', 'rb') as fp: model = dill.load(fp) # ^^ вызывает ошибку No module named foo ``` - person Hillary Sanders; 02.11.2018
comment
@HillarySanders: если вы пытаетесь собрать скрипт, который ссылается на второй локальный (то есть неустановленный) модуль ... тогда да - это не сработает, если вы также не перенесете неустановленный модуль. - person Mike McKerns; 02.11.2018
comment
Мм, действительно. github.com/uqfoundation/dill/issues/123 Вероятна ли проблема № 123 будет реализовано в какой-то момент? - person Hillary Sanders; 02.11.2018
comment
Было бы здорово увидеть библиотеку, которая, например. взял объект python (например, экземпляр класса) и рекурсивно извлек все зависимости, сохранил их, чтобы их можно было загрузить на другой компьютер без доступа к исходному исходному коду. Это можно даже комбинировать с автоматическим созданием файла requirements.txt. Я чувствую, что кто-то должен был сделать это раньше, но я не могу найти реализацию в Интернете :). - person Hillary Sanders; 02.11.2018
comment
@HillarySanders: это правильный вопрос. Это не так просто, и раньше такого не было. dill делает многое из того, что вы ищете (захватывает зависимости и т. д.), но, в частности, не извлекает локально импортированный модуль. В большинстве случаев он получает большинство зависимостей, но не локальные модули. - person Mike McKerns; 02.11.2018

Если вы пытаетесь рассолить что-то, чтобы использовать его где-то еще, отдельно от test_script, это не сработает, потому что pickle (очевидно) просто пытается загрузить функцию из модуля. Вот пример:

test_script.py

def my_awesome_function(x, y, z):
    return x + y + z

picklescript.py

import pickle
import test_script
with open("awesome.pickle", "wb") as f:
    pickle.dump(test_script.my_awesome_function, f)

Если вы запустите python picklescript.py, а затем измените имя файла test_script, при попытке загрузить функцию произойдет сбой. например

Выполнение этого:

import pickle
with open("awesome.pickle", "rb") as f:
    pickle.load(f)

Дает вам следующую трассировку:

Traceback (most recent call last):
  File "load_pickle.py", line 3, in <module>
    pickle.load(f)
  File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/pickle.py", line 1378, in load
    return Unpickler(file).load()
  File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/pickle.py", line 858, in load
    dispatch[key](self)
  File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/pickle.py", line 1090, in load_global
    klass = self.find_class(module, name)
  File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/pickle.py", line 1124, in find_class
    __import__(module)
ImportError: No module named test_script
person Jeff Tratner    schedule 08.08.2012
comment
Я согласен. Однако я ищу решение, в котором модуль/скрипт имеет доступ к test_script.py, см. мое недавнее редактирование. - person andrew; 08.08.2012