Làm thế nào để tôi có được thư mục mẹ trong Python?


350

Ai đó có thể cho tôi biết làm thế nào để có được thư mục mẹ của một đường dẫn trong Python theo cách đa nền tảng. Ví dụ

C:\Program Files ---> C:\

C:\ ---> C:\

Nếu thư mục không có thư mục cha, nó sẽ trả về thư mục đó. Câu hỏi có vẻ đơn giản nhưng tôi không thể tìm hiểu nó thông qua Google.

Câu trả lời:


481

Cập nhật từ Python 3.4

Sử dụng pathlibmô-đun.

from pathlib import Path
path = Path("/here/your/path/file.txt")
print(path.parent)

Câu trả lời cũ

Thử cái này:

import os.path
print os.path.abspath(os.path.join(yourpath, os.pardir))

đâu yourpathlà con đường bạn muốn cha mẹ cho.


136
Câu trả lời của bạn là chính xác nhưng phức tạp; os.path.dirnamelà chức năng cho việc này, giống như a+=5-4là phức tạp hơn a+=1. Câu hỏi chỉ yêu cầu thư mục cha, không phải là tồn tại hay thư mục mẹ thực sự giả sử các liên kết tượng trưng có được cản trở.
tzot

15
os.pardir, không phải os.path.pardir.
bouteillebleu

9
@bouteillebleu: Cả hai os.pardiros.path.pardirthực sự chính xác (chúng giống hệt nhau).
Eric O Lebigot

45
@tzot: không may os.path.dirnamecho kết quả khác nhau tùy thuộc vào việc một dấu gạch chéo có được bao gồm trong đường dẫn hay không. Nếu bạn muốn kết quả đáng tin cậy, bạn cần sử dụng os.path.joinphương pháp trong câu trả lời ở trên.
Artfunkel

21
Vì điều này rõ ràng đủ phức tạp để đảm bảo một câu hỏi StackOverflow, tôi cảm thấy rằng điều này nên được thêm vào thư viện os.path dưới dạng một hàm tích hợp.
antred

324

Sử dụng os.path.dirname:

>>> os.path.dirname(r'C:\Program Files')
'C:\\'
>>> os.path.dirname('C:\\')
'C:\\'
>>>

Hãy cẩn thận: os.path.dirname()cho các kết quả khác nhau tùy thuộc vào việc một dấu gạch chéo có được bao gồm trong đường dẫn hay không. Điều này có thể hoặc không thể là ngữ nghĩa mà bạn muốn. Cf. @ câu trả lời của kender bằng cách sử dụng os.path.join(yourpath, os.pardir).


6
os.path.dirname(r'C:\Program Files')gì? Python chỉ cung cấp cho bạn thư mục chứa tệp 'Tệp chương trình'. Hơn nữa, nó thậm chí không phải tồn tại, kìa: os.path.dirname(r'c:\i\like\to\eat\pie')đầu ra'c:\\i\\like\\to\\eat'
Nick T

41
Các poster ban đầu không nói rằng thư mục phải tồn tại. Có rất nhiều phương thức tên đường dẫn không làm gì ngoài thao tác chuỗi. Để xác minh nếu tên đường dẫn thực sự tồn tại yêu cầu truy cập đĩa. Phụ thuộc vào ứng dụng này có thể có hoặc không mong muốn.
Wai Yip Tung

10
giải pháp này rất nhạy cảm với dấu vết os.sep. Nói os.sep == '/'. dirname (foo / bar) -> foo, nhưng dirname (foo / bar /) -> foo / bar
marcin

6
Đó là do thiết kế. Nó đi đến việc giải thích một con đường có dấu /. Bạn có xem "path1" bằng với "path1 /" không? Thư viện sử dụng cách giải thích chung nhất rằng chúng là khác biệt. Trong một số bối cảnh mọi người có thể muốn coi chúng là tương đương. Trong trường hợp này, bạn có thể thực hiện một rstrip ('/') trước. Nếu thư viện chọn cách giải thích khác, bạn sẽ mất lòng trung thành.
Wai Yip Tung

3
@Ryan, tôi không biết về điều đó. Có toàn bộ RFC 1808 được viết để giải quyết vấn đề về đường dẫn tương đối trong URI và tất cả sự tinh tế của sự hiện diện và không có dấu vết /. Nếu bạn biết bất kỳ tài liệu nào nói rằng họ nên được đối xử tương đương nói chung, vui lòng chỉ ra.
Ái Mộc Tung

112

Phương thức Pathlib (Python 3,4+)

from pathlib import Path
Path('C:\Program Files').parent
# Returns a Pathlib object

Phương pháp truyền thống

import os.path
os.path.dirname('C:\Program Files')
# Returns a string


Tôi nên sử dụng phương pháp nào?

Sử dụng phương pháp truyền thống nếu:

  • Bạn lo lắng về lỗi tạo mã hiện có nếu sử dụng đối tượng Pathlib. (Vì các đối tượng Pathlib không thể được nối với các chuỗi.)

  • Phiên bản Python của bạn nhỏ hơn 3,4.

  • Bạn cần một chuỗi, và bạn đã nhận được một chuỗi. Ví dụ, bạn có một chuỗi đại diện cho một filepath và bạn muốn lấy thư mục mẹ để bạn có thể đặt nó trong một chuỗi JSON. Sẽ thật ngớ ngẩn khi chuyển đổi thành đối tượng Pathlib và quay lại lần nữa vì điều đó.

Nếu không có điều nào ở trên áp dụng, hãy sử dụng Pathlib.



Pathlib là gì?

Nếu bạn không biết Pathlib là gì, mô-đun Pathlib là một mô-đun tuyệt vời giúp làm việc với các tệp thậm chí còn dễ dàng hơn cho bạn. Hầu hết nếu không phải tất cả các mô-đun Python tích hợp hoạt động với các tệp sẽ chấp nhận cả các đối tượng và chuỗi Pathlib. Tôi đã nhấn mạnh bên dưới một vài ví dụ từ tài liệu Pathlib giới thiệu một số điều gọn gàng bạn có thể làm với Pathlib.

Điều hướng trong cây thư mục:

>>> p = Path('/etc')
>>> q = p / 'init.d' / 'reboot'
>>> q
PosixPath('/etc/init.d/reboot')
>>> q.resolve()
PosixPath('/etc/rc.d/init.d/halt')

Truy vấn thuộc tính đường dẫn:

>>> q.exists()
True
>>> q.is_dir()
False

4
Đây là câu trả lời lành mạnh duy nhất. Nếu bạn buộc phải sử dụng Python 2, chỉ cần pip install pathlib2sử dụng backport.
Navin

1
Giải pháp này KHÔNG nhạy cảm với dấu vết os.sep!
Dylan F

35
import os
p = os.path.abspath('..')

C:\Program Files ---> C:\\\

C:\ ---> C:\\\


7
Điều này chỉ nhận được cha mẹ của CWD chứ không phải cha mẹ của một con đường tùy ý như OP yêu cầu.
Sergio

Thêm dấu chấm kép ở cuối URL của bạn và nó sẽ hoạt động Ví dụ: os.path.abspath(r'E:\O3M_Tests_Embedded\branches\sw_test_level_gp\test_scripts\..\..') Kết quả:E:\\O3M_Tests_Embedded\\branches
Arindam Roychowdhury

Điều này có nghĩa là : /.
loretoparisi

26

Một giải pháp thay thế của @kender

import os
os.path.dirname(os.path.normpath(yourpath))

Ở đâu yourpathlà con đường bạn muốn cha mẹ cho.

Nhưng giải pháp này không hoàn hảo, vì nó sẽ không xử lý trường hợp yourpath chuỗi rỗng hoặc dấu chấm.

Giải pháp khác này sẽ xử lý độc đáo hơn trường hợp góc này:

import os
os.path.normpath(os.path.join(yourpath, os.pardir))

Ở đây các đầu ra cho mọi trường hợp có thể tìm thấy (Đường dẫn đầu vào là tương đối):

os.path.dirname(os.path.normpath('a/b/'))          => 'a'
os.path.normpath(os.path.join('a/b/', os.pardir))  => 'a'

os.path.dirname(os.path.normpath('a/b'))           => 'a'
os.path.normpath(os.path.join('a/b', os.pardir))   => 'a'

os.path.dirname(os.path.normpath('a/'))            => ''
os.path.normpath(os.path.join('a/', os.pardir))    => '.'

os.path.dirname(os.path.normpath('a'))             => ''
os.path.normpath(os.path.join('a', os.pardir))     => '.'

os.path.dirname(os.path.normpath('.'))             => ''
os.path.normpath(os.path.join('.', os.pardir))     => '..'

os.path.dirname(os.path.normpath(''))              => ''
os.path.normpath(os.path.join('', os.pardir))      => '..'

os.path.dirname(os.path.normpath('..'))            => ''
os.path.normpath(os.path.join('..', os.pardir))    => '../..'

Đường dẫn đầu vào là tuyệt đối (đường dẫn Linux):

os.path.dirname(os.path.normpath('/a/b'))          => '/a'
os.path.normpath(os.path.join('/a/b', os.pardir))  => '/a'

os.path.dirname(os.path.normpath('/a'))            => '/'
os.path.normpath(os.path.join('/a', os.pardir))    => '/'

os.path.dirname(os.path.normpath('/'))             => '/'
os.path.normpath(os.path.join('/', os.pardir))     => '/'

Bình thường hóa đường dẫn luôn là một thực hành tốt, đặc biệt là khi thực hiện công việc đa nền tảng.
DevPlayer

Đây là câu trả lời chính xác! Nó giữ đường dẫn tương đối. Cảm ơn!
Tối đa

@Maxim Giải pháp này không hoàn hảo, tôi đã cải thiện nó vì giải pháp ban đầu không xử lý một trường hợp
benjarobin

@benjarobin Vâng, tôi đã không nghĩ đến trường hợp góc. Cảm ơn.
Maxim

18
os.path.split(os.path.abspath(mydir))[0]

Điều này sẽ không hoạt động đối với các đường dẫn đến một thư mục, nó sẽ chỉ trả lại thư mục một lần nữa.
Anthony Briggs

2
@AnthonyBriggs, tôi vừa thử cái này bằng Python 2.7.3 trên Ubuntu 12.04 và nó có vẻ hoạt động tốt. os.path.split(os.path.abspath("this/is/a/dir/"))[0]trả lại '/home/daniel/this/is/a'như mong đợi. Hiện tại tôi không có hộp Windows đang chạy để kiểm tra. Trên thiết lập nào bạn đã quan sát hành vi mà bạn báo cáo?
Dan Menes

Bạn có thể làm parentdir = os.path.split(os.path.apspath(dir[:-1]))[0]. Điều này - tôi chắc chắn - hoạt động bởi vì nếu có một dấu gạch chéo ở cuối, thì nó sẽ bị xóa; nếu không có dấu gạch chéo, điều này vẫn sẽ hoạt động (ngay cả khi phần cuối của đường dẫn chỉ có một char dài) vì dấu gạch chéo trước. Tất nhiên, điều này giả định rằng đường dẫn là đúng và không nói điều gì đó giống như /a//b/c///d////(trong unix, điều này vẫn hợp lệ), trong hầu hết các trường hợp chúng là (đúng) đặc biệt là khi bạn làm một cái gì đó như os.path.abspathhoặc bất kỳ os.pathchức năng nào khác .
dylnmc

Ngoài ra, để chống lại rất nhiều dấu gạch chéo ở cuối, bạn chỉ cần viết một vòng lặp nhỏ để loại bỏ các vòng lặp. Tôi chắc chắn thậm chí có thể có một lớp lót thông minh để làm điều đó, hoặc có thể làm điều đó và os.path.split trong một dòng.
dylnmc

@Den Menes Tôi vừa thấy bạn bình luận. Nó không hoạt động nếu bạn có một cái gì đó như os.path.split("a/b//c/d///")và, ví dụ, cd //////dev////// is equivalent to cd / dev / `hoặc cd /dev; Tất cả những thứ này đều hợp lệ trong linux. Tôi chỉ nghĩ ra điều này và nó có thể hữu ích, mặc dù : os.path.split(path[:tuple(ind for ind, char in enumerate(path) if char != "/" and char != "\\")[-1]])[0]. (Điều này về cơ bản là tìm kiếm dấu gạch chéo cuối cùng và nhận chuỗi con của đường dẫn đến char đó.) Tôi đã sử dụng path = "/a//b///c///d////"và sau đó chạy câu lệnh đã nói ở trên và nhận được '/a//b///c'.
dylnmc

14
os.path.abspath(os.path.join(somepath, '..'))

Quan sát:

import posixpath
import ntpath

print ntpath.abspath(ntpath.join('C:\\', '..'))
print ntpath.abspath(ntpath.join('C:\\foo', '..'))
print posixpath.abspath(posixpath.join('/', '..'))
print posixpath.abspath(posixpath.join('/home', '..'))

7
import os
print"------------------------------------------------------------"
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
print("example 1: "+SITE_ROOT)
PARENT_ROOT=os.path.abspath(os.path.join(SITE_ROOT, os.pardir))
print("example 2: "+PARENT_ROOT)
GRANDPAPA_ROOT=os.path.abspath(os.path.join(PARENT_ROOT, os.pardir))
print("example 3: "+GRANDPAPA_ROOT)
print "------------------------------------------------------------"

6

Nếu bạn muốn chỉtên của thư mục đó là cha mẹ trực tiếp của các tập tin được cung cấp như một cuộc tranh cãi và không các đường dẫn tuyệt đối đến tập tin đó:

os.path.split(os.path.dirname(currentDir))[1]

tức là với currentDirgiá trị là/home/user/path/to/myfile/file.ext

Lệnh trên sẽ trả về:

myfile


3
os.path.basename (os.path.dirname (current_dir)) cũng hoạt động ở đây.
DevPlayer

4
>>> import os
>>> os.path.basename(os.path.dirname(<your_path>))

Ví dụ trong Ubuntu:

>>> my_path = '/home/user/documents'
>>> os.path.basename(os.path.dirname(my_path))
# Output: 'user'

Ví dụ trong Windows:

>>> my_path = 'C:\WINDOWS\system32'
>>> os.path.basename(os.path.dirname(my_path))
# Output: 'WINDOWS'

Cả hai ví dụ đã thử trong Python 2.7


3
import os.path

os.path.abspath(os.pardir)

Điều này giả sử bạn muốn thư mục mẹ của "thư mục làm việc hiện tại" chứ không phải thư mục mẹ nói chung bất kỳ đường dẫn nào.
DevPlayer

3

Giả sử chúng ta có cấu trúc thư mục như

1]

/home/User/P/Q/R

Chúng tôi muốn truy cập đường dẫn "P" từ thư mục R thì chúng tôi có thể truy cập bằng

ROOT = os.path.abspath(os.path.join("..", os.pardir));

2]

/home/User/P/Q/R

Chúng tôi muốn truy cập đường dẫn của thư mục "Q" từ thư mục R thì chúng tôi có thể truy cập bằng

ROOT = os.path.abspath(os.path.join(".", os.pardir));

2

Chỉ cần thêm một cái gì đó vào câu trả lời của Tung (bạn cần sử dụng rstrip('/')để trở nên an toàn hơn nếu bạn ở trên một hộp unix).

>>> input = "../data/replies/"
>>> os.path.dirname(input.rstrip('/'))
'../data'
>>> input = "../data/replies"
>>> os.path.dirname(input.rstrip('/'))
'../data'

Nhưng, nếu bạn không sử dụng rstrip('/'), với đầu vào của bạn là

>>> input = "../data/replies/"

sẽ đầu ra,

>>> os.path.dirname(input)
'../data/replies'

đó có lẽ không phải là những gì bạn đang nhìn khi bạn muốn cả hai "../data/replies/""../data/replies"cư xử theo cùng một cách.


1
Tôi khuyên bạn không nên sử dụng "đầu vào" như một biến / tham chiếu. Nó là một chức năng tích hợp.
DevPlayer

2
import os

dir_path = os.path.dirname(os.path.realpath(__file__))
parent_path = os.path.abspath(os.path.join(dir_path, os.pardir))

1
print os.path.abspath(os.path.join(os.getcwd(), os.path.pardir))

Bạn có thể sử dụng điều này để có được thư mục mẹ của vị trí hiện tại của tệp py của bạn.


2
Đề nghị đó thường dẫn đến lỗi. os.getcwd () thường KHÔNG phải là "tập tin py của bạn". Hãy suy nghĩ gói. Nếu tôi "nhập some_package_with_subpackages", nhiều mô-đun sẽ không nằm trong thư mục trên cùng của gói đó. os.getcwd () trả về nơi bạn thực thi tập lệnh hàng đầu. Và điều đó cũng cho rằng bạn đang làm điều đó từ một dòng lệnh.
DevPlayer

0

NHẬN đường dẫn thư mục gốctạo thư mục mới (tên new_dir)

Nhận đường dẫn thư mục cha mẹ

os.path.abspath('..')
os.pardir

ví dụ 1

import os
print os.makedirs(os.path.join(os.path.dirname(__file__), os.pardir, 'new_dir'))

Ví dụ 2

import os
print os.makedirs(os.path.join(os.path.dirname(__file__), os.path.abspath('..'), 'new_dir'))


0
import os

def parent_filedir(n):
    return parent_filedir_iter(n, os.path.dirname(__file__))

def parent_filedir_iter(n, path):
    n = int(n)
    if n <= 1:
        return path
    return parent_filedir_iter(n - 1, os.path.dirname(path))

test_dir = os.path.abspath(parent_filedir(2))

0

Các câu trả lời được đưa ra ở trên hoàn toàn tốt cho việc tăng một hoặc hai cấp thư mục, nhưng chúng có thể hơi phức tạp nếu một người cần đi qua cây thư mục theo nhiều cấp độ (giả sử, 5 hoặc 10). Điều này có thể được thực hiện chính xác bằng cách tham gia một danh sách N os.pardirs os.path.join. Thí dụ:

import os
# Create list of ".." times 5
upup = [os.pardir]*5
# Extract list as arguments of join()
go_upup = os.path.join(*upup)
# Get abspath for current file
up_dir = os.path.abspath(os.path.join(__file__, go_upup))
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.