Làm thế nào để nhập một mô-đun cho đường dẫn đầy đủ?


1141

Làm cách nào tôi có thể tải mô-đun Python với đường dẫn đầy đủ của nó? Lưu ý rằng tệp có thể ở bất kỳ đâu trong hệ thống tệp, vì đây là tùy chọn cấu hình.


21
Câu hỏi hay và đơn giản - và câu trả lời hữu ích nhưng chúng khiến tôi tự hỏi điều gì đã xảy ra với câu thần chú trăn "Có một cách rõ ràng " để làm điều đó .. Nó không giống bất cứ điều gì giống như một câu trả lời đơn giản và rõ ràng cho nó. Có vẻ như vô lý hacky và phụ thuộc phiên bản cho một hoạt động cơ bản như vậy (và nó trông và cồng kềnh hơn trong các phiên bản mới hơn ..).
inger

Câu trả lời:


1282

Đối với Python 3.5+ sử dụng:

import importlib.util
spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
foo.MyClass()

Đối với Python 3.3 và 3.4, hãy sử dụng:

from importlib.machinery import SourceFileLoader

foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()

(Mặc dù điều này đã không được chấp nhận trong Python 3.4.)

Đối với Python 2 sử dụng:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Có các hàm tiện lợi tương đương cho các tệp Python và DLL được biên dịch.

Xem thêm http://bugs.python.org/su21436 .


53
Nếu tôi biết không gian tên - 'module.name' - tôi sẽ sử dụng __import__.
Sridhar Ratnakumar

62
@SridharRatnakumar giá trị của đối số đầu tiên imp.load_sourcechỉ đặt .__name__mô-đun trả về. nó không có hiệu lực tải.
Dan D.

17
@DanD. - đối số đầu tiên imp.load_source()xác định khóa của mục nhập mới được tạo trong sys.modulestừ điển, vì vậy đối số đầu tiên thực sự ảnh hưởng đến việc tải.
Brandon Rhodes

9
Các impmô-đun bị phản đối kể từ phiên bản 3.4: Các impgói đang chờ deprecation ủng hộ importlib.
Chiel ten Brinke

9
@AXO và nhiều hơn nữa đến điểm người ta tự hỏi tại sao một cái gì đó đơn giản và cơ bản như thế này lại phải phức tạp đến vậy. Nó không có trong nhiều ngôn ngữ khác.
đá

423

Ưu điểm của việc thêm đường dẫn đến sys.path (sử dụng imp) là nó đơn giản hóa mọi thứ khi nhập nhiều hơn một mô-đun từ một gói. Ví dụ:

import sys
# the mock-0.3.1 dir contains testcase.py, testutils.py & mock.py
sys.path.append('/foo/bar/mock-0.3.1')

from testcase import TestCase
from testutils import RunTests
from mock import Mock, sentinel, patch

13
Làm thế nào để chúng ta sử dụng sys.path.appendđể trỏ đến một tập tin python duy nhất thay vì một thư mục?
Phani

28
:-) Có lẽ câu hỏi của bạn sẽ phù hợp hơn với câu hỏi StackOverflow, không phải là nhận xét về câu trả lời.
Daryl Spitzer

3
Đường dẫn trăn có thể chứa tài liệu lưu trữ zip, "trứng" (một loại lưu trữ zip phức tạp), v.v ... Các mô-đun có thể được nhập từ chúng. Vì vậy, các thành phần đường dẫn thực sự là các thùng chứa các tệp, nhưng chúng không nhất thiết phải là các thư mục.
alexis

12
Cảnh giác với thực tế là Python lưu trữ các câu lệnh nhập khẩu. Trong trường hợp hiếm hoi mà bạn có hai thư mục khác nhau chia sẻ một tên lớp (classX), cách tiếp cận thêm đường dẫn đến sys.path, nhập classX, xóa đường dẫn và lặp lại cho các đường dẫn phát lại sẽ không hiệu quả. Python sẽ luôn tải lớp từ đường dẫn đầu tiên từ bộ đệm của nó. Trong trường hợp của tôi, tôi nhằm mục đích tạo ra một hệ thống plugin trong đó tất cả các plugin triển khai một classX cụ thể. Tôi đã kết thúc bằng SourceFileLoader , lưu ý rằng sự phản đối của nó đang gây tranh cãi .
ComFalet

3
Lưu ý cách tiếp cận này cho phép mô-đun được nhập để nhập các mô-đun khác từ cùng một thư mục, điều mà các mô-đun thường làm, trong khi cách tiếp cận của câu trả lời được chấp nhận thì không (ít nhất là trên 3.7). importlib.import_module(mod_name)có thể được sử dụng thay vì nhập rõ ràng ở đây nếu tên mô-đun không được biết đến trong thời gian chạy sys.path.pop(), tuy nhiên, tôi sẽ thêm một mã nhập vào không cố gắng nhập thêm các mô-đun khi nó được sử dụng.
Eli_B

43

Nếu mô-đun cấp cao nhất của bạn không phải là tệp nhưng được đóng gói dưới dạng thư mục có __init__.py, thì giải pháp được chấp nhận gần như hoạt động, nhưng không hoàn toàn. Trong Python 3.5+, đoạn mã sau là cần thiết (lưu ý dòng được thêm bắt đầu bằng 'sys.modules'):

MODULE_PATH = "/path/to/your/module/__init__.py"
MODULE_NAME = "mymodule"
import importlib
import sys
spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module 
spec.loader.exec_module(module)

Nếu không có dòng này, khi exec_module được thực thi, nó sẽ cố gắng ràng buộc nhập khẩu tương đối ở cấp cao nhất của bạn __init__.py với tên mô-đun cấp cao nhất - trong trường hợp này là "mymodule". Nhưng "mymodule" chưa được tải nên bạn sẽ gặp lỗi "SystemError: Mô-đun mẹ 'mymodule' không được tải, không thể thực hiện nhập tương đối". Vì vậy, bạn cần phải ràng buộc tên trước khi bạn tải nó. Lý do cho điều này là bất biến cơ bản của hệ thống nhập tương đối: "Việc giữ bất biến là nếu bạn có sys.modules ['spam'] và sys.modules ['spam.foo'] (như bạn sẽ thực hiện sau khi nhập ở trên ), cái sau phải xuất hiện dưới dạng thuộc tính foo của cái trước " như được thảo luận ở đây .


Cảm ơn rất nhiều! Phương pháp này cho phép nhập khẩu tương đối giữa các mô hình con. Tuyệt quá!
tebanep

Câu trả lời này phù hợp với tài liệu ở đây: docs.python.org/3/l Library / .
Tim Ludwinski

1
nhưng là mymodule
Gulzar

@Gulzar, đó là bất kỳ tên nào bạn muốn đặt cho mô-đun của mình, để sau này bạn có thể thực hiện: "từ mymodule nhập mygroup"
Idodo

Vậy ... /path/to/your/module/có thật /path/to/your/PACKAGE/không? và mymoduleý bạn là myfile.pygì?
Gulzar

37

Để nhập mô-đun của bạn, bạn cần thêm thư mục của nó vào biến môi trường, tạm thời hoặc vĩnh viễn.

Một cách tạm thời

import sys
sys.path.append("/path/to/my/modules/")
import my_module

Vĩnh viễn

Thêm dòng sau vào .bashrctệp của bạn (trong linux) và thực hiện source ~/.bashrctrong thiết bị đầu cuối:

export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"

Tín dụng / Nguồn: saarrrr , một câu hỏi stackexchange khác


3
Giải pháp "tạm thời" này là một câu trả lời tuyệt vời nếu bạn muốn đưa ra một dự án xung quanh trong một máy tính xách tay jupyter ở nơi khác.
từ

Nhưng ... thật nguy hiểm khi can thiệp vào con đường
Shai Alon

@ShaiAlon Bạn đang thêm đường dẫn, vì vậy không có nguy hiểm nào khác ngoài khi bạn chuyển mã từ máy tính này sang máy tính khác, đường dẫn có thể bị rối. Vì vậy, để phát triển gói, tôi chỉ nhập gói địa phương. Ngoài ra, tên gói nên là duy nhất. Nếu bạn lo lắng, hãy sử dụng giải pháp tạm thời.
Miladiouss

28

Có vẻ như bạn không muốn nhập cụ thể tệp cấu hình (có rất nhiều tác dụng phụ và các biến chứng bổ sung có liên quan), bạn chỉ muốn chạy nó và có thể truy cập vào không gian tên kết quả. Thư viện tiêu chuẩn cung cấp một API cụ thể cho dạng đó dưới dạng runpy.run_path :

from runpy import run_path
settings = run_path("/path/to/file.py")

Giao diện đó có sẵn trong Python 2.7 và Python 3.2+


Tôi thích phương pháp này nhưng khi tôi nhận được kết quả của run_path thì đó là một từ điển mà tôi dường như không thể truy cập?
Stephen Ellwood

Bạn có ý nghĩa gì bởi "không thể truy cập"? Bạn không thể nhập từ nó (đó là lý do đây chỉ là một lựa chọn tốt khi truy cập nhập phong cách không thực sự cần thiết), nhưng nội dung nên có sẵn qua API dict thường xuyên ( result[name], result.get('name', default_value), vv)
ncoghlan

Câu trả lời này là cách đánh giá thấp. Nó rất ngắn và đơn giản! Thậm chí tốt hơn, nếu bạn cần một không gian tên mô-đun thích hợp, bạn có thể làm một cái gì đó như from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
RuRo

20

Bạn cũng có thể làm một cái gì đó như thế này và thêm thư mục mà tệp cấu hình đang ngồi vào đường dẫn tải Python, sau đó chỉ cần thực hiện nhập thông thường, giả sử bạn biết trước tên của tệp, trong trường hợp này là "config".

Lộn xộn, nhưng nó hoạt động.

configfile = '~/config.py'

import os
import sys

sys.path.append(os.path.dirname(os.path.expanduser(configfile)))

import config

Đó không phải là động.
Shai Alon

Tôi đã thử: config_file = 'setup-for-chats', setup_file = get_setup_file (config_file + ".py"), sys.path.append (os.path.dirname (os.path.apidanduser (setup_file)), nhập khẩu >> "ImportError: Không có mô-đun có tên config_file"
Shai Alon

17

Bạn có thể dùng

load_source(module_name, path_to_file) 

phương pháp từ mô đun imp .


... Và imp.load_dynamic(module_name, path_to_file)đối với DLL
HEKTO

34
đứng lên mà imp bị phản đối bây giờ
t1m0

13
def import_file(full_path_to_module):
    try:
        import os
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)
        save_cwd = os.getcwd()
        os.chdir(module_dir)
        module_obj = __import__(module_name)
        module_obj.__file__ = full_path_to_module
        globals()[module_name] = module_obj
        os.chdir(save_cwd)
    except:
        raise ImportError

import_file('/home/somebody/somemodule.py')

37
Tại sao phải viết 14 dòng mã lỗi khi thư viện chuẩn đã được xử lý? Bạn chưa thực hiện kiểm tra lỗi trên định dạng hoặc nội dung của full_path_to_module hoặc các hoạt động os.whthing; và sử dụng except:mệnh đề Catch-all hiếm khi là một ý tưởng tốt.
Chris Johnson

Bạn nên sử dụng nhiều "thử cuối cùng" ở đây. Ví dụ:save_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
kay - SE là ác

11
@ChrisJohnson this is already addressed by the standard libraryyeah, nhưng python có thói quen khó chịu là không tương thích ngược ... vì câu trả lời được kiểm tra cho biết có 2 cách khác nhau trước và sau 3.3. Trong trường hợp đó, tôi muốn viết chức năng phổ quát của riêng mình hơn là kiểm tra phiên bản nhanh chóng. Và vâng, có thể mã này không được bảo vệ quá tốt, nhưng nó cho thấy một ý tưởng (đó là os.chdir (), tôi chưa hiểu về nó), dựa vào đó tôi có thể viết mã tốt hơn. Do đó +1.
Sushi271

13

Đây là một số mã hoạt động trong tất cả các phiên bản Python, từ 2.7-3.5 và có thể cả những mã khác.

config_file = "/tmp/config.py"
with open(config_file) as f:
    code = compile(f.read(), config_file, 'exec')
    exec(code, globals(), locals())

Tôi đã thử nó. Nó có thể xấu nhưng cho đến nay là người duy nhất hoạt động trong tất cả các phiên bản.


1
Câu trả lời này có hiệu quả đối với tôi khi load_sourcekhông nhập tập lệnh và cung cấp quyền truy cập tập lệnh vào các mô-đun và toàn cầu tại thời điểm nhập.
Klik

13

Tôi đã đưa ra một phiên bản sửa đổi nhỏ của câu trả lời tuyệt vời @ SebastianRittau (đối với Python> 3,4 tôi nghĩ), sẽ cho phép bạn tải một tệp với bất kỳ tiện ích mở rộng nào dưới dạng mô-đun bằng cách sử dụng spec_from_loaderthay vì spec_from_file_location:

from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader 

spec = spec_from_loader("module.name", SourceFileLoader("module.name", "/path/to/file.py"))
mod = module_from_spec(spec)
spec.loader.exec_module(mod)

Ưu điểm của việc mã hóa đường dẫn một cách rõ ràng SourceFileLoadermáy móc sẽ không cố gắng tìm ra loại tệp từ phần mở rộng. Điều này có nghĩa là bạn có thể tải một cái gì đó như một .txttệp bằng phương pháp này, nhưng bạn không thể làm điều đó spec_from_file_locationmà không chỉ định trình tải vì .txtkhông có trong importlib.machinery.SOURCE_SUFFIXES.


13

Bạn có nghĩa là tải hoặc nhập khẩu?

Bạn có thể thao tác sys.pathdanh sách chỉ định đường dẫn đến mô-đun của bạn, sau đó nhập mô-đun của bạn. Ví dụ, được cung cấp một mô-đun tại:

/foo/bar.py

Bạn có thể làm:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar

1
@Wheat Tại sao sys.path [0: 0] thay vì sys.path [0]?
dùng618677

5
B / c sys.path [0] = xy ghi đè lên mục đường dẫn đầu tiên trong khi đường dẫn [0: 0] = xy tương đương với path.insert (0, xy)
dom0

2
hm the path.insert làm việc cho tôi nhưng thủ thuật [0: 0] thì không.
JSH

11
sys.path[0:0] = ['/foo']
Kevin Edwards

6
Explicit is better than implicit.Vậy tại sao không sys.path.insert(0, ...)thay thế sys.path[0:0]?
winklerrr

8

Tôi tin rằng bạn có thể sử dụng imp.find_module()imp.load_module()để tải các mô-đun được chỉ định. Bạn sẽ cần tách tên mô-đun khỏi đường dẫn, tức là nếu bạn muốn tải, /home/mypath/mymodule.pybạn cần phải làm:

imp.find_module('mymodule', '/home/mypath/')

... nhưng điều đó sẽ hoàn thành công việc.


6

Bạn có thể sử dụng pkgutilmô-đun (cụ thể là walk_packagesphương thức) để lấy danh sách các gói trong thư mục hiện tại. Từ đó, việc sử dụng importlibmáy móc để nhập các mô-đun bạn muốn là chuyện nhỏ:

import pkgutil
import importlib

packages = pkgutil.walk_packages(path='.')
for importer, name, is_package in packages:
    mod = importlib.import_module(name)
    # do whatever you want with module now, it's been imported!

5

Tạo mô-đun python test.txt

import sys
sys.path.append("<project-path>/lib/")
from tes1 import Client1
from tes2 import Client2
import tes3

Tạo mô-đun python test_check.py

from test import Client1
from test import Client2
from test import test3

Chúng ta có thể nhập mô-đun nhập từ mô-đun.


4

Khu vực này của Python 3,4 dường như cực kỳ khó hiểu! Tuy nhiên, với một chút hack bằng cách sử dụng mã từ Chris Calloway khi bắt đầu, tôi đã xoay sở để có được thứ gì đó hoạt động. Đây là chức năng cơ bản.

def import_module_from_file(full_path_to_module):
    """
    Import a module given the full path/filename of the .py file

    Python 3.4

    """

    module = None

    try:

        # Get module name and path from full path
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)

        # Get module "spec" from filename
        spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)

        module = spec.loader.load_module()

    except Exception as ec:
        # Simple error printing
        # Insert "sophisticated" stuff here
        print(ec)

    finally:
        return module

Điều này dường như sử dụng các mô-đun không phản đối từ Python 3.4. Tôi không giả vờ hiểu tại sao, nhưng nó dường như hoạt động từ trong một chương trình. Tôi thấy giải pháp của Chris hoạt động trên dòng lệnh nhưng không phải từ bên trong một chương trình.


4

Tôi không nói rằng nó tốt hơn, nhưng để hoàn thiện, tôi muốn đề xuất execchức năng, có sẵn trong cả python 2 và 3. execcho phép bạn thực thi mã tùy ý trong phạm vi toàn cầu hoặc trong phạm vi nội bộ, cung cấp như một từ điển.

Ví dụ: nếu bạn có một mô-đun được lưu trữ trong "/path/to/module"với chức năng foo(), bạn có thể chạy nó bằng cách làm như sau:

module = dict()
with open("/path/to/module") as f:
    exec(f.read(), module)
module['foo']()

Điều này làm cho rõ ràng hơn một chút rằng bạn đang tải mã động và cấp cho bạn một số sức mạnh bổ sung, chẳng hạn như khả năng cung cấp nội dung tùy chỉnh.

Và nếu việc truy cập thông qua các thuộc tính, thay vì các khóa là quan trọng đối với bạn, bạn có thể thiết kế một lớp chính tả tùy chỉnh cho toàn cầu, cung cấp quyền truy cập đó, ví dụ:

class MyModuleClass(dict):
    def __getattr__(self, name):
        return self.__getitem__(name)

4

Để nhập mô-đun từ một tên tệp đã cho, bạn có thể tạm thời mở rộng đường dẫn và khôi phục đường dẫn hệ thống trong tham chiếu khối cuối cùng :

filename = "directory/module.py"

directory, module_name = os.path.split(filename)
module_name = os.path.splitext(module_name)[0]

path = list(sys.path)
sys.path.insert(0, directory)
try:
    module = __import__(module_name)
finally:
    sys.path[:] = path # restore

3

Điều này sẽ làm việc

path = os.path.join('./path/to/folder/with/py/files', '*.py')
for infile in glob.glob(path):
    basename = os.path.basename(infile)
    basename_without_extension = basename[:-3]

    # http://docs.python.org/library/imp.html?highlight=imp#module-imp
    imp.load_source(basename_without_extension, infile)

4
Một cách tổng quát hơn để cắt phần mở rộng ra là : name, ext = os.path.splitext(os.path.basename(infile)). Phương pháp của bạn hoạt động vì hạn chế trước đó đối với phần mở rộng .py. Ngoài ra, có lẽ bạn nên nhập mô-đun vào một số mục từ điển / biến.
ReneSac

3

Nếu chúng ta có các tập lệnh trong cùng một dự án nhưng trong các thư mục khác nhau, chúng ta có thể giải quyết vấn đề này bằng phương pháp sau.

Trong tình huống utils.pynày là trongsrc/main/util/

import sys
sys.path.append('./')

import src.main.util.utils
#or
from src.main.util.utils import json_converter # json_converter is example method

2

Tôi đã thực hiện một gói sử dụng impcho bạn. Tôi gọi nó import_filevà đây là cách nó được sử dụng:

>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')

Bạn có thể lấy nó tại:

http://pypi.python.org/pypi/import_file

hoặc tại

http://code.google.com.vn/p/import-file/


1
os.chdir? (ký tự tối thiểu để phê duyệt nhận xét).
ychaouche

Tôi đã dành cả ngày để khắc phục lỗi nhập trong trình cài đặt pyinstaller. Cuối cùng, đây là điều duy nhất làm việc cho tôi. Cảm ơn bạn rất nhiều vì đã làm điều này!
frakman1

2

Nhập mô-đun gói trong thời gian chạy (công thức Python)

http://code.activestate.com/recipes/223972/

###################
##                #
## classloader.py #
##                #
###################

import sys, types

def _get_mod(modulePath):
    try:
        aMod = sys.modules[modulePath]
        if not isinstance(aMod, types.ModuleType):
            raise KeyError
    except KeyError:
        # The last [''] is very important!
        aMod = __import__(modulePath, globals(), locals(), [''])
        sys.modules[modulePath] = aMod
    return aMod

def _get_func(fullFuncName):
    """Retrieve a function object from a full dotted-package name."""

    # Parse out the path, module, and function
    lastDot = fullFuncName.rfind(u".")
    funcName = fullFuncName[lastDot + 1:]
    modPath = fullFuncName[:lastDot]

    aMod = _get_mod(modPath)
    aFunc = getattr(aMod, funcName)

    # Assert that the function is a *callable* attribute.
    assert callable(aFunc), u"%s is not callable." % fullFuncName

    # Return a reference to the function itself,
    # not the results of the function.
    return aFunc

def _get_class(fullClassName, parentClass=None):
    """Load a module and retrieve a class (NOT an instance).

    If the parentClass is supplied, className must be of parentClass
    or a subclass of parentClass (or None is returned).
    """
    aClass = _get_func(fullClassName)

    # Assert that the class is a subclass of parentClass.
    if parentClass is not None:
        if not issubclass(aClass, parentClass):
            raise TypeError(u"%s is not a subclass of %s" %
                            (fullClassName, parentClass))

    # Return a reference to the class itself, not an instantiated object.
    return aClass


######################
##       Usage      ##
######################

class StorageManager: pass
class StorageManagerMySQL(StorageManager): pass

def storage_object(aFullClassName, allOptions={}):
    aStoreClass = _get_class(aFullClassName, StorageManager)
    return aStoreClass(allOptions)

2

Trong Linux, việc thêm một liên kết tượng trưng trong thư mục tập lệnh python của bạn được đặt.

I E:

ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py

trăn sẽ tạo ra /absolute/path/to/script/module.pyc và sẽ cập nhật nó nếu bạn thay đổi nội dung của/absolute/path/to/module/module.py

sau đó bao gồm các mục sau trong mypythonscript.py

from module import *

1
Đây là bản hack tôi đã sử dụng và nó đã gây ra cho tôi một số vấn đề. Một trong những điều đau đớn hơn là IDEA có một vấn đề trong đó nó không nhận được mã đã thay đổi từ bên trong liên kết, nhưng vẫn cố lưu những gì nó nghĩ là có. Một điều kiện cuộc đua mà cuối cùng để cứu là những gì dính vào ... Tôi đã mất một lượng công việc kha khá vì điều này.
Nắm chặt

@Gripp không chắc tôi có hiểu vấn đề của bạn không, nhưng tôi thường xuyên (hầu như độc quyền) chỉnh sửa tập lệnh của mình trên máy chủ từ xa từ máy tính để bàn thông qua SFTP với một máy khách như CyberDuck, và trong trường hợp đó cũng là một ý tưởng tồi để thử và chỉnh sửa tệp symlinked, thay vào đó an toàn hơn nhiều để chỉnh sửa tệp gốc. Bạn có thể nắm bắt một số vấn đề này bằng cách sử dụng gitvà kiểm tra git statusđể xác minh rằng các thay đổi của bạn đối với tập lệnh thực sự khiến nó trở lại tài liệu nguồn và không bị mất trong ether.
dùng5359531

2

Tôi đã viết chức năng nhập toàn cầu và di động của riêng mình, dựa trên importlibmô-đun, cho:

  • Có thể nhập cả hai mô-đun dưới dạng mô hình con và để nhập nội dung của mô-đun vào mô-đun mẹ (hoặc vào toàn cầu nếu không có mô-đun mẹ).
  • Có thể nhập các mô-đun với một ký tự dấu chấm trong tên tệp.
  • Có thể nhập các mô-đun với bất kỳ phần mở rộng.
  • Có thể sử dụng tên độc lập cho một mô hình con thay vì tên tệp mà không có phần mở rộng theo mặc định.
  • Có thể xác định thứ tự nhập dựa trên mô-đun đã nhập trước đó thay vì phụ thuộc vào sys.path hoặc lưu trữ đường dẫn tìm kiếm.

Cấu trúc thư mục ví dụ:

<root>
 |
 +- test.py
 |
 +- testlib.py
 |
 +- /std1
 |   |
 |   +- testlib.std1.py
 |
 +- /std2
 |   |
 |   +- testlib.std2.py
 |
 +- /std3
     |
     +- testlib.std3.py

Phụ thuộc và trật tự bao gồm:

test.py
  -> testlib.py
    -> testlib.std1.py
      -> testlib.std2.py
    -> testlib.std3.py 

Thực hiện:

Cửa hàng thay đổi mới nhất: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py

kiểm tra :

import os, sys, inspect, copy

SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("test::SOURCE_FILE: ", SOURCE_FILE)

# portable import to the global space
sys.path.append(TACKLELIB_ROOT) # TACKLELIB_ROOT - path to the library directory
import tacklelib as tkl

tkl.tkl_init(tkl)

# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()

tkl_import_module(SOURCE_DIR, 'testlib.py')

print(globals().keys())

testlib.base_test()
testlib.testlib_std1.std1_test()
testlib.testlib_std1.testlib_std2.std2_test()
#testlib.testlib.std3.std3_test()                             # does not reachable directly ...
getattr(globals()['testlib'], 'testlib.std3').std3_test()     # ... but reachable through the `globals` + `getattr`

tkl_import_module(SOURCE_DIR, 'testlib.py', '.')

print(globals().keys())

base_test()
testlib_std1.std1_test()
testlib_std1.testlib_std2.std2_test()
#testlib.std3.std3_test()                                     # does not reachable directly ...
globals()['testlib.std3'].std3_test()                         # ... but reachable through the `globals` + `getattr`

testlib.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("1 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std1', 'testlib.std1.py', 'testlib_std1')

# SOURCE_DIR is restored here
print("2 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std3', 'testlib.std3.py')

print("3 testlib::SOURCE_FILE: ", SOURCE_FILE)

def base_test():
  print('base_test')

testlib.std1.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std1::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/../std2', 'testlib.std2.py', 'testlib_std2')

def std1_test():
  print('std1_test')

testlib.std2.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std2::SOURCE_FILE: ", SOURCE_FILE)

def std2_test():
  print('std2_test')

testlib.std3.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std3::SOURCE_FILE: ", SOURCE_FILE)

def std3_test():
  print('std3_test')

Đầu ra ( 3.7.4):

test::SOURCE_FILE:  <root>/test01/test.py
import : <root>/test01/testlib.py as testlib -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib'])
base_test
std1_test
std2_test
std3_test
import : <root>/test01/testlib.py as . -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib', 'testlib_std1', 'testlib.std3', 'base_test'])
base_test
std1_test
std2_test
std3_test

Đã thử nghiệm trên Python 3.7.4 , 3.2.5.2.7.16

Ưu :

  • Có thể nhập cả hai mô-đun dưới dạng mô hình con và có thể nhập nội dung của mô-đun vào mô-đun mẹ (hoặc vào toàn cầu nếu không có mô-đun mẹ).
  • Có thể nhập các mô-đun với dấu chấm trong một tên tệp.
  • Có thể nhập bất kỳ mô-đun mở rộng từ bất kỳ mô-đun mở rộng.
  • Có thể sử dụng một tên độc lập cho một submodule thay vì một tên file mà không cần mở rộng mà là theo mặc định (ví dụ testlib.std.pynhư testlib, testlib.blabla.pynhưtestlib_blabla , v.v.).
  • Không phụ thuộc vào sys.path hoặc vào một lưu trữ đường dẫn tìm kiếm.
  • Không yêu cầu lưu / khôi phục các biến toàn cục như SOURCE_FILESOURCE_DIRgiữa các lệnh gọi đếntkl_import_module .
  • [cho 3.4.xvà cao hơn] Có thể trộn các không gian tên mô-đun trong các tkl_import_modulecuộc gọi lồng nhau (ví dụ: named->local->namedhoặclocal->named->local vv).
  • [cho 3.4.xvà cao hơn] Có thể tự động xuất các biến / hàm / lớp toàn cầu từ nơi được khai báo cho tất cả các mô đun con được nhập thông qua tkl_import_module(thông qua tkl_declare_globalhàm).

Nhược điểm :

  • [cho 3.3.xvà thấp hơn] Yêu cầu khai báo tkl_import_moduletrong tất cả các mô-đun gọi tkl_import_module(sao chép mã)

Cập nhật 1,2 (chỉ dành cho 3.4.xvà cao hơn):

Trong Python 3.4 trở lên, bạn có thể bỏ qua yêu cầu khai báo tkl_import_moduletrong mỗi mô-đun bằng cách khai báo tkl_import_moduletrong mô-đun cấp cao nhất và hàm sẽ tự tiêm cho tất cả các mô-đun con trong một cuộc gọi (đó là một kiểu nhập tự triển khai).

Cập nhật 3 :

Đã thêm chức năng tkl_source_moduletương tự như bash sourcevới bảo vệ thực thi hỗ trợ khi nhập (được thực hiện thông qua hợp nhất mô-đun thay vì nhập).

Cập nhật 4 :

Đã thêm chức năng tkl_declare_globalđể tự động xuất một biến toàn cục mô-đun cho tất cả các mô-đun con trong đó biến toàn cục mô-đun không hiển thị vì không phải là một phần của mô-đun con.

Cập nhật 5 :

Tất cả các chức năng đã được chuyển vào thư viện tacklelib, xem liên kết ở trên.


2

Có một gói dành riêng cho việc này cụ thể:

from thesmuggler import smuggle

# À la `import weapons`
weapons = smuggle('weapons.py')

# À la `from contraband import drugs, alcohol`
drugs, alcohol = smuggle('drugs', 'alcohol', source='contraband.py')

# À la `from contraband import drugs as dope, alcohol as booze`
dope, booze = smuggle('drugs', 'alcohol', source='contraband.py')

Nó đã được thử nghiệm trên các phiên bản Python (Jython và PyPy), nhưng nó có thể quá mức tùy thuộc vào quy mô dự án của bạn.


1

Thêm phần này vào danh sách các câu trả lời vì tôi không thể tìm thấy bất cứ điều gì hiệu quả. Điều này sẽ cho phép nhập các mô-đun python đã biên dịch (pyd) trong 3.4:

import sys
import importlib.machinery

def load_module(name, filename):
    # If the Loader finds the module name in this list it will use
    # module_name.__file__ instead so we need to delete it here
    if name in sys.modules:
        del sys.modules[name]
    loader = importlib.machinery.ExtensionFileLoader(name, filename)
    module = loader.load_module()
    locals()[name] = module
    globals()[name] = module

load_module('something', r'C:\Path\To\something.pyd')
something.do_something()

1

Cách khá đơn giản: giả sử bạn muốn nhập tệp với đường dẫn tương đối ../../MyLibs/pyfunc.py


libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf

Nhưng nếu bạn làm nó mà không có người bảo vệ, cuối cùng bạn cũng có thể đi được một con đường rất dài


1

Một giải pháp đơn giản sử dụng importlibthay vì impgói (đã thử nghiệm cho Python 2.7, mặc dù nó cũng hoạt động với Python 3):

import importlib

dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
sys.path.append(dirname) # only directories should be added to PYTHONPATH
module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")

Bây giờ bạn có thể trực tiếp sử dụng không gian tên của mô-đun đã nhập, như thế này:

a = module.myvar
b = module.myfunc(a)

Ưu điểm của giải pháp này là chúng tôi thậm chí không cần biết tên thực của mô-đun mà chúng tôi muốn nhập để sử dụng nó trong mã của mình. Điều này rất hữu ích, ví dụ trong trường hợp đường dẫn của mô-đun là một đối số có thể định cấu hình.


Bằng cách này, bạn đang sửa đổi sys.path, không phù hợp với mọi trường hợp sử dụng.
bgusach

@bgusach Điều này có thể đúng, nhưng nó cũng được mong muốn trong một số trường hợp (thêm đường dẫn đến sys.path đơn giản hóa mọi thứ khi nhập nhiều hơn một mô-đun từ một gói). Ở bất cứ giá nào, nếu điều này không được mong muốn, người ta có thể ngay lập tức sau đósys.path.pop()
Ataxias

0

Câu trả lời này là phần bổ sung cho câu trả lời của Sebastian Rittau khi trả lời bình luận: "nhưng nếu bạn không có tên mô-đun thì sao?" Đây là một cách nhanh chóng và bẩn thỉu để lấy tên mô-đun python có khả năng được đặt tên tệp - nó chỉ đi lên cây cho đến khi tìm thấy một thư mục không có __init__.pytệp và sau đó biến nó thành tên tệp. Đối với Python 3.4+ (sử dụng pathlib), điều này hợp lý vì người Py2 có thể sử dụng "imp" hoặc các cách khác để thực hiện nhập khẩu tương đối:

import pathlib

def likely_python_module(filename):
    '''
    Given a filename or Path, return the "likely" python module name.  That is, iterate
    the parent directories until it doesn't contain an __init__.py file.

    :rtype: str
    '''
    p = pathlib.Path(filename).resolve()
    paths = []
    if p.name != '__init__.py':
        paths.append(p.stem)
    while True:
        p = p.parent
        if not p:
            break
        if not p.is_dir():
            break

        inits = [f for f in p.iterdir() if f.name == '__init__.py']
        if not inits:
            break

        paths.append(p.stem)

    return '.'.join(reversed(paths))

Chắc chắn có khả năng cải tiến và các __init__.pytệp tùy chọn có thể cần các thay đổi khác, nhưng nếu bạn __init__.pynói chung, đây là mẹo.


-1

Cách tốt nhất, tôi nghĩ là từ tài liệu chính thức ( 29.1. Imp - Truy cập nội bộ nhập khẩu ):

import imp
import sys

def __import__(name, globals=None, locals=None, fromlist=None):
    # Fast path: see if the module has already been imported.
    try:
        return sys.modules[name]
    except KeyError:
        pass

    # If any of the following calls raises an exception,
    # there's a problem we can't handle -- let the caller handle it.

    fp, pathname, description = imp.find_module(name)

    try:
        return imp.load_module(name, fp, pathname, description)
    finally:
        # Since we may exit via an exception, close fp explicitly.
        if fp:
            fp.close()

1
Giải pháp này không cho phép bạn cung cấp đường dẫn, đó là những gì câu hỏi yêu cầu.
Micah Smith
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.