Bagaimana cara mengimpor modul dengan path lengkap?

1141

Bagaimana saya bisa memuat modul Python diberikan path lengkapnya? Perhatikan bahwa file tersebut dapat berada di mana saja di sistem file, karena ini merupakan opsi konfigurasi.

tercabut
sumber
21
Pertanyaan yang bagus dan sederhana - dan jawaban yang berguna tetapi mereka membuat saya bertanya-tanya apa yang terjadi dengan mantra python "Ada satu cara yang jelas " untuk melakukannya .. Sepertinya tidak ada yang seperti jawaban tunggal atau sederhana dan jawaban yang jelas untuk itu. Tampak sangat konyol dan tergantung pada versi untuk operasi mendasar (dan terlihat dan lebih membengkak dalam versi yang lebih baru ..).
inger

Jawaban:

1282

Untuk penggunaan Python 3.5+:

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

Untuk Python 3.3 dan 3.4 gunakan:

from importlib.machinery import SourceFileLoader

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

(Meskipun ini sudah usang dalam Python 3.4.)

Untuk Python 2 gunakan:

import imp

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

Ada fungsi kenyamanan yang setara untuk file Python dan DLL yang dikompilasi.

Lihat juga http://bugs.python.org/issue21436 .

Sebastian Rittau
sumber
53
Jika saya tahu namespace - 'module.name' - Saya sudah akan menggunakan __import__.
Sridhar Ratnakumar
62
@SridharRatnakumar nilai argumen pertama imp.load_sourcehanya menetapkan .__name__modul yang dikembalikan. itu tidak mempengaruhi pemuatan.
Dan D.
17
@DanD. - argumen pertama imp.load_source()menentukan kunci entri baru yang dibuat dalam sys.moduleskamus, sehingga argumen pertama memang mempengaruhi pemuatan.
Brandon Rhodes
9
The impmodul ditinggalkan sejak versi 3.4: The imppaket tertunda bantahan mendukung importlib.
Chiel ten Brinke
9
@AXO dan lebih ke titik yang keajaiban mengapa sesuatu yang sederhana dan dasar karena hal ini telah menjadi begitu rumit. Itu tidak dalam banyak bahasa lain.
berbatu
423

Keuntungan menambahkan path ke sys.path (lebih dari menggunakan imp) adalah bahwa hal itu menyederhanakan hal-hal ketika mengimpor lebih dari satu modul dari satu paket. Sebagai contoh:

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
Daryl Spitzer
sumber
13
Bagaimana kita menggunakan sys.path.appenduntuk menunjuk ke file python tunggal, bukan direktori?
Phani
28
:-) Mungkin pertanyaan Anda akan lebih cocok sebagai pertanyaan StackOverflow, bukan komentar pada jawaban.
Daryl Spitzer
3
Jalur python dapat berisi arsip zip, "telur" (sejenis arsip zip kompleks), dll. Modul dapat diimpor dari situ. Jadi elemen path memang wadah file, tetapi mereka belum tentu direktori.
alexis
12
Waspadalah terhadap fakta bahwa Python membuat cache pernyataan impor. Dalam kasus yang jarang terjadi bahwa Anda memiliki dua folder berbeda yang berbagi nama kelas tunggal (classX), pendekatan menambahkan path ke sys.path, mengimpor classX, menghapus path dan mengulangi untuk jalur reamaining tidak akan bekerja. Python akan selalu memuat kelas dari jalur pertama dari cache-nya. Dalam kasus saya, saya bertujuan membuat sistem plugin di mana semua plugin menerapkan classX tertentu. Saya akhirnya menggunakan SourceFileLoader , perhatikan bahwa penghentiannya kontroversial .
ComFreek
3
Catatan pendekatan ini memungkinkan modul yang diimpor untuk mengimpor modul lain dari dir yang sama, modul mana yang sering dilakukan, sedangkan pendekatan jawaban yang diterima tidak (paling tidak pada 3,7). importlib.import_module(mod_name)dapat digunakan sebagai pengganti impor eksplisit di sini jika nama modul tidak dikenal saat runtime saya akan menambahkan sys.path.pop()pada akhirnya, meskipun, dengan asumsi kode yang diimpor tidak mencoba untuk mengimpor lebih banyak modul seperti yang digunakan.
Eli_B
43

Jika modul tingkat atas Anda bukan file tetapi dikemas sebagai direktori dengan __init__.py, maka solusi yang diterima hampir berfungsi, tetapi tidak cukup. Dalam Python 3.5+ kode berikut diperlukan (perhatikan baris yang ditambahkan yang dimulai dengan '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)

Tanpa baris ini, ketika exec_module dijalankan, ia mencoba untuk mengikat impor relatif di __init__.py tingkat atas ke nama modul tingkat atas - dalam hal ini "mymodule". Tetapi "mymodule" belum dimuat sehingga Anda akan mendapatkan kesalahan "SystemError: Modul induk 'mymodule' tidak dimuat, tidak dapat melakukan impor relatif". Jadi, Anda harus mengikat nama sebelum memuatnya. Alasan untuk ini adalah invarian mendasar dari sistem impor relatif: "holding invarian adalah bahwa jika Anda memiliki sys.modules ['spam'] dan sys.modules ['spam.foo'] (seperti yang Anda lakukan setelah impor di atas ), yang terakhir harus muncul sebagai atribut foo dari yang sebelumnya " seperti yang dibahas di sini .

Sam Grondahl
sumber
Terima kasih banyak! Metode ini memungkinkan impor relatif antar submodul. Bagus!
tebanep
Jawaban ini cocok dengan dokumentasi di sini: docs.python.org/3/library/… .
Tim Ludwinski
1
tapi apa mymodule?
Gulzar
@Gulzar, itu adalah nama apa pun yang Anda ingin berikan modul Anda, sehingga nantinya Anda dapat melakukan: "dari mymodule import myclass"
Idodo
Jadi ... /path/to/your/module/sebenarnya /path/to/your/PACKAGE/? dan mymodulemaksud Anda myfile.py?
Gulzar
37

Untuk mengimpor modul Anda, Anda perlu menambahkan direktori ke variabel lingkungan, baik sementara atau permanen.

Untuk sementara

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

Secara permanen

Menambahkan baris berikut ke .bashrcfile Anda (di linux) dan jalankan source ~/.bashrcdi terminal:

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

Credit / Source: saarrrr , pertanyaan lain tentang stackexchange

Miladiouss
sumber
3
Solusi "temp" ini adalah jawaban yang bagus jika Anda ingin mendorong proyek di notebook jupyter di tempat lain.
fordy
Tapi ... itu berbahaya merusak jalan
Shai Alon
@ShaiAlon Anda menambahkan jalur, jadi tidak ada bahaya selain ketika Anda mentransfer kode dari satu komputer ke komputer lain, jalur mungkin akan kacau. Jadi, untuk pengembangan paket, saya hanya mengimpor paket lokal. Juga, nama paket harus unik. Jika Anda khawatir, gunakan solusi sementara.
Miladiouss
28

Sepertinya Anda tidak ingin mengimpor file konfigurasi secara khusus (yang memiliki banyak efek samping dan komplikasi tambahan), Anda hanya ingin menjalankannya, dan dapat mengakses namespace yang dihasilkan. Pustaka standar menyediakan API khusus untuk itu dalam bentuk runpy.run_path :

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

Antarmuka itu tersedia dalam Python 2.7 dan Python 3.2+

ncoghlan
sumber
Saya suka metode ini tetapi ketika saya mendapatkan hasil dari run_path ini adalah kamus yang sepertinya tidak bisa saya akses?
Stephen Ellwood
Apa yang Anda maksud dengan "tidak dapat mengakses"? Anda tidak dapat mengimpor dari itu (bahwa itu sebabnya ini hanya pilihan yang baik ketika akses bergaya impor sebenarnya tidak diperlukan), tapi isi harus tersedia melalui API dict biasa ( result[name], result.get('name', default_value), dll)
ncoghlan
Jawaban ini diremehkan. Ini sangat singkat dan sederhana! Lebih baik lagi, jika Anda membutuhkan modul namespace yang tepat, Anda dapat melakukan sesuatu seperti from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
RuRo
20

Anda juga dapat melakukan sesuatu seperti ini dan menambahkan direktori tempat file konfigurasi berada di jalur memuat Python, dan kemudian hanya melakukan impor normal, dengan asumsi Anda tahu nama file di muka, dalam hal ini "config".

Berantakan, tetapi berhasil.

configfile = '~/config.py'

import os
import sys

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

import config
ctcherry
sumber
Itu tidak dinamis.
Shai Alon
Saya mencoba: config_file = 'setup-for-chat', setup_file = get_setup_file (config_file + ".py"), sys.path.append (os.path.dirname (os.path.expanduser (setup_file))), import config_file >> "ImportError: Tidak ada modul bernama config_file"
Shai Alon
17

Anda dapat menggunakan

load_source(module_name, path_to_file) 

metode dari modul imp .

zuber
sumber
... dan imp.load_dynamic(module_name, path_to_file)untuk DLL
HEKTO
34
mengepalai imp itu sudah ditinggalkan sekarang.
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')
Chris Calloway
sumber
37
Mengapa menulis 14 baris kode kereta saat ini sudah ditangani oleh perpustakaan standar? Anda belum melakukan pengecekan error pada format atau konten full_path_to_module atau os.apa pun operasi; dan menggunakan except:klausa catch-all jarang merupakan ide yang bagus.
Chris Johnson
Anda harus menggunakan lebih banyak "coba-akhirnya" di sini. Misalnyasave_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
kay - SE jahat
11
@ ChrisJohnson this is already addressed by the standard libraryya, tapi python memiliki kebiasaan buruk untuk tidak kompatibel ke belakang ... karena jawaban yang dicentang mengatakan ada 2 cara berbeda sebelum dan sesudah 3.3. Dalam hal ini saya lebih suka menulis fungsi universal saya sendiri daripada memeriksa versi dengan cepat. Dan ya, mungkin kode ini tidak terlindungi dengan baik dari kesalahan, tetapi ini menunjukkan sebuah ide (yaitu os.chdir (), saya belum memikirkannya), berdasarkan mana saya dapat menulis kode yang lebih baik. Karenanya +1.
Sushi271
13

Berikut adalah beberapa kode yang berfungsi di semua versi Python, dari 2.7-3.5 dan bahkan mungkin yang lain.

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

Saya mengujinya. Mungkin jelek tapi sejauh ini adalah satu-satunya yang berfungsi di semua versi.

Sorin
sumber
1
Jawaban ini berhasil bagi saya di mana load_sourcetidak karena mengimpor skrip dan memberikan akses skrip ke modul dan global pada saat mengimpor.
Klik
13

Saya telah datang dengan versi yang sedikit dimodifikasi dari jawaban luar biasa @ SebastianRittau (untuk Python> 3,4 saya pikir), yang akan memungkinkan Anda untuk memuat file dengan ekstensi apa pun sebagai modul menggunakan spec_from_loaderalih-alih 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)

Keuntungan dari pengkodean jalur secara eksplisit SourceFileLoaderadalah bahwa mesin tidak akan mencoba mencari tahu jenis file dari ekstensi. Ini berarti bahwa Anda dapat memuat sesuatu seperti .txtfile menggunakan metode ini, tetapi Anda tidak dapat melakukannya spec_from_file_locationtanpa menentukan loader karena .txttidak ada importlib.machinery.SOURCE_SUFFIXES.

Fisikawan Gila
sumber
13

Apakah maksud Anda memuat atau mengimpor?

Anda dapat memanipulasi sys.pathdaftar menentukan jalur ke modul Anda, lalu mengimpor modul Anda. Misalnya, diberikan modul di:

/foo/bar.py

Anda bisa melakukannya:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
Gandum
sumber
1
@Apakah Mengapa sys.path [0: 0] bukan sys.path [0]?
user618677
5
B / c sys.path [0] = xy menimpa item path pertama sementara path [0: 0] = xy setara dengan path.insert (0, xy)
dom0
2
hm the path.insert bekerja untuk saya tetapi trik [0: 0] tidak.
jsh
11
sys.path[0:0] = ['/foo']
Kevin Edwards
6
Explicit is better than implicit.Jadi mengapa tidak sys.path.insert(0, ...)bukan sys.path[0:0]?
winklerrr
8

Saya yakin Anda bisa menggunakan imp.find_module()dan imp.load_module()memuat modul yang ditentukan. Anda harus memisahkan nama modul dari jalur, yaitu jika Anda ingin memuat, /home/mypath/mymodule.pyAnda harus melakukan:

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

... tapi itu harus menyelesaikan pekerjaan.

Mat
sumber
6

Anda dapat menggunakan pkgutilmodul (khususnya walk_packagesmetode) untuk mendapatkan daftar paket di direktori saat ini. Dari sana sepele untuk menggunakan importlibmesin untuk mengimpor modul yang Anda inginkan:

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!
bob_twinkles
sumber
5

Buat python module test.py

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

Buat modul python test_check.py

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

Kita dapat mengimpor modul yang diimpor dari modul.

abimanyu
sumber
4

Area Python 3.4 ini tampaknya sangat berliku untuk dipahami! Namun dengan sedikit peretasan menggunakan kode dari Chris Calloway sebagai permulaan saya berhasil membuat sesuatu bekerja. Inilah fungsi dasarnya.

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

Ini tampaknya menggunakan modul yang tidak usang dari Python 3.4. Saya tidak berpura-pura mengerti mengapa, tetapi tampaknya berhasil dari dalam suatu program. Saya menemukan solusi Chris bekerja pada baris perintah tetapi tidak dari dalam suatu program.

Redlegjed
sumber
4

Saya tidak mengatakan bahwa itu lebih baik, tetapi demi kelengkapan, saya ingin menyarankan execfungsi tersebut, tersedia dalam python 2 dan 3. execmemungkinkan Anda untuk mengeksekusi kode arbitrer baik dalam lingkup global, atau dalam lingkup internal, disediakan sebagai kamus.

Misalnya, jika Anda memiliki modul yang disimpan di "/path/to/module"dengan fungsi foo(), Anda bisa menjalankannya dengan melakukan hal berikut:

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

Ini membuatnya sedikit lebih eksplisit bahwa Anda memuat kode secara dinamis, dan memberi Anda kekuatan tambahan, seperti kemampuan untuk menyediakan builtin khusus.

Dan jika memiliki akses melalui atribut, alih-alih kunci penting bagi Anda, Anda dapat mendesain kelas dict khusus untuk global, yang menyediakan akses seperti itu, misalnya:

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

Untuk mengimpor modul dari nama file yang diberikan, Anda dapat memperpanjang sementara jalur, dan mengembalikan jalur sistem di referensi blok akhirnya :

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
Peter Zhu
sumber
4

Jika kami memiliki skrip dalam proyek yang sama tetapi dengan cara direktori yang berbeda, kami dapat menyelesaikan masalah ini dengan metode berikut.

Dalam situasi utils.pyini ada disrc/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
Kumar Immanuel
sumber
yang paling sederhana IMO
Ardhi
3

Ini seharusnya bekerja

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)
Hengjie
sumber
4
Cara yang lebih umum untuk memotong perpanjangan out adalah: name, ext = os.path.splitext(os.path.basename(infile)). Metode Anda berfungsi karena batasan sebelumnya untuk ekstensi .py. Juga, Anda mungkin harus mengimpor modul ke beberapa entri variabel / kamus.
ReneSac
2

Saya membuat paket yang digunakan impuntuk Anda. Saya menyebutnya import_filedan ini adalah bagaimana ini digunakan:

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

Anda bisa mendapatkannya di:

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

atau di

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

ubershmekel
sumber
1
os.chdir? (karakter minimal untuk menyetujui komentar).
ychaouche
Saya telah menghabiskan waktu seharian untuk memecahkan masalah bug impor di exe yang dibuat oleh pyinstaller. Pada akhirnya ini adalah satu-satunya hal yang berhasil untuk saya. Terima kasih banyak untuk membuat ini!
frakman1
2

Impor modul paket saat runtime (resep 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)
pengguna10370
sumber
2

Di Linux, menambahkan tautan simbolik di direktori skrip python Anda berfungsi.

yaitu:

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

python akan membuat /absolute/path/to/script/module.pycdan akan memperbaruinya jika Anda mengubah konten/absolute/path/to/module/module.py

kemudian sertakan yang berikut ini di mypythonscript.py

from module import *
pengguna2760152
sumber
1
Ini adalah retas yang saya gunakan, dan telah menyebabkan saya beberapa masalah. Salah satu yang lebih menyakitkan adalah bahwa IDEA memiliki masalah di mana IDEA tidak mengambil kode yang diubah dari dalam tautan, tetapi upaya untuk menyimpan apa yang dianggapnya ada. Kondisi balapan di mana yang terakhir diselamatkan adalah tongkat ... Saya kehilangan banyak pekerjaan karena ini.
Cengkeraman
@ Gripp tidak yakin apakah saya memahami masalah Anda, tapi saya sering (hampir secara eksklusif) mengedit skrip saya di server jarak jauh dari desktop saya melalui SFTP dengan klien seperti CyberDuck, dan dalam kasus itu juga merupakan ide yang buruk untuk mencoba dan edit file yang disinkronkan, sebagai gantinya jauh lebih aman untuk mengedit file asli. Anda dapat menangkap beberapa masalah ini dengan menggunakan gitdan memeriksa Anda git statusuntuk memverifikasi bahwa perubahan Anda pada skrip benar-benar membuatnya kembali ke dokumen sumber dan tidak tersesat di eter.
user5359531
2

Saya telah menulis fungsi impor global dan portabel saya sendiri, berdasarkan importlibmodul, untuk:

  • Mampu mengimpor kedua modul sebagai submodule dan untuk mengimpor konten modul ke modul induk (atau ke global jika tidak memiliki modul induk).
  • Mampu mengimpor modul dengan karakter titik dalam nama file.
  • Mampu mengimpor modul dengan ekstensi apa pun.
  • Mampu menggunakan nama mandiri untuk submodule alih-alih nama file tanpa ekstensi yang secara default.
  • Mampu menetapkan urutan impor berdasarkan modul yang diimpor sebelumnya alih-alih bergantung pada sys.pathatau pada penyimpanan jalur pencarian apa pun.

Struktur direktori contoh:

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

Ketergantungan dan keteraturan inklusi:

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

Penerapan:

Toko perubahan terbaru: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py

test.py :

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

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

Diuji di Python 3.7.4, 3.2.5,2.7.16

Pro :

  • Dapat mengimpor kedua modul sebagai submodule dan dapat mengimpor konten modul ke modul induk (atau ke global jika tidak memiliki modul induk).
  • Dapat mengimpor modul dengan titik dalam nama file.
  • Dapat mengimpor modul ekstensi apa pun dari modul ekstensi apa pun.
  • Dapat menggunakan nama mandiri untuk submodul alih-alih nama file tanpa ekstensi yang secara default (misalnya, testlib.std.pyseperti testlib, testlib.blabla.pysepertitestlib_blabla dan seterusnya).
  • Tidak tergantung pada a sys.path atau pada penyimpanan jalur pencarian apa pun.
  • Tidak perlu menyimpan / mengembalikan variabel global seperti SOURCE_FILEdan SOURCE_DIRantar panggilan ketkl_import_module .
  • [untuk 3.4.xdan lebih tinggi] Dapat mencampur ruang nama modul dalam tkl_import_modulepanggilan bersarang (mis: named->local->namedataulocal->named->local dan sebagainya).
  • [untuk 3.4.xdan lebih tinggi] Dapat mengekspor variabel / fungsi / kelas global secara otomatis dari tempat dideklarasikan ke semua modul anak yang diimpor melalui tkl_import_module(melalui tkl_declare_globalfungsi).

Cons :

  • [untuk 3.3.xdan lebih rendah] Harus menyatakan tkl_import_moduledalam semua modul yang membutuhkan tkl_import_module(duplikasi kode)

Perbarui 1,2 (untuk3.4.x dan lebih tinggi):

Dalam Python 3.4 dan lebih tinggi Anda dapat melewati persyaratan untuk mendeklarasikan tkl_import_moduledalam setiap modul dengan mendeklarasikantkl_import_module dalam modul tingkat atas dan fungsinya akan menyuntikkan dirinya sendiri ke semua modul anak-anak dalam satu panggilan (ini semacam impor self deploy).

Perbarui 3 :

Menambahkan fungsi tkl_source_modulesebagai analog ke bash sourcedengan penjaga eksekusi dukungan saat impor (diimplementasikan melalui penggabungan modul, bukan impor).

Pembaruan 4 :

Fungsi yang ditambahkan tkl_declare_globaluntuk secara otomatis mengekspor variabel global modul ke semua modul anak-anak di mana variabel global modul tidak terlihat karena bukan merupakan bagian dari modul anak.

Pembaruan 5 :

Semua fungsi telah pindah ke perpustakaan tacklelib, lihat tautan di atas.

Andry
sumber
2

Ada paket yang didedikasikan untuk ini secara khusus:

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

Ini diuji di versi Python (Jython dan PyPy juga), tetapi mungkin berlebihan tergantung pada ukuran proyek Anda.

fny
sumber
1

Menambahkan ini ke daftar jawaban karena saya tidak dapat menemukan apa pun yang berfungsi. Ini akan memungkinkan impor modul python dikompilasi (pyd) di 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()
David
sumber
1

caranya cukup sederhana: misalkan Anda ingin mengimpor file dengan path relatif ../../MyLibs/pyfunc.py


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

Tetapi jika Anda berhasil tanpa penjaga Anda akhirnya bisa mendapatkan jalan yang sangat panjang

Andrei Keino
sumber
1

Solusi sederhana menggunakan importlibalih-alih imppaket (diuji untuk Python 2.7, meskipun harus bekerja untuk Python 3 juga):

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

Sekarang Anda dapat langsung menggunakan namespace dari modul yang diimpor, seperti ini:

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

Keuntungan dari solusi ini adalah kita bahkan tidak perlu tahu nama sebenarnya dari modul yang ingin kita impor , untuk menggunakannya dalam kode kita. Ini berguna, misalnya jika jalur modul adalah argumen yang dapat dikonfigurasi.

Ataksia
sumber
Dengan cara ini Anda memodifikasi sys.path, yang tidak cocok untuk setiap kasus penggunaan.
bgusach
@ bgusach Ini mungkin benar, tetapi juga diinginkan dalam beberapa kasus (menambahkan path ke sys.path menyederhanakan hal-hal ketika mengimpor lebih dari satu modul dari satu paket). Bagaimanapun, jika ini tidak diinginkan, seseorang dapat segera melakukannyasys.path.pop()
Ataxias
0

Jawaban ini adalah tambahan untuk jawaban Sebastian Rittau menanggapi komentar: "tetapi bagaimana jika Anda tidak memiliki nama modul?" Ini adalah cara cepat dan kotor untuk mendapatkan nama modul python yang mungkin diberikan nama file - hanya naik pohon sampai menemukan direktori tanpa __init__.pyfile dan kemudian mengubahnya kembali menjadi nama file. Untuk Python 3.4+ (menggunakan pathlib), yang masuk akal karena orang Py2 dapat menggunakan "imp" atau cara lain untuk melakukan impor relatif:

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

Tentunya ada kemungkinan untuk peningkatan, dan __init__.pyfile-file opsional mungkin memerlukan perubahan lain, tetapi jika Anda memiliki __init__.pysecara umum, ini adalah triknya.

Michael Scott Cuthbert
sumber
-1

Cara terbaik, saya pikir, adalah dari dokumentasi resmi ( 29.1. Imp - Akses internal impor ):

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()
Zompa
sumber
1
Solusi ini tidak memungkinkan Anda untuk menyediakan jalur, yang merupakan pertanyaan yang ditanyakan.
Micah Smith