Tam yol verilen bir modül nasıl alınır?


1139

Tam yolu verilen bir Python modülünü nasıl yükleyebilirim? Bir yapılandırma seçeneği olduğundan dosyanın dosya sisteminin herhangi bir yerinde olabileceğini unutmayın.


21
Güzel ve basit bir soru - ve yararlı cevaplar ama bana bunu yapmak için python mantra ile ne olduğunu merak ediyorsun .. Bunu yapmak için tek bariz bir yol var .. Tek ya da basit ve açık bir cevap gibi bir şey gibi görünmüyor. Böyle temel bir operasyon için gülünç hileli ve versiyona bağımlı görünüyor (ve daha yeni versiyonlarda görünüyor ve daha şişkin ..).
inger

Yanıtlar:


1281

Python 3.5+ kullanımı için:

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

Python 3.3 ve 3.4 için:

from importlib.machinery import SourceFileLoader

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

(Bu Python 3.4'te kullanımdan kaldırılmasına rağmen.)

Python 2 için:

import imp

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

Derlenmiş Python dosyaları ve DLL dosyaları için eşdeğer kolaylık işlevleri vardır.

Ayrıca bkz . Http://bugs.python.org/issue21436 .


53
Ad alanını bilseydim - 'module.name' - Ben zaten kullanırdım __import__.
Sridhar Ratnakumar

62
@SridharRatnakumar imp.load_sourceyalnızca .__name__döndürülen modülün ilk argümanının değerini ayarlar . yüklemeyi etkilemez.
Dan D.

17
@DanD. - ilk argümanı sözlükte imp.load_source()oluşturulan yeni girdinin anahtarını belirler sys.modules, bu nedenle ilk argüman yüklemeyi gerçekten etkiler.
Brandon Rhodes

9
impModül sürümü 3.4 beri kullanım dışı kalmıştır: imppaket lehine itirazın bekliyor importlib.
Chiel ten Brinke

9
@AXO ve basit ve temel olarak bir şey bu kadar neden fazla noktaya bir merak etti bu yüzden karmaşık olmak. Diğer birçok dilde değil.
kayalık

422

Sys.path (imp kullanarak aşırı) yolu eklemenin avantajı, tek bir paketten birden fazla modül içe aktarırken işleri basitleştirmesidir. Örneğin:

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
sys.path.appendDizin yerine tek bir python dosyasına işaret etmek için nasıl kullanılır ?
Phani

28
:-) Belki de sorunuz bir cevaba yorum olarak değil, StackOverflow sorusu olarak daha uygun olacaktır.
Daryl Spitzer

3
Python yolu, zip arşivleri, "yumurtalar" (karmaşık bir zip arşivleri türü) vb. İçerebilir. Modüller bunlardan içe aktarılabilir. Bu nedenle yol öğeleri gerçekten dosya kaplarıdır , ancak mutlaka dizinler değildir.
alexis

12
Python'un içe aktarma ifadelerini önbelleğe almasına dikkat edin. Nadiren, tek bir sınıf adını (classX) paylaşan iki farklı klasörünüz olması durumunda, sys.path yolunu ekleme, classX alma, yolu kaldırma ve yeniden oluşturma yolları için yineleme yaklaşımı çalışmaz. Python sınıfı her zaman ilk yoldan önbelleğinden yükler. Benim durumumda, tüm eklentilerin belirli bir classX uyguladığı bir eklenti sistemi oluşturmayı amaçladım. SourceFileLoader kullanarak sona erdi , itiraz tartışmalı olduğunu unutmayın .
ComFreek

3
Bu yaklaşımın, içe aktarılan modülün, modüllerin genellikle yaptığı aynı direkten diğer modülleri içe aktarmasına izin verdiğini, kabul edilen yanıtın yaklaşımı ise (en azından 3.7'de) izin verdiğini unutmayın. importlib.import_module(mod_name)modül adı çalışma zamanında bilinmiyorsa, burada açık içe aktarma yerine kullanılabilir sys.path.pop(), ancak sonunda, içe aktarılan kodun kullanıldıkça daha fazla modül içe aktarmaya çalışmadığı varsayılarak ekleyeceğim .
Eli_B

43

Üst düzey modülünüz bir dosya değilse ancak __init__.py ile bir dizin olarak paketlenmişse, kabul edilen çözüm neredeyse çalışır, ancak tam olarak çalışmaz. Python 3.5+ sürümünde aşağıdaki kod gereklidir ('sys.modules' ile başlayan ek satırı not edin):

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)

Bu satır olmadan, exec_module yürütüldüğünde, üst düzey __init__.py'deki göreli içe aktarımları üst düzey modül adına bağlamaya çalışır - bu durumda "mymodule". Ancak "mymodule" henüz yüklenmediği için "SystemError: 'mymodule' ana modülü yüklenmedi, göreli içe aktarma gerçekleştirilemiyor" hatasını alırsınız. Bu yüzden yüklemeden önce adı bağlamanız gerekir. Bunun nedeni, göreli ithalat sisteminin temel değişmezidir: "Değişmez tutma, sys.modules ['spam'] ve sys.modules ['spam.foo'] (yukarıdaki içe aktarmadan sonra yaptığınız gibi) ), ikincisinin " burada tartışıldığı gibi " ilkinin foo özelliği olarak görünmesi gerekir .


Çok teşekkürler! Bu yöntem, alt modüller arasında göreli ithalatı mümkün kılar. Harika!
tebanep

Bu yanıt şu belgedeki belgelerle eşleşiyor: docs.python.org/3/library/… .
Tim Ludwinski

1
ama nedir mymodule?
Gulzar

@Gulzar, modülünüzü vermek istediğiniz isim, daha sonra yapabileceğiniz gibi: "mymodule import myclass"
Idodo

Yani ... /path/to/your/module/aslında /path/to/your/PACKAGE/? ve mymodulene demek istiyorsun myfile.py?
Gulzar

37

Modülünüzü içe aktarmak için, dizinini geçici veya kalıcı olarak ortam değişkenine eklemeniz gerekir.

Geçici

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

kalıcı olarak

.bashrcDosyanıza (linux cinsinden) aşağıdaki satırı ekleme ve source ~/.bashrcterminalde excecute :

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

Kredi / Kaynak: saarrrr , başka bir yığın değişim sorusu


3
Bu "geçici" çözüm, başka bir yerde bir jupyter not defterinde bir proje üretmek istiyorsanız harika bir cevaptır.
fordy

Ama ... yolla tehlikeli bir değişiklik
Shai Alon

@ShaiAlon Yollar ekliyorsunuz, bu nedenle kodları bir bilgisayardan diğerine aktarmanızdan başka hiçbir tehlike yok, yollar karışabilir. Bu nedenle, paket geliştirme için yalnızca yerel paketleri içe aktarıyorum. Ayrıca, paket adları benzersiz olmalıdır. Endişeleniyorsanız geçici çözümü kullanın.
Miladiouss

28

Yapılandırma dosyasını (çok fazla yan etkisi ve ek komplikasyonları olan) içe aktarmak istemediğiniz, sadece çalıştırmak istediğiniz ve sonuçta elde edilen ad alanına erişebildiğiniz anlaşılıyor. Standart kütüphane, runpy.run_path biçiminde özel bir API sağlar :

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

Bu arayüz Python 2.7 ve Python 3.2+ sürümlerinde mevcuttur


Bu yöntemi seviyorum ama run_path sonucunu aldığımda erişemediğim bir sözlük?
Stephen Ellwood

Ne demek "erişemiyorum"? (Bu ithalat tarzı erişim aslında gerekli değildir, sadece iyi bir seçenektir neden en yani), ancak içeriği düzenli dict API (yoluyla ulaşılabilir olmalıdır ondan alamıyor result[name], result.get('name', default_value)vs)
ncoghlan

Bu cevabın önemsiz olduğunu düşünüyoruz. Çok kısa ve basit! Daha da iyisi, uygun bir modül ad alanına ihtiyacınız varsa, şöyle yapabilirsiniz from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
RuRo

20

Bunun gibi bir şey de yapabilir ve yapılandırma dosyasının içinde bulunduğu dizini Python yükleme yoluna ekleyebilir ve daha sonra normal bir içe aktarma yaparak dosyanın adını önceden bildiğinizi varsayarak bu durumda "config" yapabilirsiniz.

Dağınık, ama işe yarıyor.

configfile = '~/config.py'

import os
import sys

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

import config

Bu dinamik değil.
Shai Alon

Denedim: config_file = 'sohbetler için kurulum', setup_file = get_setup_file (config_file + ".py"), sys.path.append (os.path.dirname (os.path.expanduser (setup_file)), import_file >> "ImportError: config_file adında bir modül yok"
Shai Alon

17

Kullanabilirsiniz

load_source(module_name, path_to_file) 

imp modülünden yöntem .


... ve imp.load_dynamic(module_name, path_to_file)DLL'ler için
HEKTO

34
bu küçük şeytan şu an itiraz ediliyor.
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
Standart kitaplık tarafından zaten ele alındığında neden 14 satır hata kodu yazalım? Full_path_to_module biçiminde veya içeriğinde veya os.whatever işlemlerinde hata denetimi yapmadınız; ve tümünü yakalama except:şartını kullanmak nadiren iyi bir fikirdir.
Chris Johnson

Burada daha fazla "try-nihayet" kullanmalısınız. Örsave_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
kay - SE,

11
@ChrisJohnson this is already addressed by the standard libraryevet, ama python geriye dönük uyumlu olmama gibi kötü bir alışkanlığa sahip ... kontrol edilen cevap 3.3'ten önce ve sonra 2 farklı yol olduğunu söylüyor. Bu durumda, versiyonunu anında kontrol etmekten ziyade kendi evrensel fonksiyonumu yazmak istiyorum. Ve evet, belki de bu kod çok iyi hata korumalı değildir, ancak daha iyi bir kod yazabileceğime dayanarak bir fikir (os.chdir (), bununla birlikte değilim) gösterir. Dolayısıyla +1.
Sushi271

13

İşte tüm Python sürümlerinde çalışan bazı kodlar, 2.7-3.5 ve muhtemelen diğerleri.

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

Test ettim. Çirkin olabilir, ancak şimdiye kadar tüm versiyonlarda çalışan tek kişi.


1
Bu yanıt benim için işe load_sourceyaramadı çünkü komut dosyasını içe aktardığı ve içe aktarma sırasında komut dosyasına modüllere ve globallere erişim sağladığı için işe yaramadı.
Klik

13

@ SebastianRittau harika cevabının biraz değiştirilmiş bir versiyonu ile geldim (Python> 3.4 için), bunun spec_from_loaderyerine kullanarak herhangi bir uzantıya sahip bir modülü modül olarak yüklemenize izin verecek 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)

Yolu açık bir şekilde kodlamanın avantajı SourceFileLoader, makinenin uzantıdan dosyanın türünü bulmaya çalışmamasıdır. Bu, .txtbu yöntemi kullanarak dosya gibi bir şey yükleyebileceğiniz anlamına gelir , ancak içinde spec_from_file_locationolmadığı için yükleyiciyi belirtmeden bunu .txtyapamazsınız importlib.machinery.SOURCE_SUFFIXES.


13

Yük veya ithalat mı demek istediniz?

sys.pathModülünüzün yolunu belirten listeyi değiştirebilir , ardından modülünüzü içe aktarabilirsiniz. Örneğin, bir modül verildiğinde:

/foo/bar.py

Şunları yapabilirsiniz:

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

1
@Wheat Neden sys.path [0] yerine sys.path [0: 0]?
user618677

5
B / c sys.path [0] = xy, ilk yol öğesinin üzerine yazarken path [0: 0] = xy, path.insert (0, xy)
dom0

2
hm path.insert benim için çalıştı ama [0: 0] hile işe yaramadı.
jsh

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

6
Explicit is better than implicit.Neden olmasın Yani sys.path.insert(0, ...)yerine sys.path[0:0]?
winklerrr

8

Belirtilen modülü kullanabileceğinizi imp.find_module()ve imp.load_module()yükleyebileceğinizi düşünüyorum . Modül adını yoldan ayırmanız gerekir, yani yüklemek /home/mypath/mymodule.pyistiyorsanız yapmanız gerekenler:

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

... ama bu işi halletmeli.


6

Geçerli dizindeki paketlerin bir listesini almak için pkgutilmodülü (özellikle walk_packagesyöntem) kullanabilirsiniz. Oradan importlibistediğiniz modülleri almak için makineleri kullanmak önemsizdir :

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

Python module test.py oluştur

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

Python modülü oluştur test_check.py

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

İthal modülü modülden alabiliriz.


4

Python 3.4'ün bu alanının anlaşılması son derece kıvrımlı görünüyor! Ancak bir başlangıç ​​olarak Chris Calloway kodunu kullanarak kesmek biraz ile çalışan bir şey başardı. İşte temel işlev.

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

Bu, Python 3.4'teki kullanımdan kaldırılmış modülleri kullanıyor gibi görünüyor. Nedenini anlamıyormuş gibi yapmıyorum, ama bir program içinden çalışıyor gibi görünüyor. Chris'in çözümünün komut satırında çalıştığını, ancak programın içinden çalışmadığını gördüm.


4

Bunun iyi olduğunu söylemiyorum, fakat bütünlük uğruna, ben önermek istedik exechem piton 2 mevcuttur fonksiyonunu ve 3. exec, küresel etki ya rasgele kod yürütmesine olanak veya dahili kapsamında sözlük olarak sağlanmıştır.

Örneğin, "/path/to/module"" işleviyle birlikte depolanmış bir modülünüz varsa foo(), aşağıdakileri yaparak çalıştırabilirsiniz:

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

Bu, kodu dinamik olarak yüklediğinizi biraz daha açık hale getirir ve size özel yerleşikler sağlama yeteneği gibi bazı ek güç verir.

Nitelikler aracılığıyla erişime sahipseniz, anahtarlar yerine sizin için önemliyse, globaller için bu tür bir erişim sağlayan özel bir dikte sınıfı tasarlayabilirsiniz, örneğin:

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

4

Belirli bir dosya adından bir modülü içe aktarmak için, yolu geçici olarak genişletebilir ve son olarak blok başvurusunda sistem yolunu geri yükleyebilirsiniz :

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

Bu çalışmalı

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
Uzatma out kesmek için daha genel bir yoludur: name, ext = os.path.splitext(os.path.basename(infile)). Yönteminiz, .py uzantısına yönelik önceki kısıtlama nedeniyle çalışır. Ayrıca, modülü muhtemelen bazı değişken / sözlük girişlerine aktarmanız gerekir.
ReneSac

3

Aynı projede ancak farklı dizin araçlarında komut dosyalarımız varsa, bu sorunu aşağıdaki yöntemle çözebiliriz.

Bu durum ise utils.pyiçindedirsrc/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

Sizin için bir paket hazırladım imp. Ben diyorum import_fileve bu nasıl kullanılır:

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

Şuradan alabilirsiniz:

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

ya da

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


1
os.chdir? (yorumu onaylamak için en az karakter).
ychaouche

Ben bütün gün bir pyinstaller oluşturulan exe bir ithalat hata sorun giderme geçirdim. Sonunda benim için işe yarayan tek şey bu. Bunu yaptığınız için çok teşekkür ederim!
frakman1

2

Paket modüllerini çalışma zamanında içe aktarma (Python tarifi)

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

Linux'ta, python betiğinizin bulunduğu dizine sembolik bir bağlantı eklemek işe yarar.

yani:

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

python /absolute/path/to/script/module.pyc, içeriğini değiştirirseniz oluşturur ve günceller/absolute/path/to/module/module.py

ardından mypythonscript.py dosyasına aşağıdakileri ekleyin

from module import *

1
Kullandığım saldırı bu ve bana bazı sorunlara neden oldu. Daha acı verici olanlardan biri, IDEA'nın, bağlantıdan değiştirilmiş kodu almaması, ancak orada olduğunu düşündüğü şeyi kaydetmeye çalıştığı bir sorunu olmasıydı. En son biriken sopa olan bir yarış koşulu ... Bu yüzden iyi bir iş kaybettim.
Gripp

@Gripp sorununuzu anladığımdan emin değilim, ancak CyberDuck gibi bir istemciyle SFTP aracılığıyla masaüstümdeki uzak bir sunucudaki komut dosyalarını sık sık (neredeyse tamamen) düzenliyorum ve bu durumda da denemek kötü bir fikir. symlinked dosyasını düzenleyin, bunun yerine orijinal dosyayı düzenlemek daha güvenlidir. Koddaki değişikliklerinizin kaynak belgeye geri döndüğünü ve eterde kaybolmadığını doğrulamak gitiçin kullanarak ve kontrol ederek bu sorunlardan bazılarını yakalayabilirsiniz git status.
user5359531

2

importlibModüle dayalı olarak kendi global ve taşınabilir içe aktarma işlevimi yazdım :

  • Her iki modülü de bir alt modül olarak içe aktarabilir ve bir modülün içeriğini bir üst modüle (ya da üst modülü yoksa globallere) aktarabilirsiniz.
  • Dosya adında nokta karakterleri olan modülleri içe aktarabilme.
  • Herhangi bir uzantıya sahip modülleri alabilir.
  • Alt modül için varsayılan olarak uzantısız bir dosya adı yerine bağımsız bir ad kullanabilirsiniz.
  • İçe aktarma sırasını, sys.patharama yolu depolama alanına bağlı veya bunun yerine önceden içe aktarılan modüle göre tanımlayabilme .

Örnek dizin yapısı:

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

İçerme bağımlılığı ve düzeni:

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

Uygulama:

Son değişiklikler mağazası: 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')

Çıktı ( 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

Python test edilmiştir 3.7.4, 3.2.5,2.7.16

Artıları :

  • Her iki modülü de bir alt modül olarak içe aktarabilir ve bir modülün içeriğini bir ana modüle (ya da ana modülü yoksa globallere) aktarabilir.
  • Dosya adında nokta bulunan modülleri içe aktarabilir.
  • Herhangi bir genişletme modülünü herhangi bir genişletme modülünden alabilir.
  • (Örneğin, varsayılan olarak uzantısı olmadan yerine dosya adının bir altmodülün için bağımsız adını kullanabilir miyim testlib.std.pyolarak testlib, testlib.blabla.pyolarak testlib_blablave diğerleri).
  • Herhangi sys.pathbir arama yolu depolama alanına veya buna bağlı değildir .
  • Çağrıları gibi SOURCE_FILEve SOURCE_DIRarasındaki genel değişkenlerin kaydedilmesini / geri yüklenmesini gerektirmez tkl_import_module.
  • [için 3.4.xve daha yüksek] İç içe tkl_import_moduleçağrılarda modül ad alanlarını karıştırabilir (ör: named->local->namedveyalocal->named->local vs.).
  • [için 3.4.xve daha yüksek] Genel değişkenleri / işlevleri / sınıfları bildirildiği yerden tkl_import_module( tkl_declare_globalişlev aracılığıyla ) alınan tüm alt modüllere otomatik olarak verebilir .

Eksileri :

  • [için 3.3.xve daha düşük] Çağrıyı yapan tkl_import_moduletüm modüllerde tkl_import_module(kod çoğaltma)

1,2 Güncellemesi (için3.4.x yalnızca ve üstü için):

Python 3.4 ve üzeri sürümlerde , bir üst düzey modülde tkl_import_modulebildirerek her bir modülde bildirme gereksinimini atlayabilirsiniz tkl_import_moduleve işlev tek bir çağrıda tüm alt modüllere kendini enjekte eder (bu bir tür kendi kendine konuşlandırmayı içe aktarmadır).

Güncelleme 3 :

İçe aktarma sırasında destek yürütme korumasıyla tkl_source_modulebash'a analog olarak işlev eklendi source(içe aktarma yerine modül birleştirme yoluyla uygulanır).

Güncelleme 4 :

tkl_declare_globalBir modül genel değişkenini, alt modülün bir parçası olmadığı için modül genel değişkeninin görünmediği tüm alt modüllere otomatik olarak dışa aktarma işlevi eklendi .

Güncelleme 5 :

Tüm işlevler tacklelib kütüphanesine taşındı, yukarıdaki bağlantıya bakın.


2

Özellikle buna adanmış bir paket var:

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

Python sürümlerinde (Jython ve PyPy de) test edilmiştir, ancak projenizin boyutuna bağlı olarak aşırı olabilir.


1

Bunu işe yarayan bir şey bulamadığım için cevap listesine ekliyorum. Bu, derlenmiş (pyd) python modüllerinin 3.4'te içe aktarılmasına izin verecektir:

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

oldukça basit bir yol var: göreceli yolla dosya almak istediğinizi varsayalım ../../MyLibs/pyfunc.py


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

Ama eğer bir gardiyan olmadan yaparsanız, sonunda çok uzun bir yol elde edebilirsiniz.


1

Paket importlibyerine kullanarak basit bir çözüm imp(Python 3 için de çalışmasına rağmen Python 2.7 için test edilmiştir):

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

Şimdi içe aktarılan modülün ad alanını doğrudan kullanabilirsiniz, şöyle:

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

Bu çözümün avantajı , kodumuzda kullanmak için içe aktarmak istediğimiz modülün gerçek adını bile bilmemize gerek yoktur . Bu, örneğin modülün yolunun yapılandırılabilir bir argüman olması durumunda kullanışlıdır.


Bu şekilde sys.path, her kullanım durumuna uymayan 'ı değiştirirsiniz .
bgusach

@bgusach Bu doğru olabilir, ancak bazı durumlarda da istenir (sys.path'e bir yol eklemek, tek bir paketten birden fazla modül alırken işleri basitleştirir). Her halükarda, bu arzu edilmezse, hemen sonra yapılabilirsys.path.pop()
Ataxias

0

Bu cevap Sebastian Rittau'nun şu cevaba verdiği cevaba ek niteliğindedir: "ama modül isminiz yoksa ne olacak?" Bu, bir dosya adı verilen olası python modülü adını almanın hızlı ve kirli bir yoludur - sadece dosyasız bir dizin bulana __init__.pyve sonra tekrar bir dosya adına dönüşene kadar ağaca gider . Py2 kullanıcıları "imp" veya göreli ithalat yapmanın diğer yollarını kullanabildikleri için mantıklı olan Python 3.4+ (pathlib kullanır):

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

Kesinlikle iyileştirme olasılıkları vardır ve isteğe bağlı __init__.pydosyalar başka değişiklikler gerektirebilir, ancak __init__.pygenel olarak varsa, bu hile yapar.


-1

En iyi yolu, bence, resmi belgelerden ( 29.1. İmp - İthalat içlerine erişim ):

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
Bu çözüm, soruyu soran yolu sağlamanıza izin vermez.
Micah Smith
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.