Ищу советы по коду, используемому для динамического импорта функций из внешних файлов .py - PullRequest
1 голос
/ 19 июня 2019

У меня довольно обширный опыт программирования в разных средах, но я новичок в Python. Я подумал, что нашел способ динамического импорта функций из внешнего файла .py, но не уверен, что это лучший способ. Я обнаружил проблемы с использованием importlib.import_module() и importlib.__import__ в зависимости от цели. Есть ли другой способ сделать то, что я делаю здесь? По сути, я хочу получить тот же результат, что и при использовании from x import y, где x и y - переменные. Я думал, что смогу использовать eval('from '+x+' import '+y), но это приводит к синтаксической ошибке.

Я надеялся добиться этого, придумав словарь с файлами в качестве ключей (т. Е. Файл с именем 'file1.py' создаст ключ из 'file1') и список требуемых функций в виде списка, связанного с его относительный ключ. Это можно легко построить либо буквально, либо читая путь для имен файлов, а затем используя функцию dir (), чтобы получить список функций в каждом отдельном файле (среди многих других способов). Далее я надеялся просто использовать вложенные циклы for для обхода ключей словаря и связанных с ними списков значений ключей и использовать eval('from '+key+' import '+currentListItem). К сожалению, это вызывает синтаксическую ошибку при выполнении сгенерированного оператора from ... import .... Ниже приведен пример кода. Мои проблемы с importlib (и getattr) заключаются в том, что я не могу поддерживать «абстракцию», предоставляемую этим методом, так как мне приходится определять «дескриптор» для использования importlib (то есть handle = getattr(...) или handle = importlib.import_module(key), означая, что я в основном приходится жестко кодировать «дескриптор имени» для данного импортируемого модуля и, следовательно, может точно так же жестко кодировать операторы «из функции импорта файлового имени»).

# simplistic example of what I was thinking....
# FILE file.py contains the code...
def asub(p1, p2 = None, p3 = None):
    print(p1, p2 if p2 else 'p2 defaulted', p3 if p3 else 'p3 defaulted')
    return

# FILE b.py contains the code...

#!/usr/local/bin/python3
subs = {'file':['asub']}
for key in subs:
    for subrt in subs[key]:
        print("==>", key, subrt)
        eval('from '+key+' import '+subrt)

# on execution of b.py (i.e., ```python3 b.py``` I get the following...

Traceback (most recent call last):
  File "b.py", line 9, in <module>
    eval('from '+key+' import '+subrt)
  File "<string>", line 1
    from file import asub
       ^
SyntaxError: invalid syntax

ПРИМЕЧАНИЕ. Я понимаю, что это может быть не самой лучшей вещью для импорта модулей / функций, поскольку она имеет тенденцию «скрывать» информацию, которая понадобится вам для документирования и использования импортированных функций.

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

Как я уже сказал, я новичок в Python, поэтому я ищу обратную связь / руководство по этому вопросу в (крошечном) «питоническом» смысле.

Еще одна вещь: я понимаю, что eval () может открыть дверь для вставки кода, но для указанного выше конкретного использования, и, учитывая, что файлы, содержащие функции, хорошо заблокированы, я думаю, что это должно быть достаточно безопасно (?) ...

Заранее спасибо за любые отзывы

Ответы [ 2 ]

0 голосов
/ 20 июня 2019

К вашему сведению, Окончательная форма основана на принятом ответе, предоставленном Радославом. ПРИМЕЧАНИЕ: я также протестировал с importlib.import_module (ключ), и это работало так же, за исключением того, что вам, возможно, придется использовать sys.path.insert (...), чтобы убедиться, что ваш модуль может быть расположен.

import os, sys, re
from importlib.machinery import SourceFileLoader

# Build list of 'things' to be imported
#   (presumably via a lookup of some sort)
# I hard coded it here as a simple dictionary element for testing/dev.
# Notice that a fully qualified file name is used so that the target
#   subroutine/function can come from any specified/discovered file
# Another option may be to use only a list of file names here and then # use 'dir()' to get the functions from the file and then choose from
# the the list returned.  This is illustrated below.
subs = {'/some/fully/qualified/path/file.txt':['asub']}

# import the files
for key in subs:
    mod = SourceFileLoader("", key).load_module()

# Example of how to 'extract' the function names from the module for
# filtering and loading of explicit modules when using 'subs' as a
# list vs. a dictionary.  Here I simply excluded anything beginning
# and ending with double '_' and then processed those 'functions'.
#    for d in dir(mod):
#        if not re.match(r'^__.+?__$', d):
#            locals()[subrt] = getattr(mod, subrt)

# otherwise, if 'subs' is used as a dictionary as
# described above, just walk the list of functions
# built as part of the 'subs' dictionary
    for subrt in subs[key]:
        locals()[subrt] = getattr(mod, subrt)

# done
asub('parm1', 'p2_parm')

quit()

Интересное упражнение. Большое спасибо за помощь Радослав.

0 голосов
/ 20 июня 2019

Попробуйте:

from importlib.machinery import SourceFileLoader

subs = {'file':['asub']}

for key in subs:
    # import module by file path
    # since path is relative, it will import against working directory
    mod = SourceFileLoader("", "%s.py" % key).load_module()

    for subrt in subs[key]:
        print("==>", key, subrt)

        # retrieve object from module by name subrt
        obj = getattr(mod, subrt)

        # this should call file.asub and it does
        obj('1', '2')

        # this add local variable with the same name
        locals()[subrt] = obj

# so now we can call asub as if it were done
# from file import asub
asub('3', '4')

Этот код печатает:

==> file asub
1 2 p3 defaulted
3 4 p3 defaulted

Это работает на моем python3 и требует как минимум python 3.3.Это основано на вопросе Как импортировать модуль с полным путем? .locals()[subrt] = obj помещает импортированный объект в каталог локальных переменных, что позволяет обращаться к нему, как если бы это было сделано from file import asub.Если я правильно понял, это то, что вы хотели достичь?

...