Không bao gồm các thư mục trong os.walk


147

Tôi đang viết một tập lệnh đi xuống cây thư mục (sử dụng os.walk ()) và sau đó truy cập từng tệp khớp với một phần mở rộng tệp nhất định. Tuy nhiên, vì một số các cây thư mục đó công cụ của tôi sẽ được sử dụng trên cũng chứa thư mục con mà lần lượt chứa một LOT của vô dụng (đối với mục đích của kịch bản này) công cụ, tôi figured tôi muốn thêm một lựa chọn cho người sử dụng để xác định một danh sách các thư mục để loại trừ khỏi giao dịch.

Điều này là đủ dễ dàng với os.walk (). Rốt cuộc, tùy thuộc vào tôi để quyết định xem tôi có thực sự muốn truy cập các tệp / thư mục tương ứng do os.walk () mang lại hay chỉ cần bỏ qua chúng. Vấn đề là nếu tôi có, ví dụ, một cây thư mục như thế này:

root--
     |
     --- dirA
     |
     --- dirB
     |
     --- uselessStuff --
                       |
                       --- moreJunk
                       |
                       --- yetMoreJunk

và tôi muốn loại trừ uslessStuff và tất cả các con của nó, os.walk () vẫn sẽ truy cập vào tất cả các thư mục con (có khả năng hàng ngàn) của uslessStuff , không cần phải nói, làm chậm mọi thứ. Trong một thế giới lý tưởng, tôi có thể nói với os.walk () thậm chí không bận tâm đến việc sinh thêm bất kỳ đứa trẻ nào vô dụng , nhưng theo hiểu biết của tôi thì không có cách nào để làm điều đó (có phải không?).

Có ai có ý tưởng gì không? Có lẽ có một thư viện bên thứ ba cung cấp một cái gì đó như thế?

Câu trả lời:


242

Sửa đổi dirs tại chỗ sẽ cắt tỉa các tệp và thư mục (tiếp theo) được truy cập bởi os.walk:

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    dirs[:] = [d for d in dirs if d not in exclude]

Từ trợ giúp (os.walk):

Khi topdown là true, người gọi có thể sửa đổi danh sách dirnames tại chỗ (ví dụ: thông qua phân bổ del hoặc lát) và walk sẽ chỉ lặp lại vào các thư mục con có tên vẫn còn trong dirnames; điều này có thể được sử dụng để cắt tỉa tìm kiếm ...


31
Tại sao dirs[:] =?
ben

56
@ben: dirs[:] = valuesửa đổi dirs tại chỗ . Nó thay đổi nội dung của danh sách dirsmà không thay đổi container. Như đã help(os.walk)đề cập, điều này là cần thiết nếu bạn muốn ảnh hưởng đến cách os.walkđi qua các thư mục con. ( dirs = valuechỉ đơn giản là gán lại (hoặc "liên kết") biến dirscho một danh sách mới, mà không sửa đổi bản gốc dirs.)
unutbu

6
Bạn cũng có thể sử dụng filter():dirs[:] = list(filter(lambda x: not x in exclude, dirs))
NuclePeon

2
@ p014k: Bạn có thể viết hàm tạo của riêng mình để gọi os.walkvà sinh lợi root, dirs, filessau khi loại trừ .git(hoặc bất cứ điều gì khác bạn muốn) từ dirs.
unutbu

3
@unutbu Chỉ cần cho bạn biết rằng trong một trường hợp, việc tối ưu hóa này đã giảm thời gian truyền tải từ hơn 100 giây xuống còn khoảng 2 giây. Đó là những gì tôi gọi là tối ưu hóa đáng giá. : D
antred

7

... một dạng khác của câu trả lời xuất sắc của @ unutbu đọc trực tiếp hơn một chút, với mục đích là loại trừ các thư mục, với chi phí là thời gian O (n ** 2) so với O (n).

(Tạo một bản sao của danh sách dirs list(dirs)là bắt buộc để thực hiện chính xác)

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    [dirs.remove(d) for d in list(dirs) if d in exclude]

5
Nếu bạn muốn trực tiếp hơn với chi phí của một số bộ nhớ, bạn nên viết dirs[:] = set(dirs) - exclude. Ít nhất thì nó vẫn còn \ $ O (n) \ $ và bạn không xây dựng sự hiểu biết chỉ cho các tác dụng phụ của nó ...
301_Mond_P vĩnh viễn

3
Điều này không thực sự xấu nhưng cũng không phải là thành ngữ Python.
Torsten Bronger

for d in list(dirs)là một chút kỳ lạ. dirsđã là một danh sách. Và những gì bạn có không thực sự là một sự hiểu biết danh sách. dirs.remove(d)không trả lại bất cứ điều gì, vì vậy bạn kết thúc với một danh sách đầy đủ None. Tôi đồng ý với @Torsten.
seanahern
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.