Tôi đang cố gắng viết một tập lệnh Python đơn giản sẽ sao chép index.tpl sang index.html trong tất cả các thư mục con (với một vài ngoại lệ).
Tôi đang bị sa lầy bằng cách cố gắng để có được danh sách các thư mục con.
Tôi đang cố gắng viết một tập lệnh Python đơn giản sẽ sao chép index.tpl sang index.html trong tất cả các thư mục con (với một vài ngoại lệ).
Tôi đang bị sa lầy bằng cách cố gắng để có được danh sách các thư mục con.
Câu trả lời:
Tôi đã thực hiện một số thử nghiệm tốc độ trên các chức năng khác nhau để trả lại đường dẫn đầy đủ cho tất cả các thư mục con hiện tại.
tl; dr:
Luôn sử dụng scandir
:
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
Phần thưởng: Với scandir
bạn cũng có thể chỉ đơn giản là nhận tên thư mục bằng cách sử dụng f.name
thay vì f.path
.
Điều này (cũng như tất cả các chức năng khác bên dưới) sẽ không sử dụng phân loại tự nhiên . Điều này có nghĩa là kết quả sẽ được sắp xếp như sau: 1, 10, 2. Để có được sự sắp xếp tự nhiên (1, 2, 10), vui lòng xem tại https://stackoverflow.com/a/48030307/2441026
Kết quả :
scandir
là: nhanh hơn 3 lần so với walk
, nhanh hơn 32 lần so với listdir
(với bộ lọc), Pathlib
nhanh hơn listdir
35 lần so với và nhanh hơn 36 lần so với và nhanh hơn 37 lần (!) glob
.
Scandir: 0.977
Walk: 3.011
Listdir (filter): 31.288
Pathlib: 34.075
Listdir: 35.501
Glob: 36.277
Đã thử nghiệm với W7x64, Python 3.8.1. Thư mục với 440 thư mục con.
Trong trường hợp bạn tự hỏi liệu listdir
có thể tăng tốc bằng cách không thực hiện os.path.join () hai lần, có, nhưng về cơ bản sự khác biệt là không có.
Mã số:
import os
import pathlib
import timeit
import glob
path = r"<example_path>"
def a():
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
# print(len(list_subfolders_with_paths))
def b():
list_subfolders_with_paths = [os.path.join(path, f) for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
# print(len(list_subfolders_with_paths))
def c():
list_subfolders_with_paths = []
for root, dirs, files in os.walk(path):
for dir in dirs:
list_subfolders_with_paths.append( os.path.join(root, dir) )
break
# print(len(list_subfolders_with_paths))
def d():
list_subfolders_with_paths = glob.glob(path + '/*/')
# print(len(list_subfolders_with_paths))
def e():
list_subfolders_with_paths = list(filter(os.path.isdir, [os.path.join(path, f) for f in os.listdir(path)]))
# print(len(list(list_subfolders_with_paths)))
def f():
p = pathlib.Path(path)
list_subfolders_with_paths = [x for x in p.iterdir() if x.is_dir()]
# print(len(list_subfolders_with_paths))
print(f"Scandir: {timeit.timeit(a, number=1000):.3f}")
print(f"Listdir: {timeit.timeit(b, number=1000):.3f}")
print(f"Walk: {timeit.timeit(c, number=1000):.3f}")
print(f"Glob: {timeit.timeit(d, number=1000):.3f}")
print(f"Listdir (filter): {timeit.timeit(e, number=1000):.3f}")
print(f"Pathlib: {timeit.timeit(f, number=1000):.3f}")
import os
def get_immediate_subdirectories(a_dir):
return [name for name in os.listdir(a_dir)
if os.path.isdir(os.path.join(a_dir, name))]
Tại sao không có ai nhắc đến glob
? glob
cho phép bạn sử dụng mở rộng tên đường dẫn kiểu Unix và là chức năng của tôi cho hầu hết mọi thứ cần tìm nhiều hơn một tên đường dẫn. Nó làm cho nó rất dễ dàng:
from glob import glob
paths = glob('*/')
Lưu ý rằng glob
sẽ trả về thư mục với dấu gạch chéo cuối cùng (như unix sẽ) trong khi hầu hết các path
giải pháp dựa trên sẽ bỏ qua dấu gạch chéo cuối cùng.
paths = [ p.replace('/', '') for p in glob('*/') ]
.
[p[:-1] for p in paths]
, vì phương thức thay thế đó cũng sẽ thay thế bất kỳ dấu gạch chéo nào được thoát trong tên tệp (không phải là ký tự phổ biến).
rstrip
thay vì vì strip
sau này sẽ biến bất kỳ đường dẫn đủ điều kiện nào thành đường dẫn tương đối.
strip('/')
sẽ xóa cả bắt đầu và theo dõi '/', rstrip('/')
sẽ chỉ xóa một dấu vết
Kiểm tra " Lấy danh sách tất cả các thư mục con trong thư mục hiện tại ".
Đây là phiên bản Python 3:
import os
dir_list = next(os.walk('.'))[1]
print(dir_list)
(s.rstrip("/") for s in glob(parent_dir+"*/"))
này có hiệu quả hơn về thời gian hay không. Sự nghi ngờ trực giác của tôi là một giải pháp stat()
dựa trên cơ sở nên nhanh hơn rất nhiều so với phương pháp tạo hình vỏ sò. Đáng buồn thay, tôi thiếu ý chí và thực sự tìm ra. os.walk()
timeit
import os, os.path
Để có được (toàn đường dẫn) thư mục con ngay lập tức trong một thư mục:
def SubDirPath (d):
return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])
Để có được thư mục con mới nhất (mới nhất):
def LatestDirectory (d):
return max(SubDirPath(d), key=os.path.getmtime)
list( filter(...) )
.
os.walk
là bạn của bạn trong tình huống này.
Trực tiếp từ tài liệu:
walk () tạo tên tệp trong cây thư mục, bằng cách đi cây từ trên xuống hoặc từ dưới lên. Đối với mỗi thư mục trong cây gốc ở thư mục trên cùng (bao gồm cả đỉnh), nó mang lại một bộ 3 (dirpath, dirnames, tên tệp).
Phương pháp này độc đáo làm tất cả trong một lần.
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
Sử dụng mô-đun FilePath của Twisted:
from twisted.python.filepath import FilePath
def subdirs(pathObj):
for subpath in pathObj.walk():
if subpath.isdir():
yield subpath
if __name__ == '__main__':
for subdir in subdirs(FilePath(".")):
print "Subdirectory:", subdir
Vì một số người bình luận đã hỏi những lợi thế của việc sử dụng các thư viện của Twisted cho việc này là gì, tôi sẽ vượt xa câu hỏi ban đầu ở đây.
Có một số tài liệu được cải tiến trong một nhánh giải thích các ưu điểm của FilePath; bạn có thể muốn đọc nó
Cụ thể hơn trong ví dụ này: không giống như phiên bản thư viện tiêu chuẩn, chức năng này có thể được thực hiện mà không cần nhập . Hàm "subirs" hoàn toàn chung chung, ở chỗ nó hoạt động không có gì ngoài đối số của nó. Để sao chép và di chuyển các tệp bằng thư viện chuẩn, bạn cần phụ thuộc vào " open
" dựng sẵn listdir
"," có lẽ " isdir
" hoặc " os.walk
" hoặc " shutil.copy
". Có lẽ " os.path.join
" cũng vậy. Không đề cập đến thực tế là bạn cần một chuỗi thông qua một đối số để xác định tệp thực tế. Chúng ta hãy xem việc thực hiện đầy đủ sẽ sao chép "index.tpl" của mỗi thư mục thành "index.html":
def copyTemplates(topdir):
for subdir in subdirs(topdir):
tpl = subdir.child("index.tpl")
if tpl.exists():
tpl.copyTo(subdir.child("index.html"))
Hàm "subirs" ở trên có thể hoạt động trên mọi FilePath
đối tượng giống như. Có nghĩa là, trong số những thứ khác, ZipPath
các đối tượng. Thật không may ZipPath
là chỉ đọc ngay bây giờ, nhưng nó có thể được mở rộng để hỗ trợ viết.
Bạn cũng có thể vượt qua các đối tượng của riêng bạn cho mục đích thử nghiệm. Để kiểm tra các API sử dụng os.path được đề xuất ở đây, bạn phải sử dụng tên đã nhập và phụ thuộc ngầm và thường thực hiện phép thuật đen để thử nghiệm của bạn hoạt động. Với FilePath, bạn làm một cái gì đó như thế này:
class MyFakePath:
def child(self, name):
"Return an appropriate child object"
def walk(self):
"Return an iterable of MyFakePath objects"
def exists(self):
"Return true or false, as appropriate to the test"
def isdir(self):
"Return true or false, as appropriate to the test"
...
subdirs(MyFakePath(...))
Tôi chỉ viết một số mã để di chuyển các máy ảo vmware xung quanh, và cuối cùng đã sử dụng os.path
và shutil
để thực hiện sao chép tệp giữa các thư mục con.
def copy_client_files (file_src, file_dst):
for file in os.listdir(file_src):
print "Copying file: %s" % file
shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))
Nó không thanh lịch khủng khiếp, nhưng nó hoạt động.
Đây là một cách:
import os
import shutil
def copy_over(path, from_name, to_name):
for path, dirname, fnames in os.walk(path):
for fname in fnames:
if fname == from_name:
shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))
copy_over('.', 'index.tpl', 'index.html')
Tôi phải đề cập đến thư viện path.py mà tôi sử dụng rất thường xuyên.
Tìm nạp các thư mục con ngay lập tức trở nên đơn giản như sau:
my_dir.dirs()
Ví dụ làm việc đầy đủ là:
from path import Path
my_directory = Path("path/to/my/directory")
subdirs = my_directory.dirs()
NB: my_directory vẫn có thể được thao tác dưới dạng một chuỗi, vì Đường dẫn là một lớp con của chuỗi, nhưng cung cấp một loạt các phương thức hữu ích để thao tác các đường dẫn
def get_folders_in_directories_recursively(directory, index=0):
folder_list = list()
parent_directory = directory
for path, subdirs, _ in os.walk(directory):
if not index:
for sdirs in subdirs:
folder_path = "{}/{}".format(path, sdirs)
folder_list.append(folder_path)
elif path[len(parent_directory):].count('/') + 1 == index:
for sdirs in subdirs:
folder_path = "{}/{}".format(path, sdirs)
folder_list.append(folder_path)
return folder_list
Hàm sau có thể được gọi là:
get_ Foldftimein_directories_recursively (thư mục, index = 1) -> đưa ra danh sách các thư mục ở cấp độ đầu tiên
get_ Foldftimein_directories_recursively (thư mục) -> cung cấp cho tất cả các thư mục con
import glob
import os
def child_dirs(path):
cd = os.getcwd() # save the current working directory
os.chdir(path) # change directory
dirs = glob.glob("*/") # get all the subdirectories
os.chdir(cd) # change directory to the script original location
return dirs
Các child_dirs
chức năng có một con đường một thư mục và trả về một danh sách các thư mục con ngay trong đó.
dir
|
-- dir_1
-- dir_2
child_dirs('dir') -> ['dir_1', 'dir_2']
import pathlib
def list_dir(dir):
path = pathlib.Path(dir)
dir = []
try:
for item in path.iterdir():
if item.is_dir():
dir.append(item)
return dir
except FileNotFoundError:
print('Invalid directory')
Một lớp lót sử dụng pathlib:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]