Как загрузить скомпилированные модули Python из памяти?

@ilitirit:

public class Class<T> where T : IComparable
{
    public T Value { get; set; }
    public void MyMethod(T val)
    {
        if (Value == val)
            return;
    }
}

Оператор '==' не может быть применен к операндам типа 'T' и 'T'

, я не могу думать о способе сделать это без явного пустого теста, сопровождаемого путем вызова метода Equals или объекта. Равняется, как предложено выше.

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

6
задан Steve 2 December 2009 в 04:53
поделиться

2 ответа

Это зависит от того, что именно у вас есть как «модуль (предварительно скомпилированный)». Предположим, что это в точности содержимое файла .pyc , например, ciao.pyc , созданного:

$ cat>'ciao.py'
def ciao(): return 'Ciao!' 
$ python -c'import ciao; print ciao.ciao()'
Ciao!

IOW, таким образом построив ciao.pyc скажите, что теперь вы это делаете:

$ python
Python 2.5.1 (r251:54863, Feb  6 2009, 19:02:12) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> b = open('ciao.pyc', 'rb').read()
>>> len(b)
200

и ваша цель - перейти от этой байтовой строки b к импортируемому модулю ciao . Вот как:

>>> import marshal
>>> c = marshal.loads(b[8:])
>>> c
<code object <module> at 0x65188, file "ciao.py", line 1>

вот как вы получаете объект кода из двоичного содержимого .pyc . Правка : если вам интересно, первые 8 байтов - это «магическое число» и отметка времени - здесь не требуется (если вы не хотите их проверять на работоспособность и вызывать исключения, если это необходимо, но это кажется выходит за рамки вопроса; marshal.loads все равно поднимется, если обнаружит поврежденную строку).

Затем:

>>> import types
>>> m = types.ModuleType('ciao')
>>> import sys
>>> sys.modules['ciao'] = m
>>> exec c in m.__dict__

то есть: создать новый объект модуля, установить его в sys.modules , заполнить его, выполнив объект кода в его __ dict __ . Edit : порядок, в котором вы выполняете вставку sys.modules и exec , имеет значение тогда и только тогда, когда у вас может быть циклический импорт - но это order import обычно использует собственный Python, поэтому лучше имитировать его (что не имеет конкретных недостатков).

Вы можете «создать новый объект модуля» несколькими способами (например, из функций в стандартной библиотеке такие модули, как new и imp ), но «вызов типа для получения экземпляра» - это нормальный способ Python в наши дни, и обычное место для получения типа (если у него нет встроенного имени или он уже у вас уже есть) находится в модуле стандартной библиотеки types , поэтому я рекомендую именно это.

Теперь , наконец:

>>> import ciao
>>> ciao.ciao()
'Ciao!'
>>> 

... вы можете импортировать модуль и использовать его функции, классы и так далее. Другие операторы import из ) затем найдут модуль как sys.modules ['ciao'] , поэтому вам не нужно будет повторять эту последовательность операций (действительно, вам не нужен этот последний import ) здесь, если все, что вам нужно, это убедиться, что модуль доступен для импорта из другого места - я добавляю его только в покажите это работает; -).

Edit : Если вам абсолютно необходимо импортировать таким образом пакеты и модули оттуда, а не "простые модули" как я только что показал, это тоже выполнимо, но немного сложнее. Поскольку этот ответ уже довольно длинный, и я надеюсь, что вы сможете упростить себе жизнь, придерживаясь для этой цели простых модулей, я собираюсь уклониться от этой части ответа; -).

Также обратите внимание, что это может или может не делать то, что вы хотите, в случаях «загрузки одного и того же модуля из памяти несколько раз» (при этом каждый раз модуль перестраивается; вы можете проверить sys.modules и просто пропустить все, если модуль уже существует) и, в частности, когда такое повторяется «загрузка из памяти» происходит из нескольких потоков (требующих блокировок, но лучшая архитектура - иметь один выделенный поток, предназначенный для выполнения задачи, с другими модулями, связывающимися с ним через очередь).

Наконец, есть '

31
ответ дан 8 December 2019 в 02:19
поделиться

Скомпилированный файл Python состоит из

  1. магического числа (4 байта) для определения типа и версии Python,
  2. метки времени (4 байта) для проверки наличия более нового источника,
  3. маршалированный объект кода.

Чтобы загрузить модуль, вы должны создать объект модуля с помощью imp.new_module () , выполнить немашалированный код в пространстве имен нового модуля и поместить его в sys.modules . Ниже в примере реализации:

import sys, imp, marshal

def load_compiled_from_memory(name, filename, data, ispackage=False):
    if data[:4]!=imp.get_magic():
        raise ImportError('Bad magic number in %s' % filename)
    # Ignore timestamp in data[4:8]
    code = marshal.loads(data[8:])
    imp.acquire_lock() # Required in threaded applications
    try:
        mod = imp.new_module(name)
        sys.modules[name] = mod # To handle circular and submodule imports 
                                # it should come before exec.
        try:
            mod.__file__ = filename # Is not so important.
            # For package you have to set mod.__path__ here. 
            # Here I handle simple cases only.
            if ispackage:
                mod.__path__ = [name.replace('.', '/')]
            exec code in mod.__dict__
        except:
            del sys.modules[name]
            raise
    finally:
        imp.release_lock()
    return mod

Обновление : код обновлен для правильной обработки пакетов.

Обратите внимание, что вам необходимо установить ловушку импорта для обработки импорта внутри загруженных модулей. Один из способов сделать это - добавить искатель в sys.meta_path . См. PEP302 для получения дополнительной информации.

9
ответ дан 8 December 2019 в 02:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: