วิธีการนำเข้าโมดูลที่กำหนดเส้นทางแบบเต็ม?


1139

ฉันจะโหลดโมดูล Python ที่ระบุเส้นทางแบบเต็มได้อย่างไร โปรดทราบว่าไฟล์สามารถอยู่ที่ใดก็ได้ในระบบไฟล์เนื่องจากเป็นตัวเลือกการกำหนดค่า


21
คำถามที่ดีและเรียบง่าย - และคำตอบที่มีประโยชน์ แต่พวกเขาทำให้ฉันสงสัยว่าเกิดอะไรขึ้นกับงูหลาม "มีวิธีหนึ่งที่ ชัดเจน " ในการทำ .. ดูเหมือนว่ามันจะไม่เหมือนอะไรเลย ดูเหมือนแฮ็คที่น่าขันและขึ้นอยู่กับรุ่นสำหรับการดำเนินการขั้นพื้นฐาน (และมันก็ดูและป่องมากขึ้นในรุ่นที่ใหม่กว่า .. )
inger

คำตอบ:


1281

สำหรับ 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()

สำหรับ Python 3.3 และ 3.4 ใช้:

from importlib.machinery import SourceFileLoader

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

(แม้ว่าสิ่งนี้จะถูกคัดค้านใน Python 3.4)

สำหรับ Python 2 ให้ใช้:

import imp

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

มีฟังก์ชั่นอำนวยความสะดวกที่เทียบเท่าสำหรับไฟล์ Python ที่รวบรวมและ DLL

ดูเพิ่มเติมhttp://bugs.python.org/issue21436


53
ถ้าผมรู้ว่าการ namespace - 'module.name' - __import__ฉันจะใช้แล้ว
Sridhar Ratnakumar

62
@SridharRatnakumar ค่าของอาร์กิวเมนต์แรกของimp.load_sourceเพียงชุด.__name__ของโมดูลกลับ มันไม่มีผลต่อการโหลด
Dan D.

17
@DanD - อาร์กิวเมนต์แรกของการimp.load_source()กำหนดคีย์ของรายการใหม่ที่สร้างขึ้นในsys.modulesพจนานุกรมดังนั้นอาร์กิวเมนต์แรกจะมีผลต่อการโหลด
Brandon Rhodes

9
impโมดูลจะเลิกตั้งแต่รุ่น 3.4 การแพคเกจอยู่ระหว่างการพิจารณาเลิกในความโปรดปรานของimp importlib
Chiel ten Brinke

9
@AXO และขึ้นไปยังจุดสิ่งมหัศจรรย์หนึ่งว่าทำไมสิ่งที่ง่ายและพื้นฐานเช่นนี้มีความซับซ้อนดังนั้น มันไม่ได้อยู่ในภาษาอื่น ๆ อีกมากมาย
ร็อ

422

ข้อดีของการเพิ่มพา ธ ไปยัง sys.path (over using imp) คือมันทำให้สิ่งต่าง ๆ ง่ายขึ้นเมื่อนำเข้าโมดูลมากกว่าหนึ่งโมดูลจากแพ็คเกจเดียว ตัวอย่างเช่น:

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.appendการชี้ไปที่ไฟล์หลามเดียวแทนที่จะเป็นไดเรกทอรีได้อย่างไร?
Phani

28
:-) บางทีคำถามของคุณอาจเหมาะกว่าสำหรับคำถาม StackOverflow ไม่ใช่ความคิดเห็นในคำตอบ
ดาริลสปิตเซอร์

3
เส้นทางหลามสามารถมีไฟล์เก็บถาวร zip, "ไข่" (ไฟล์เก็บถาวรแบบซิปที่ซับซ้อน) ฯลฯ โมดูลสามารถนำเข้าได้ ดังนั้นองค์ประกอบพา ธ จึงเป็นที่เก็บไฟล์แน่นอนแต่ไม่จำเป็นต้องมีไดเรกทอรี
alexis

12
ระวังความจริงที่ว่า Python เก็บคำสั่งการนำเข้า ในกรณีที่ไม่ค่อยเกิดขึ้นที่คุณมีสองโฟลเดอร์ที่แตกต่างกันที่ใช้ชื่อคลาสเดียว (classX) วิธีการเพิ่มพา ธ ไปยัง sys.path การนำเข้า classX การลบพา ธ และการทำซ้ำสำหรับพา ธ การ reamaining จะไม่ทำงาน Python จะโหลดคลาสจากพา ธ แรกจากแคชเสมอ ในกรณีของฉันฉันมุ่งสร้างระบบปลั๊กอินที่ปลั๊กอินทั้งหมดใช้ classX เฉพาะ ฉันสิ้นสุดการใช้SourceFileLoaderทราบว่าเลิกเป็นที่ถกเถียง
ComFreek

3
หมายเหตุวิธีการนี้อนุญาตให้โมดูลที่อิมพอร์ตนำเข้าโมดูลอื่นจาก dir เดียวกันซึ่งโมดูลมักทำในขณะที่วิธีการของคำตอบที่ยอมรับไม่ได้ (อย่างน้อยใน 3.7) importlib.import_module(mod_name)สามารถนำมาใช้แทนการนำเข้าที่ชัดเจนได้ที่นี่หากไม่ทราบชื่อโมดูลในรันไทม์ฉันจะเพิ่มsys.path.pop()ในที่สุดแม้ว่าสมมติว่ารหัสที่นำเข้าไม่ได้พยายามที่จะนำเข้าโมดูลมากขึ้นตามที่ใช้
Eli_B

43

หากโมดูลระดับบนสุดของคุณไม่ใช่ไฟล์ แต่ได้รับการบรรจุเป็นไดเรกทอรีที่มี __init__.py แสดงว่าโซลูชันที่ได้รับการยอมรับนั้นใช้งานได้ แต่ไม่มากนัก ใน Python 3.5+ จำเป็นต้องใช้รหัสต่อไปนี้ (สังเกตบรรทัดเพิ่มที่ขึ้นต้นด้วย '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)

หากไม่มีบรรทัดนี้เมื่อเรียกใช้งาน exec_module จะพยายามผูกการนำเข้าที่สัมพันธ์กันในระดับสูงสุด __init__.py ของคุณไปยังชื่อโมดูลระดับบนสุด - ในกรณีนี้ "mymodule" แต่ยังไม่ได้โหลด "mymodule" ดังนั้นคุณจะได้รับข้อผิดพลาด "SystemError: โมดูลหลัก 'mymodule' ไม่ได้โหลดไม่สามารถนำเข้าแบบสัมพัทธ์ได้" ดังนั้นคุณต้องผูกชื่อก่อนที่จะโหลด เหตุผลนี้เป็นค่าคงที่พื้นฐานของระบบนำเข้าแบบสัมพัทธ์: "การเปลี่ยนแปลงแบบคงที่คือถ้าคุณมี sys.modules ['spam'] และ sys.modules ['spam.foo'] (ตามที่คุณต้องการหลังจากการนำเข้าข้างต้น ) หลังต้องปรากฏเป็นแอตทริบิวต์ foo ของอดีต" ตามที่กล่าวไว้ที่นี่


ขอบคุณมาก! วิธีนี้ช่วยให้การนำเข้าสัมพัทธ์ระหว่าง submodules ที่ดี!
tebanep

คำตอบนี้ตรงกับเอกสารที่นี่: docs.python.org/3/library/...
Tim Ludwinski

1
แต่สิ่งที่เป็นmymodule?
Gulzar

@Gulzar มันเป็นสิ่งที่ชื่อคุณต้องการที่จะให้โมดูลของคุณเช่นว่าในภายหลังคุณสามารถทำได้: "จาก MyClass นำเข้า MyModule"
Idodo

ดังนั้น ... /path/to/your/module/เป็นจริง/path/to/your/PACKAGE/? และโดยmymoduleที่คุณหมายถึงmyfile.py?
Gulzar

37

ในการนำเข้าโมดูลของคุณคุณจะต้องเพิ่มไดเรกทอรีลงในตัวแปรสภาพแวดล้อมทั้งชั่วคราวหรือถาวร

ชั่วคราว

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

อย่างถาวร

การเพิ่มบรรทัดต่อไปนี้ใน.bashrcไฟล์ของคุณ(เป็น linux) และ excecute source ~/.bashrcในเทอร์มินัล:

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

เครดิต / ที่มา: saarrrr , คำถาม stackexchange อื่น


3
วิธีการแก้ปัญหา "temp" นี้เป็นคำตอบที่ดีถ้าคุณต้องการที่จะทำโปรเจ็กต์รอบ ๆ
fordy

แต่ ... มันเป็นอันตรายจากการเข้าไปยุ่งเกี่ยวกับเส้นทาง
ไชอาลอน

@ShaiAlon คุณกำลังเพิ่มเส้นทางดังนั้นจึงไม่มีอันตรายใด ๆ นอกจากเมื่อคุณถ่ายโอนรหัสจากคอมพิวเตอร์เครื่องหนึ่งไปยังอีกเครื่องเส้นทางอาจทำให้เกิดความสับสน ดังนั้นสำหรับการพัฒนาแพ็คเกจฉันจะนำเข้าเฉพาะแพ็คเกจเท่านั้น นอกจากนี้ชื่อแพ็คเกจควรไม่ซ้ำกัน หากคุณกังวลให้ใช้วิธีแก้ปัญหาชั่วคราว
Miladiouss

28

ดูเหมือนว่าคุณไม่ต้องการนำเข้าไฟล์การกำหนดค่าโดยเฉพาะ (ซึ่งมีผลข้างเคียงมากมายและมีปัญหาแทรกซ้อนเพิ่มเติมที่เกี่ยวข้อง) คุณเพียงแค่ต้องการเรียกใช้และสามารถเข้าถึงเนมสเปซที่เกิดขึ้นได้ ไลบรารีมาตรฐานจัดเตรียม API เฉพาะสำหรับรูปแบบของrunpy.run_path :

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

ส่วนต่อประสานนั้นมีอยู่ใน Python 2.7 และ Python 3.2+


ฉันชอบวิธีนี้ แต่เมื่อฉันได้รับผลลัพธ์ของ run_path เป็นพจนานุกรมที่ฉันไม่สามารถเข้าถึงได้
Stephen Ellwood

คุณหมายถึง "ไม่สามารถเข้าถึง" หมายความว่าอย่างไร คุณไม่สามารถนำเข้าจากมัน (ที่ว่าทำไมนี้เป็นเพียงตัวเลือกที่ดีเมื่อมีการเข้าถึงนำเข้าแบบไม่จำเป็นต้องใช้จริง) แต่เนื้อหาควรจะสามารถใช้ได้ผ่านทาง API Dict ปกติ ( result[name], result.get('name', default_value)ฯลฯ )
ncoghlan

คำตอบนี้ถูกประเมินต่ำเกินไป มันสั้นและเรียบง่ายมาก! ยิ่งไปกว่านั้นถ้าคุณต้องการเนมสเปซโมดูลที่เหมาะสมคุณสามารถทำอะไรเช่น from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
RuRo

20

คุณยังสามารถทำสิ่งนี้และเพิ่มไดเรกทอรีที่ไฟล์การกำหนดค่านั่งอยู่ในเส้นทางโหลด Python และจากนั้นก็ทำการอิมพอร์ตตามปกติโดยสมมติว่าคุณรู้ชื่อไฟล์ล่วงหน้าในกรณีนี้ "config"

ยุ่ง แต่ก็ใช้งานได้

configfile = '~/config.py'

import os
import sys

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

import config

นั่นไม่ได้เป็นแบบไดนามิก
ไชอาลอน

ฉันพยายาม: config_file = 'การตั้งค่าสำหรับการแชท', setup_file = get_setup_file (config_file + ".py"), sys.path.append (os.path.dirname (os.path.expanduser (setup_file))) นำเข้า config_file >> "ImportError: ไม่มีโมดูลชื่อ config_file"
Shai Alon

17

คุณสามารถใช้

load_source(module_name, path_to_file) 

วิธีการจากโมดูลภูตผีปีศาจ


... และimp.load_dynamic(module_name, path_to_file)สำหรับ DLLs
HEKTO

34
มุ่งหน้าไปที่ imp ถูกเลิกใช้ตอนนี้
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
ทำไมต้องเขียนโค้ด buggy 14 บรรทัดเมื่อสิ่งนี้ได้รับการจัดการโดยไลบรารี่มาตรฐานแล้ว? คุณยังไม่ได้ตรวจสอบข้อผิดพลาดเกี่ยวกับรูปแบบหรือเนื้อหาของ full_path_to_module หรือการดำเนินการ os.whever และการใช้except:ประโยคcatch-all เป็นความคิดที่ดี
Chris Johnson

คุณควรใช้ "ลอง - สุดท้าย" เพิ่มเติมในที่นี่ Egsave_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
kay - SE นั้นชั่วร้าย

11
@ChrisJohnson this is already addressed by the standard libraryใช่ แต่ไพ ธ อนมีนิสัยที่น่ารังเกียจที่จะไม่เข้ากันได้แบบย้อนหลัง ... เนื่องจากคำตอบที่ตรวจสอบแล้วบอกว่ามี 2 วิธีที่ต่างกันก่อนและหลัง 3.3 ในกรณีนั้นฉันอยากจะเขียนฟังก์ชั่นสากลของตัวเองมากกว่าตรวจสอบเวอร์ชั่นทันที และใช่บางทีรหัสนี้อาจป้องกันข้อผิดพลาดได้ไม่ดีนัก แต่มันแสดงให้เห็นแนวคิด (ซึ่งก็คือ os.chdir () ฉันไม่ได้คิดเกี่ยวกับมัน) โดยอ้างอิงจากที่ฉันสามารถเขียนรหัสได้ดีกว่า ดังนั้น +1
Sushi271

13

นี่คือโค้ดบางส่วนที่ใช้ได้กับ Python ทุกรุ่นตั้งแต่ 2.7-3.5 และอาจเป็นรหัสอื่น ๆ

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

ฉันทดสอบมัน มันอาจจะดูน่าเกลียด แต่จนถึงตอนนี้เป็นรุ่นเดียวที่ทำงานได้ในทุกรุ่น


1
คำตอบนี้เหมาะสำหรับฉันที่load_sourceไม่ได้เพราะมันนำเข้าสคริปต์และให้การเข้าถึงสคริปต์เพื่อโมดูลและกลมในเวลาที่นำเข้า
Klik

13

ฉันได้คำตอบที่ยอดเยี่ยมของ@ SebastianRittau มาแล้ว (สำหรับ Python> 3.4 ฉันคิดว่า) ซึ่งจะช่วยให้คุณโหลดไฟล์ที่มีนามสกุลเป็นโมดูลที่ใช้spec_from_loaderแทน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)

ข้อดีของการเข้ารหัสเส้นทางในที่ชัดเจนSourceFileLoaderคือเครื่องจักรจะไม่พยายามค้นหาประเภทของไฟล์จากส่วนขยาย ซึ่งหมายความว่าคุณสามารถโหลดสิ่งที่ต้องการ.txtไฟล์โดยใช้วิธีการนี้ แต่คุณไม่สามารถทำมันได้ด้วยspec_from_file_locationโดยไม่ต้องระบุโหลดเพราะไม่ได้อยู่ใน.txtimportlib.machinery.SOURCE_SUFFIXES


13

คุณหมายถึงการโหลดหรือนำเข้า?

คุณสามารถจัดการsys.pathรายการระบุพา ธ ไปยังโมดูลของคุณจากนั้นอิมพอร์ตโมดูลของคุณ ตัวอย่างเช่นกำหนดโมดูลที่:

/foo/bar.py

คุณสามารถทำได้:

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

1
@ ความร้อนทำไม sys.path [0: 0] แทนที่จะเป็น sys.path [0]
user618677

5
B / c sys.path [0] = xy เขียนทับรายการเส้นทางแรกในขณะที่เส้นทาง [0: 0] = xy เทียบเท่ากับ path.insert (0, xy)
dom0

2
hm path.insert ใช้งานได้สำหรับฉัน แต่เคล็ดลับ [0: 0] ไม่ได้
jsh

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

6
Explicit is better than implicit.ดังนั้นทำไมไม่sys.path.insert(0, ...)แทนsys.path[0:0]?
winklerrr

8

ฉันเชื่อว่าคุณสามารถใช้imp.find_module()และimp.load_module()โหลดโมดูลที่ระบุได้ คุณจะต้องแยกชื่อโมดูลออกจากเส้นทางเช่นถ้าคุณต้องการโหลด/home/mypath/mymodule.pyคุณต้องทำ:

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

... แต่นั่นควรจะทำให้งานเสร็จ


6

คุณสามารถใช้pkgutilโมดูล (โดยเฉพาะwalk_packagesเมธอด) เพื่อรับรายการแพ็กเกจในไดเร็กทอรีปัจจุบัน การใช้importlibเครื่องจักรเพื่อนำเข้าโมดูลที่คุณต้องการมีดังนี้:

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 test.py

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

สร้างโมดูลหลาม test_check.py

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

เราสามารถนำเข้าโมดูลที่นำเข้าจากโมดูล


4

Python 3.4 ในพื้นที่นี้ดูเหมือนจะเข้าใจผิดอย่างยิ่ง! อย่างไรก็ตามด้วยการแฮ็คเล็กน้อยโดยใช้รหัสจาก Chris Calloway เป็นจุดเริ่มต้นฉันจัดการเพื่อให้ทำงานบางอย่างได้ นี่คือฟังก์ชั่นพื้นฐาน

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

ดูเหมือนว่าจะใช้โมดูลที่ไม่สนับสนุนจาก Python 3.4 ฉันไม่แกล้งทำเป็นเข้าใจว่าทำไม แต่ดูเหมือนว่าจะทำงานได้จากภายในโปรแกรม ฉันพบว่าโซลูชันของ Chris ทำงานบนบรรทัดคำสั่ง แต่ไม่ได้มาจากภายในโปรแกรม


4

ฉันไม่ได้บอกว่ามันดีกว่า แต่เพื่อความสมบูรณ์ฉันต้องการแนะนำexecฟังก์ชั่นที่มีทั้ง python 2 และ 3 execช่วยให้คุณสามารถรันโค้ดโดยพลการในขอบเขตทั่วโลกหรือในขอบเขตภายใน ให้ไว้เป็นพจนานุกรม

ตัวอย่างเช่นหากคุณมีโมดูลที่จัดเก็บใน"/path/to/module"พร้อมกับฟังก์ชั่นfoo()คุณสามารถเรียกใช้งานได้โดยทำดังต่อไปนี้:

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

สิ่งนี้ทำให้ชัดเจนยิ่งขึ้นอีกเล็กน้อยที่คุณกำลังโหลดโค้ดแบบไดนามิกและให้พลังงานเพิ่มเติมแก่คุณเช่นความสามารถในการสร้างบิวด์อินแบบกำหนดเอง

และหากมีการเข้าถึงผ่านแอตทริบิวต์แทนที่จะเป็นกุญแจสำคัญสำหรับคุณคุณสามารถออกแบบคลาส dict แบบกำหนดเองสำหรับ globals ที่ให้การเข้าถึงดังกล่าวเช่น:

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

4

ในการนำเข้าโมดูลจากชื่อไฟล์ที่กำหนดคุณสามารถขยายเส้นทางชั่วคราวและเรียกคืนเส้นทางของระบบในการอ้างอิงบล็อกในที่สุด:

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

สิ่งนี้น่าจะใช้ได้

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
วิธีทั่วไปที่จะตัดส่วนขยายออกคือ: name, ext = os.path.splitext(os.path.basename(infile)). วิธีการของคุณใช้งานได้เนื่องจากข้อ จำกัด ก่อนหน้านี้สำหรับส่วนขยาย. py นอกจากนี้คุณควรนำเข้าโมดูลไปยังรายการตัวแปร / พจนานุกรมบางรายการ
ReneSac

3

หากเรามีสคริปต์ในโครงการเดียวกัน แต่ในไดเรกทอรีที่แตกต่างกันหมายถึงเราสามารถแก้ปัญหานี้ได้ด้วยวิธีการดังต่อไปนี้

ในสถานการณ์เช่นutils.pyนี้src/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

ฉันทำแพ็คเกจที่ใช้impสำหรับคุณ ฉันเรียกมันimport_fileและนี่คือวิธีการใช้งาน:

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

คุณสามารถรับได้ที่:

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

หรือที่

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


1
os.chdir? (ตัวอักษรน้อยที่สุดเพื่ออนุมัติความคิดเห็น)
ychaouche

ฉันใช้เวลาตลอดทั้งวันในการแก้ไขปัญหาข้อผิดพลาดในการนำเข้าใน exe pyinstaller ที่สร้างขึ้น ในที่สุดนี่เป็นสิ่งเดียวที่ทำงานให้ฉัน ขอบคุณมากที่ทำสิ่งนี้!
frakman1

2

นำเข้าโมดูลแพ็คเกจที่รันไทม์ (สูตร 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

ใน Linux การเพิ่มลิงก์สัญลักษณ์ในไดเรกทอรีสคริปต์ของคุณจะอยู่ในตำแหน่ง

เช่น:

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

หลามจะสร้าง/absolute/path/to/script/module.pycและจะอัปเดตหากคุณเปลี่ยนเนื้อหาของ/absolute/path/to/module/module.py

จากนั้นให้รวมสิ่งต่อไปนี้ใน mypythonscript.py

from module import *

1
นี่คือแฮ็คที่ฉันใช้และมันทำให้ฉันมีปัญหา หนึ่งในสิ่งที่เจ็บปวดกว่านั้นคือความคิดที่มีปัญหาที่มันไม่ได้รับการเปลี่ยนแปลงรหัสจากภายในลิงค์ แต่ยังพยายามที่จะบันทึกสิ่งที่คิดว่ามี สภาพการแข่งขันที่คนสุดท้ายที่บันทึกคือสิ่งที่ติด ... ฉันสูญเสียงานที่เหมาะสมเนื่องจากสิ่งนี้
Gripp

@Gripp ไม่แน่ใจว่าฉันเข้าใจปัญหาของคุณหรือไม่ แต่ฉันมักจะแก้ไขสคริปต์ของฉันบนเซิร์ฟเวอร์ระยะไกลจากเดสก์ท็อปของฉันผ่าน SFTP กับไคลเอนต์อย่าง CyberDuck และในกรณีนี้ก็เป็นความคิดที่ดีที่จะลองและ แก้ไขไฟล์ symlinked แทนที่จะปลอดภัยกว่าในการแก้ไขไฟล์ต้นฉบับ คุณสามารถgitตรวจสอบปัญหาเหล่านี้ได้โดยใช้และตรวจสอบgit statusเพื่อยืนยันว่าการเปลี่ยนแปลงของคุณกับสคริปต์ทำให้มันกลับไปที่เอกสารต้นฉบับและไม่หลงทางในอีเธอร์
user5359531

2

ฉันได้เขียนฟังก์ชั่นการนำเข้าทั่วโลกและพกพาของฉันเองตามimportlibโมดูลสำหรับ:

  • สามารถนำเข้าโมดูลทั้งสองเป็น submodule และนำเข้าเนื้อหาของโมดูลไปยังโมดูลหลัก (หรือเข้าสู่ globals หากไม่มีโมดูลหลัก)
  • สามารถนำเข้าโมดูลที่มีอักขระแบบจุดในชื่อไฟล์
  • สามารถนำเข้าโมดูลที่มีส่วนขยายใด ๆ
  • สามารถใช้ชื่อแบบสแตนด์อโลนสำหรับ submodule แทนชื่อไฟล์โดยไม่มีนามสกุลซึ่งเป็นค่าเริ่มต้น
  • สามารถกำหนดลำดับการนำเข้าตามโมดูลที่นำเข้าก่อนหน้านี้แทนที่จะขึ้นอยู่กับsys.pathหรือที่เก็บข้อมูลพา ธ การค้นหาที่เคยมีมา

โครงสร้างไดเรกทอรีตัวอย่าง:

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

การรวมการพึ่งพาและคำสั่งซื้อ:

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

การดำเนินงาน:

การเปลี่ยนแปลงล่าสุดของร้านค้า: 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')

ผลลัพธ์ ( 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

ผ่านการทดสอบในหลาม3.7.4, 3.2.5,2.7.16

ข้อดี :

  • สามารถนำเข้าโมดูลทั้งสองเป็น submodule และสามารถนำเข้าเนื้อหาของโมดูลไปยังโมดูลหลัก (หรือเข้าสู่ globals หากไม่มีโมดูลหลัก)
  • สามารถนำเข้าโมดูลที่มีเครื่องหมายจุดในชื่อไฟล์
  • สามารถนำเข้าโมดูลส่วนขยายใด ๆ จากโมดูลส่วนขยายใด ๆ
  • สามารถใช้ชื่อแบบสแตนด์อโลนสำหรับ submodule แทนชื่อไฟล์ที่ไม่มีนามสกุลซึ่งเป็นค่าเริ่มต้น (เช่นtestlib.std.pyเป็นtestlib, testlib.blabla.pyเป็นtestlib_blablaและอื่น ๆ )
  • ไม่ได้ขึ้นอยู่กับsys.pathพื้นที่เก็บข้อมูลการค้นหาพา ธ
  • ไม่จำเป็นต้องมีการบันทึก / เรียกคืนตัวแปรระดับโลกเช่นSOURCE_FILEและระหว่างการโทรไปยังSOURCE_DIRtkl_import_module
  • [สำหรับ3.4.xและสูงกว่า] สามารถผสม namespaces โมดูลในซ้อนกันtkl_import_moduleโทร (เช่น: named->local->namedหรือlocal->named->localและอื่น ๆ )
  • [สำหรับ3.4.xและสูงกว่า] สามารถส่งออกตัวแปร / ฟังก์ชั่น / คลาสส่วนกลางโดยอัตโนมัติจากตำแหน่งที่ถูกประกาศไปยังโมดูลลูกทั้งหมดที่นำเข้าผ่านtkl_import_module(ผ่านtkl_declare_globalฟังก์ชั่น)

ข้อเสีย :

  • [สำหรับ3.3.xและต่ำกว่า] จำเป็นต้องประกาศtkl_import_moduleในโมดูลทั้งหมดที่โทรไปtkl_import_module(การทำสำเนารหัส)

อัปเดต 1,2 (สำหรับ3.4.xและสูงกว่าเท่านั้น):

ใน Python 3.4 และสูงกว่าคุณสามารถข้ามข้อกำหนดในการประกาศtkl_import_moduleในแต่ละโมดูลโดยประกาศtkl_import_moduleในโมดูลระดับบนสุดและฟังก์ชั่นจะฉีดตัวเองไปยังโมดูลลูกทั้งหมดในการโทรเพียงครั้งเดียว

อัปเดต 3 :

เพิ่มฟังก์ชั่นtkl_source_moduleเป็นแบบอะนาล็อกเพื่อทุบตีsourceด้วยตัวป้องกันการเรียกใช้งานสนับสนุนเมื่อนำเข้า (ใช้งานผ่านการผสานโมดูลแทนการนำเข้า)

อัปเดต 4 :

เพิ่มฟังก์ชั่นtkl_declare_globalเพื่อส่งออกตัวแปรส่วนกลางของโมดูลไปยังโมดูลย่อยทั้งหมดโดยอัตโนมัติซึ่งไม่สามารถมองเห็นตัวแปรโมดูลส่วนกลางได้เนื่องจากไม่ใช่ส่วนหนึ่งของโมดูลย่อย

อัปเดต 5 :

ฟังก์ชั่นทั้งหมดได้ย้ายเข้าไปในห้องสมุด tacklelib ดูลิงค์ด้านบน


2

มีแพ็คเกจที่อุทิศให้กับสิ่งนี้โดยเฉพาะ:

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 (Jython และ PyPy ด้วย) แต่มันอาจมีค่ามากเกินไปขึ้นอยู่กับขนาดของโครงการของคุณ


1

การเพิ่มสิ่งนี้ลงในรายการคำตอบเนื่องจากฉันไม่พบสิ่งใดที่ทำงานได้ สิ่งนี้จะอนุญาตการนำเข้าโมดูลหลามที่คอมไพล์แล้ว (pyd) ใน 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

ค่อนข้างง่าย: สมมติว่าคุณต้องการนำเข้าไฟล์ที่มีพา ธ สัมพัทธ์ ../../MyLibs/pyfunc.py


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

แต่ถ้าคุณทำโดยไม่มีผู้พิทักษ์คุณจะได้เส้นทางที่ยาวที่สุด


1

วิธีง่ายๆในการใช้importlibแทนimpแพ็คเกจ (ทดสอบสำหรับ Python 2.7 แต่ควรใช้กับ 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")

ตอนนี้คุณสามารถใช้เนมสเปซของโมดูลที่อิมพอร์ตโดยตรงเช่นนี้:

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

ข้อดีของการแก้ปัญหานี้คือเราไม่จำเป็นต้องรู้ชื่อจริงของโมดูลที่เราต้องการนำเข้าเพื่อใช้ในโค้ดของเรา สิ่งนี้มีประโยชน์เช่นในกรณีที่เส้นทางของโมดูลเป็นอาร์กิวเมนต์ที่กำหนดค่าได้


วิธีนี้คุณกำลังแก้ไขsys.pathซึ่งไม่เหมาะกับทุกกรณีการใช้งาน
bgusach

@bgusach สิ่งนี้อาจเป็นจริง แต่ก็เป็นที่ต้องการในบางกรณี (การเพิ่มพา ธ ไปยัง sys.path จะทำให้สิ่งต่าง ๆ ง่ายขึ้นเมื่อนำเข้าโมดูลมากกว่าหนึ่งจากแพ็คเกจเดียว) ไม่ว่าในกรณีใดถ้าสิ่งนี้ไม่เป็นที่พึงปรารถนาใคร ๆ ก็สามารถทำได้ทันทีsys.path.pop()
Ataxias

0

คำตอบนี้เป็นส่วนเสริมของคำตอบของ Sebastian Rittau ที่ตอบสนองต่อความคิดเห็น: "แต่ถ้าคุณไม่มีชื่อโมดูล" นี่เป็นวิธีที่รวดเร็วและสกปรกในการรับชื่อโมดูลของไพ ธ อนที่ให้ชื่อไฟล์ - มันจะขึ้นไปบนต้นไม้จนกว่ามันจะพบไดเรกทอรีที่ไม่มี__init__.pyไฟล์แล้วเปลี่ยนมันกลับไปเป็นชื่อไฟล์ สำหรับ Python 3.4+ (ใช้ pathlib) ซึ่งสมเหตุสมผลเนื่องจาก Py2 คนสามารถใช้ "imp" หรือวิธีอื่น ๆ ในการนำเข้าแบบสัมพันธ์:

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

มีความเป็นไปได้ในการปรับปรุงอย่างแน่นอนและ__init__.pyไฟล์เสริมอาจจำเป็นต้องมีการเปลี่ยนแปลงอื่น ๆ แต่ถ้าคุณมี__init__.pyโดยทั่วไปสิ่งนี้จะเป็นการหลอกลวง


-1

ฉันคิดว่าวิธีที่ดีที่สุดมาจากเอกสารอย่างเป็นทางการ ( 29.1 Imp - เข้าถึง internals การนำเข้า ):

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
โซลูชันนี้ไม่อนุญาตให้คุณระบุเส้นทางซึ่งเป็นคำถามที่ถาม
คาห์สมิ ธ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.