Dynamicznie importuj metodę w pliku, z ciągu

84

Mam ciąg, powiedzieć: abc.def.ghi.jkl.myfile.mymethod. Jak dynamicznie importować mymethod?

Oto jak to zrobiłem:

def get_method_from_file(full_path):
    if len(full_path) == 1:
        return map(__import__,[full_path[0]])[0]
    return getattr(get_method_from_file(full_path[:-1]),full_path[-1])


if __name__=='__main__':
    print get_method_from_file('abc.def.ghi.jkl.myfile.mymethod'.split('.'))

Zastanawiam się, czy w ogóle jest wymagane importowanie poszczególnych modułów.

Edycja: używam Pythona w wersji 2.6.5.

Lakshman Prasad
źródło

Odpowiedzi:

111

Od Pythona 2.7 możesz użyć funkcji importlib.import_module () . Możesz zaimportować moduł i uzyskać dostęp do zdefiniowanego w nim obiektu za pomocą następującego kodu:

from importlib import import_module

p, m = name.rsplit('.', 1)

mod = import_module(p)
met = getattr(mod, m)

met()
frm
źródło
2
Warto zauważyć, że dokumentacja Pythona zaleca używanie importlib.import_module()over __import__(): docs.python.org/2/library/functions.html#__import__ - dla wersji 2.7+.
BoltzmannBrain
4
import_module + rsplit = Jedyna prawdziwa droga.
Cecil Curry,
32

Nie musisz importować poszczególnych modułów. Wystarczy zaimportować moduł, z którego chcesz zaimportować nazwę i podać fromlistargument:

def import_from(module, name):
    module = __import__(module, fromlist=[name])
    return getattr(module, name)

Na przykład abc.def.ghi.jkl.myfile.mymethodnazwij tę funkcję jako

import_from("abc.def.ghi.jkl.myfile", "mymethod")

(Zauważ, że funkcje na poziomie modułu są w Pythonie nazywane funkcjami, a nie metodami).

W przypadku tak prostego zadania nie ma korzyści z korzystania z importlibmodułu.

Sven Marnach
źródło
import_from () wskazał mi właściwy kierunek. myClass = getattr(__import__("module.to.import", fromlist=["myClassName"]), "myClassName"). Dzięki za pomoc!
Fydo
1
To __import__podejście działa. Ale spróbuj biec help(__import__). Mówi się: „Ponieważ ta funkcja jest przeznaczona do użytku przez interpreter języka Python, a nie do ogólnego użytku, lepiej jest używać jej importlib.import_module()do programowego importu modułu”.
twasbrillig
5
@twasbrillig: Kiedy odpowiedziałem na to pytanie, Python 2.5 i 2.6 nadal były w powszechnym użyciu. Dziś z pewnością najlepiej jest użyć importlibzamiast tego.
Sven Marnach
1
Fajne dzięki. Dobrym pomysłem może być zaktualizowanie odpowiedzi, aby to odzwierciedlić.
twasbrillig
26

W przypadku Pythona <2.7 można użyć wbudowanej metody __ import__ :

__import__('abc.def.ghi.jkl.myfile.mymethod', fromlist=[''])

Dla Pythona> = 2.7 lub 3.1 dodano wygodną metodę importlib.import_module . Po prostu zaimportuj swój moduł w następujący sposób:

importlib.import_module('abc.def.ghi.jkl.myfile.mymethod')

Aktualizacja : Zaktualizowana wersja zgodnie z komentarzami ( muszę przyznać, że nie przeczytałem ciągu do zaimportowania do końca i przeoczyłem fakt, że należy importować metodę modułu, a nie sam moduł) :

Python <2,7:

mymethod = getattr(__import__("abc.def.ghi.jkl.myfile", fromlist=["mymethod"]))

Python> = 2.7:

mymethod = getattr(importlib.import_module("abc.def.ghi.jkl.myfile"), "mymethod")
gecco
źródło
6
Żadne z tych rozwiązań nie będzie działać, ponieważ pierwszy parametr musi być nazwą modułu w obu __import__()i importlib.import_module().
Kris Hardy
1
importlib.import_module („abc.def.ghi.jkl.myfile.mymethod”) nie będzie działać, ponieważ musisz określić „który moduł zaimportować w kategoriach bezwzględnych lub względnych”, a nie „kwalifikowaną” nazwę metody.
FRM
To oczywiście nie działa, ponieważ pierwszym parametrem do zaimportowania powinien być moduł, a nie łańcuch.
Lakshman Prasad
11

Nie jest jasne, co próbujesz zrobić z lokalną przestrzenią nazw. Zakładam, że chcesz tylko my_methodjako lokalny, piszący output = my_method()?

# This is equivalent to "from a.b.myfile import my_method"
the_module = importlib.import_module("a.b.myfile")
same_module = __import__("a.b.myfile")
# import_module() and __input__() only return modules
my_method = getattr(the_module, "my_method")

# or, more concisely,
my_method = getattr(__import__("a.b.myfile"), "my_method")
output = my_method()

Chociaż dodajesz tylko my_methoddo lokalnej przestrzeni nazw, ładujesz łańcuch modułów. Możesz przyjrzeć się zmianom, obserwując klawisze sys.modulesprzed i po imporcie. Mam nadzieję, że to jest jaśniejsze i dokładniejsze niż twoje inne odpowiedzi.

Aby uzyskać kompletność, w ten sposób dodajesz cały łańcuch.

# This is equivalent to "import a.b.myfile"
a = __import__("a.b.myfile")
also_a = importlib.import_module("a.b.myfile")
output = a.b.myfile.my_method()

# This is equivalent to "from a.b import myfile"
myfile = __import__("a.b.myfile", fromlist="a.b")
also_myfile = importlib.import_module("a.b.myfile", "a.b")
output = myfile.my_method()

I wreszcie, jeśli używasz __import__()i zmodyfikowałeś ścieżkę wyszukiwania po uruchomieniu programu, być może będziesz musiał użyć __import__(normal args, globals=globals(), locals=locals()). Dlaczego jest to złożona dyskusja.

Charles Merriam
źródło
Pierwszym przykładem the_modulei same_modulejest źle i produkować różne wyniki. Głosowanie przeciw.
sleepycal
7
from importlib import import_module

name = "file.py".strip('.py')
# if Path like : "path/python/file.py" 
# use name.replaces("/",".")

imp = import_module(name)

# get Class From File.py
model = getattr(imp, "classNameImportFromFile")

NClass = model() # Class From file 
Alaa Akiel
źródło
2
Dzięki za odpowiedź, tylko jeden mały problem: nie sądzę, aby .strip()zachowywał się poprawnie w przypadku, który podałeś. W szczególności, jeśli plik zaczyna się od „py”, te znaki również zostaną usunięte. Na przykład "pyfile.py".strip(".py")produkuje "file", gdzie byłoby preferowane, aby produkować "pyfile"w tym przypadku. name.replace(".py","")działa jednak dobrze.
jxmorris12
1

Ta strona ma fajne rozwiązanie: load_class . Używam tego w ten sposób:

foo = load_class(package.subpackage.FooClass)()
type(foo) # returns FooClass

Zgodnie z żądaniem, oto kod z łącza internetowego:

import importlib

def load_class(full_class_string):
    """
    dynamically load a class from a string
    """

    class_data = full_class_string.split(".")
    module_path = ".".join(class_data[:-1])
    class_str = class_data[-1]

    module = importlib.import_module(module_path)
    # Finally, we retrieve the Class
    return getattr(module, class_str)
kingaj
źródło
Prosimy o dołączenie podsumowania / kodu z podanego linku. Odpowiedzi zawierające tylko linki nie są świetne, ponieważ są podatne na rozkład linków.
Yet Another User
0

Użyj importlib(tylko w wersji 2.7+).

Daniel Roseman
źródło
1
Używam 2.6.5. Czy mogę coś zrobić from __future__?
Lakshman Prasad
5
Nie, __future__dotyczy funkcji językowych, a nie nowych modułów standardowej biblioteki.
ThiefMaster
1
Użyj wbudowanego trybu __import__jak w przykładach powyżej. Działa z powrotem do 2.5 i wcześniej bez słów kluczowych.
Charles Merriam
0

Sposób, w jaki zwykle to robię (a także wiele innych bibliotek, takich jak pylony i wklejanie, jeśli moja pamięć dobrze mi służy) polega na oddzieleniu nazwy modułu od nazwy funkcji / atrybutu za pomocą znaku „:” między nimi . Zobacz poniższy przykład:

'abc.def.ghi.jkl.myfile:mymethod'

To sprawia, że import_from(path)poniższa funkcja jest trochę łatwiejsza w użyciu.

def import_from(path):
    """
    Import an attribute, function or class from a module.
    :attr path: A path descriptor in the form of 'pkg.module.submodule:attribute'
    :type path: str
    """
    path_parts = path.split(':')
    if len(path_parts) < 2:
        raise ImportError("path must be in the form of pkg.module.submodule:attribute")
    module = __import__(path_parts[0], fromlist=path_parts[1])
    return getattr(module, path_parts[1])


if __name__=='__main__':
    func = import_from('a.b.c.d.myfile:mymethod')
    func()
Kris Hardy
źródło
1
Dlaczego ktokolwiek miałby to zrobić? Wymaga to od wywołującego połączenia nazwy modułu i nazwy atrybutu dwukropkiem, a funkcja musi ponownie rozdzielić ją w dwukropku. Dlaczego po prostu nie użyć dwóch parametrów w pierwszej kolejności?
Sven Marnach
1
Często zdarza się, że tak się dzieje, gdy łączysz wywołania zwrotne z frameworka do aplikacji za pomocą plików konfiguracyjnych (.yaml, .ini itp.). Następnie możesz określić, którą funkcję ma wywołać framework, itp., Używając pojedynczego ciągu znaków w pliku konfiguracyjnym.
Kris Hardy
Cóż, tak, możesz chcieć wybrać to jako składnię pliku konfiguracyjnego. Ale jak to się ma do pytania PO? (I nawet gdybym wybrał to jako składnię pliku konfiguracyjnego, wolałbym, aby był analizowany przez mój parser pliku konfiguracyjnego, a nie przez losowe funkcje API.)
Sven Marnach
-1

Co powiesz na to :

def import_module(name):

    mod = __import__(name)
    for s in name.split('.')[1:]:
        mod = getattr(mod, s)
    return mod
Aymen Alsaadi
źródło
Odradza się stosowanie metody magicznej. Zamiast tego użyj importlib.
dephinera
Czy możesz wyjaśnić, dlaczego jest to zniechęcające? @dephinera
Aymen Alsaadi
Cóż, ponieważ metody magiczne mają być wywoływane przez interpreter, a nie jawnie przez nasz kod. Nazywając je jawnie, polegamy na tym, że ich podpis nigdy się nie zmieni, jednak może się to zmienić w przyszłych wersjach językowych.
dephinera