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.

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()

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.


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")

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.


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 

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)

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

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()

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.