Nhập động một phương thức trong tệp, từ một chuỗi


84

Tôi có một chuỗi, nói: abc.def.ghi.jkl.myfile.mymethod. Làm cách nào để nhập động mymethod?

Đây là cách tôi đã làm về nó:

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

Tôi đang tự hỏi liệu việc nhập các mô-đun riêng lẻ có được yêu cầu hay không.

Chỉnh sửa: Tôi đang sử dụng phiên bản Python 2.6.5.

Câu trả lời:


111

Từ Python 2.7, bạn có thể sử dụng hàm importlib.import_module () . Bạn có thể nhập một mô-đun và truy cập một đối tượng được xác định bên trong nó bằng mã sau:

from importlib import import_module

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

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

met()

2
Đáng chú ý các tài liệu Python khuyên bạn sử dụng importlib.import_module()trên __import__(): docs.python.org/2/library/functions.html#__import__ - cho 2.7+.
BoltzmannBrain

4
import_module + rsplit = The One True Way.
Cecil Curry,

32

Bạn không cần phải nhập các mô-đun riêng lẻ. Chỉ cần nhập mô-đun bạn muốn nhập tên và cung cấp fromlistđối số:

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

Đối với ví dụ của bạn abc.def.ghi.jkl.myfile.mymethod, hãy gọi hàm này là

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

(Lưu ý rằng các hàm cấp mô-đun được gọi là hàm trong Python, không phải phương thức.)

Đối với một nhiệm vụ đơn giản như vậy, không có lợi thế trong việc sử dụng importlibmô-đun.


import_from () đã chỉ cho tôi đúng hướng. myClass = getattr(__import__("module.to.import", fromlist=["myClassName"]), "myClassName"). Cảm ơn đã giúp đỡ!
Fydo

1
Cách __import__tiếp cận thực sự hiệu quả. Nhưng hãy thử chạy help(__import__). Nó nói "Bởi vì chức năng này được sử dụng bởi trình thông dịch Python chứ không phải để sử dụng chung, tốt hơn nên sử dụng importlib.import_module()để nhập một mô-đun theo chương trình."
twasbrillig

5
@twasbrillig: Khi tôi trả lời câu hỏi này, Python 2.5 và 2.6 vẫn đang được sử dụng rộng rãi. Hôm nay, nó chắc chắn là tốt nhất để sử dụng importlibthay thế.
Sven Marnach

1
Tuyệt thật, cảm ơn nhé. Có thể là một ý tưởng tốt để cập nhật câu trả lời để phản ánh điều này.
twasbrillig

26

Đối với Python <2.7, phương thức nội trang __ import__ có thể được sử dụng:

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

Đối với Python> = 2.7 hoặc 3.1, phương thức thuận tiện importlib.import_module đã được thêm vào. Chỉ cần nhập mô-đun của bạn như sau:

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

Cập nhật : Phiên bản cập nhật theo nhận xét ( Tôi phải thừa nhận rằng tôi đã không đọc chuỗi được nhập cho đến cuối và tôi đã bỏ lỡ thực tế rằng một phương thức của một mô-đun nên được nhập chứ không phải chính một mô-đun) :

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
Cả hai giải pháp này sẽ không hoạt động vì tham số đầu tiên phải là tên mô-đun trong cả __import__()importlib.import_module().
Kris Hardy

1
importlib.import_module ('abc.def.ghi.jkl.myfile.mymethod') sẽ không hoạt động vì bạn phải chỉ định "mô-đun nào cần nhập theo điều kiện tuyệt đối hoặc tương đối" chứ không phải tên "đủ điều kiện" của phương thức.
frm

Điều này rõ ràng không hoạt động vì tham số đầu tiên để nhập phải là một mô-đun, không phải một chuỗi.
Lakshman Prasad

11

Không rõ bạn đang cố gắng làm gì với không gian tên cục bộ của mình. Tôi cho rằng bạn chỉ muốn my_methodnhư một người bản xứ, đang gõ 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()

Trong khi bạn chỉ thêm my_methodvào không gian tên cục bộ, bạn tải chuỗi mô-đun. Bạn có thể xem các thay đổi bằng cách xem các khóa sys.modulestrước và sau khi nhập. Tôi hy vọng điều này rõ ràng và chính xác hơn những câu trả lời khác của bạn.

Để hoàn chỉnh, đây là cách bạn thêm toàn bộ chuỗi.

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

Và, cuối cùng, nếu bạn đang sử dụng __import__()và đã sửa đổi đường dẫn tìm kiếm sau khi chương trình bắt đầu, bạn có thể cần sử dụng __import__(normal args, globals=globals(), locals=locals()). Tại sao là một cuộc thảo luận phức tạp.


Ví dụ đầu tiên của bạn về the_modulesame_modulesai và tạo ra các kết quả khác nhau. Phản đối.
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
Cảm ơn vì câu trả lời, chỉ một vấn đề nhỏ: Tôi nghĩ không .strip()cư xử đúng trong trường hợp bạn đã nêu. Cụ thể, nếu tệp bắt đầu bằng "py", các ký tự đó cũng sẽ bị xóa. Ví dụ, "pyfile.py".strip(".py")sản xuất "file", nơi nó được ưu tiên sản xuất "pyfile"trong trường hợp này. name.replace(".py","")hoạt động tốt, mặc dù.
jxmorris 12

1

Trang web này có một giải pháp hay: load_class . Tôi sử dụng nó như thế này:

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

Theo yêu cầu, đây là mã từ liên kết web:

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)

Vui lòng bao gồm tóm tắt / mã từ liên kết đã cho. Các câu trả lời chỉ có liên kết không tuyệt vời vì chúng dễ bị thối liên kết.
Yet Another User:

0

Sử dụng importlib(chỉ 2.7+).


1
Tôi sử dụng 2.6.5. Tôi có thể làm from __future__gì không?
Lakshman Prasad

5
Không, __future__dành cho các tính năng ngôn ngữ, không phải các mô-đun stdlib mới.
ThiefMaster

1
Sử dụng buit-in __import__như trong các ví dụ trên. Nó hoạt động trở lại 2,5 và trước đó không có từ khóa.
Charles Merriam

0

Cách tôi có xu hướng làm điều này (cũng như một số thư viện khác, chẳng hạn như giá treo và dán, nếu bộ nhớ của tôi phục vụ tôi một cách chính xác) là tách tên mô-đun khỏi tên hàm / thuộc tính bằng cách sử dụng dấu ':' giữa chúng . Xem ví dụ sau:

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

Điều này làm cho import_from(path)chức năng bên dưới dễ sử dụng hơn một chút.

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
Tại sao anybdoy nên làm điều này? Điều này yêu cầu người gọi nối tên mô-đun và tên thuộc tính bằng dấu hai chấm và hàm cần phải tách lại ở dấu hai chấm. Tại sao không chỉ sử dụng hai tham số ngay từ đầu?
Sven Marnach

1
Thông thường, một tình huống mà điều này sẽ xảy ra là nếu bạn chuyển các cuộc gọi lại từ một khung công tác tới ứng dụng của mình bằng các tệp cấu hình (.yaml, .ini, v.v.). Sau đó, bạn có thể chỉ định hàm mà khuôn khổ sẽ gọi, v.v., bằng một chuỗi đơn trong tệp cấu hình của bạn.
Kris Hardy

Vâng, bạn có thể muốn chọn đây làm cú pháp tệp cấu hình. Nhưng điều này có liên quan như thế nào đến câu hỏi của OP? (Và ngay cả khi tôi đã chọn này như cú pháp tập tin cấu hình của tôi, tôi muốn có nó phân tích bằng phân tích cú pháp tập tin cấu hình của tôi, chứ không phải bởi các chức năng API ngẫu nhiên.)
Sven Marnach

-1

Còn cái này thì sao :

def import_module(name):

    mod = __import__(name)
    for s in name.split('.')[1:]:
        mod = getattr(mod, s)
    return mod

Không khuyến khích sử dụng phương pháp ma thuật. Sử dụng importlib để thay thế.
dephinera

Bạn có thể giải thích tại sao nó không khuyến khích? @dephinera
Aymen Alsaadi

Bởi vì các phương thức ma thuật nhằm mục đích được gọi bởi trình thông dịch, không phải mã của chúng tôi một cách rõ ràng. Bằng cách gọi họ một cách rõ ràng, chúng tôi tin rằng chữ ký của họ sẽ không bao giờ thay đổi, tuy nhiên nó có thể thay đổi trong các phiên bản ngôn ngữ trong tương lai.
dephinera
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.