Python: Cách tốt nhất để thêm vào sys.path liên quan đến tập lệnh đang chạy hiện tại


99

Tôi có một thư mục chứa đầy các tập lệnh (giả sử project/bin). Tôi cũng có một thư viện nằm trong đó project/libvà muốn các tập lệnh tự động tải nó. Đây là những gì tôi thường sử dụng ở đầu mỗi tập lệnh:

#!/usr/bin/python
from os.path import dirname, realpath, sep, pardir
import sys
sys.path.append(dirname(realpath(__file__)) + sep + pardir + sep + "lib")

# ... now the real code
import mylib

Đây là loại cồng kềnh, xấu xí và phải được dán ở đầu mỗi tệp. Có cách nào tốt hơn để làm điều này?

Thực sự những gì tôi hy vọng là một cái gì đó suôn sẻ như thế này:

#!/usr/bin/python
import sys.path
from os.path import pardir, sep
sys.path.append_relative(pardir + sep + "lib")

import mylib

Hoặc thậm chí tốt hơn, điều gì đó sẽ không xảy ra khi người chỉnh sửa của tôi (hoặc người khác có quyền truy cập cam kết) quyết định sắp xếp lại các lần nhập như một phần của quy trình dọn dẹp:

#!/usr/bin/python --relpath_append ../lib
import mylib

Điều đó sẽ không chuyển trực tiếp đến các nền tảng không phải posix, nhưng nó sẽ giữ mọi thứ sạch sẽ.


Câu trả lời:


26

Nếu bạn không muốn chỉnh sửa từng tệp

  • Cài đặt thư viện của bạn như một libray python bình thường
    hoặc
  • Đặt PYTHONPATHthành của bạnlib

hoặc nếu bạn muốn thêm một dòng vào mỗi tệp, hãy thêm câu lệnh nhập ở trên cùng, ví dụ:

import import_my_lib

giữ import_my_lib.pytrong thùng và import_my_libcó thể đặt chính xác đường dẫn python thành bất kỳ thứ gì libbạn muốn


118

Đây là những gì tôi sử dụng:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))

2
Tôi sẽ làm sys.path.insert (0, ..) để nó không bị các đường dẫn khác ghi đè.
John Jiang

Thực sự không có cách nào để làm cho điều này chạy tự động?
Denis de Bernardy

1
Điều này hơi rủi ro. Nếu __file__là tên tệp tương đối so với thư mục làm việc hiện tại (ví dụ setup.py:), thì os.path.dirname(__file__)sẽ là chuỗi trống. Đối với điều này và những lo ngại tương tự do John Jiang nêu ra , giải pháp có mục đích chung hơn của ekhumoro rất được ưu tiên.
Cecil Curry,

29

Tôi đang sử dụng:

import sys,os
sys.path.append(os.getcwd())

5
Tôi thường làm vậysys.path.append('.')
kimbo

1
điều gì sẽ xảy ra nếu tập lệnh được chạy từ một thư mục khác? ví dụ từ thư mục gốc bằng cách đưa ra đường dẫn hệ thống đầy đủ? sau đó os.getcwd () sẽ trả về "/"
obayhan

2
Đừng bao giờ làm điều này. Thư mục làm việc hiện tại (CWD) không được đảm bảo giống như bạn nghĩ - đặc biệt là trong các trường hợp cạnh không lường trước được mà bạn chắc chắn nên thấy sẽ đi xa một dặm. __file__Thay vào đó, chỉ cần tham khảo như bất kỳ nhà phát triển gần như lành mạnh nào.
Cecil Curry,

14

Tạo một mô-đun trình bao bọc project/bin/lib, chứa mô-đun này:

import sys, os

sys.path.insert(0, os.path.join(
    os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib'))

import mylib

del sys.path[0], sys, os

Sau đó, bạn có thể thay thế tất cả các điểm mấu chốt ở đầu các tập lệnh của mình bằng:

#!/usr/bin/python
from lib import mylib

9

Nếu bạn không muốn thay đổi nội dung tập lệnh theo bất kỳ cách nào, hãy thêm trước thư mục làm việc hiện tại .vào $ PYTHONPATH (xem ví dụ bên dưới)

PYTHONPATH=.:$PYTHONPATH alembic revision --autogenerate -m "First revision"

Và gọi nó là một ngày!


docs.python.org/3/tutorial/modules.html#the-module-search-path nói: "sys.path được khởi tạo từ những vị trí này: -Thư mục chứa tập lệnh đầu vào". Tôi nghĩ điều này không đúng.

Tôi có xu hướng làm điều này, đặc biệt là trong .envrc để với direnv thậm chí là tự động và cô lập.
EdvardM

8

Sử dụng python
3.4+ Cấm sử dụng cx_freeze hoặc sử dụng trong IDLE. 😃

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")

Python2 tương thích: nhập khẩu sys.path.append pathlib import os (os.path.dirname ( tập tin ))
michael

5

Bạn có thể chạy script với python -mtừ root dir có liên quan. Và chuyển "đường dẫn mô-đun" làm đối số.

Thí dụ: $ python -m module.sub_module.main # Notice there is no '.py' at the end.


Một vi dụ khac:

$ tree  # Given this file structure
.
├── bar
   ├── __init__.py
   └── mod.py
└── foo
    ├── __init__.py
    └── main.py

$ cat foo/main.py
from bar.mod import print1
print1()

$ cat bar/mod.py
def print1():
    print('In bar/mod.py')

$ python foo/main.py  # This gives an error
Traceback (most recent call last):
  File "foo/main.py", line 1, in <module>
    from bar.mod import print1
ImportError: No module named bar.mod

$ python -m foo.main  # But this succeeds
In bar/mod.py

1
Sử dụng công tắc m là cách được khuyến nghị để thực hiện việc này và không phải là hack đường dẫn hệ thống
Mr_and_Mrs_D

4

Có một vấn đề với mọi câu trả lời được cung cấp có thể được tóm tắt là "chỉ cần thêm câu thần chú kỳ diệu này vào đầu tập lệnh của bạn. Xem bạn có thể làm gì chỉ với một hoặc hai dòng mã". Chúng sẽ không hoạt động trong mọi tình huống có thể xảy ra!

Ví dụ, một câu thần chú kỳ diệu như vậy sử dụng tệp . Thật không may, nếu bạn đóng gói tập lệnh của mình bằng cx_Freeze hoặc bạn đang sử dụng IDLE, điều này sẽ dẫn đến một ngoại lệ.

Một câu thần chú kỳ diệu khác như vậy sử dụng os.getcwd (). Điều này sẽ chỉ hoạt động nếu bạn đang chạy tập lệnh của mình từ dấu nhắc lệnh và thư mục chứa tập lệnh của bạn là thư mục làm việc hiện tại (tức là bạn đã sử dụng lệnh cd để thay đổi thành thư mục trước khi chạy tập lệnh). Ơ trời! Tôi hy vọng tôi không phải giải thích lý do tại sao điều này sẽ không hoạt động nếu tập lệnh Python của bạn nằm trong PATH ở đâu đó và bạn đã chạy nó bằng cách chỉ cần gõ tên tệp tập lệnh của mình.

May mắn thay, có một câu thần chú kỳ diệu sẽ hoạt động trong tất cả các trường hợp tôi đã thử nghiệm. Thật không may, câu thần chú kỳ diệu không chỉ là một hoặc hai dòng mã.

import inspect
import os
import sys

# Add script directory to sys.path.
# This is complicated due to the fact that __file__ is not always defined.

def GetScriptDirectory():
    if hasattr(GetScriptDirectory, "dir"):
        return GetScriptDirectory.dir
    module_path = ""
    try:
        # The easy way. Just use __file__.
        # Unfortunately, __file__ is not available when cx_freeze is used or in IDLE.
        module_path = __file__
    except NameError:
        if len(sys.argv) > 0 and len(sys.argv[0]) > 0 and os.path.isabs(sys.argv[0]):
            module_path = sys.argv[0]
        else:
            module_path = os.path.abspath(inspect.getfile(GetScriptDirectory))
            if not os.path.exists(module_path):
                # If cx_freeze is used the value of the module_path variable at this point is in the following format.
                # {PathToExeFile}\{NameOfPythonSourceFile}. This makes it necessary to strip off the file name to get the correct
                # path.
                module_path = os.path.dirname(module_path)
    GetScriptDirectory.dir = os.path.dirname(module_path)
    return GetScriptDirectory.dir

sys.path.append(os.path.join(GetScriptDirectory(), "lib"))
print(GetScriptDirectory())
print(sys.path)

Như bạn thấy, đây không phải là nhiệm vụ dễ dàng!


0

Cái này phù hợp nhất với tôi. Sử dụng:

os.path.abspath('')

Trên mac, nó sẽ in một cái gì đó như:

'/Users/<your username>/<path_to_where_you_at>'

Để có được đường dẫn abs đến wd hiện tại, cái này tốt hơn vì bây giờ bạn có thể đi lên nếu bạn muốn, như thế này:

os.path.abspath('../')

Và bây giờ:

 '/Users/<your username>/'

Vì vậy, nếu bạn muốn nhập utilstừ đây '/Users/<your username>/'
Tất cả những gì bạn còn lại phải làm là:

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

0

Tôi thấy một shebang trong ví dụ của bạn. Nếu bạn đang chạy các tập lệnh bin của mình ./bin/foo.py, thay vào python ./bin/foo.pyđó, có một tùy chọn sử dụng biến shebang để thay đổi $PYTHONPATH.

Tuy nhiên, bạn không thể thay đổi các biến môi trường trực tiếp trong shebangs, vì vậy bạn sẽ cần một tập lệnh trợ giúp nhỏ. Đặt cái này python.shvào binthư mục của bạn :

#!/usr/bin/env bash
export PYTHONPATH=$PWD/lib
exec "/usr/bin/python" "$@"

Và sau đó thay đổi shebang của bạn ./bin/foo.pythành#!bin/python.sh


0

Khi chúng tôi cố gắng chạy tệp python với đường dẫn từ thiết bị đầu cuối.

import sys
#For file name
file_name=sys.argv[0]
#For first argument
dir= sys.argv[1]
print("File Name: {}, argument dir: {}".format(file_name, dir)

Lưu tệp (test.py).

Hệ thống chạy.

Mở thiết bị đầu cuối và truy cập tệp lưu đó. sau đó viết

python test.py "/home/saiful/Desktop/bird.jpg"

Nhấn Enter

Đầu ra:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg

0

Tôi sử dụng:

from site import addsitedir

Sau đó, có thể sử dụng bất kỳ thư mục tương đối nào! addsitedir('..\lib') ; hai dấu chấm ngụ ý di chuyển (lên) một thư mục trước.

Hãy nhớ rằng tất cả phụ thuộc vào thư mục làm việc hiện tại của bạn bắt đầu từ đâu. Nếu C: \ Joe \ Jen \ Becky, thì additedir ('.. \ lib') sẽ nhập vào đường dẫn của bạn C: \ Joe \ Jen \ lib

C:\
  |__Joe
      |_ Jen
      |     |_ Becky
      |_ lib

Tôi đánh giá cao phản hồi, nhưng điều này không thêm đường dẫn liên quan đến tập lệnh đang chạy hiện tại. Nó thêm một đường dẫn liên quan đến thư mục làm việc hiện tại
James Harr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.