ฉันกำลังพยายามเขียนสคริปต์ Python แบบง่ายที่จะคัดลอก index.tpl ไปยัง index.html ในไดเรกทอรีย่อยทั้งหมด (มีข้อยกเว้นเล็กน้อย)
ฉันจมอยู่กับการพยายามหารายชื่อไดเรกทอรีย่อย
ฉันกำลังพยายามเขียนสคริปต์ Python แบบง่ายที่จะคัดลอก index.tpl ไปยัง index.html ในไดเรกทอรีย่อยทั้งหมด (มีข้อยกเว้นเล็กน้อย)
ฉันจมอยู่กับการพยายามหารายชื่อไดเรกทอรีย่อย
คำตอบ:
ฉันทำการทดสอบความเร็วบางฟังก์ชั่นเพื่อคืนค่าพา ธ เต็มไปยังไดเรกทอรีย่อยปัจจุบันทั้งหมด
tl; dr:
ใช้เสมอscandir
:
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
โบนัส: ด้วยscandir
คุณยังสามารถเพียงแค่เพียง แต่ได้รับชื่อโฟลเดอร์โดยใช้แทนf.name
f.path
นี้ (เช่นเดียวกับฟังก์ชั่นอื่น ๆ ทั้งหมดด้านล่าง) จะไม่ใช้การเรียงลำดับตามธรรมชาติ ซึ่งหมายความว่าผลลัพธ์จะถูกจัดเรียงเช่นนี้: 1, 10, 2 หากต้องการรับการจัดเรียงแบบธรรมชาติ (1, 2, 10) โปรดดูที่https://stackoverflow.com/a/48030307/2441026
ผลการค้นหา :
scandir
คือ 3x เร็วกว่าwalk
, เร็วกว่า 32x listdir
(กรอง) 35x เร็วกว่าPathlib
และเร็วกว่า 36x listdir
และ 37x glob
เร็วกว่า
Scandir: 0.977
Walk: 3.011
Listdir (filter): 31.288
Pathlib: 34.075
Listdir: 35.501
Glob: 36.277
ทดสอบกับ W7x64, Python 3.8.1 โฟลเดอร์ที่มีโฟลเดอร์ย่อย 440 โฟลเดอร์
ในกรณีที่คุณสงสัยว่าlistdir
สามารถเร่งความเร็วได้หรือไม่โดยไม่ทำ os.path.join () สองครั้งใช่ แต่ความแตกต่างนั้นไม่มีอยู่จริง
รหัส:
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))]
ทำไมไม่มีใครพูดถึงglob
? glob
ให้คุณใช้การขยายชื่อพา ธ แบบ Unix และเป็นหน้าที่ของฉันในการทำงานเกือบทุกอย่างที่ต้องการค้นหาชื่อพา ธ มากกว่าหนึ่งชื่อ มันทำให้ง่ายมาก:
from glob import glob
paths = glob('*/')
โปรดทราบว่าglob
จะส่งคืนไดเร็กทอรีด้วยเครื่องหมายสแลชสุดท้าย (เหมือนยูนิกซ์) ในขณะที่path
โซลูชันพื้นฐานส่วนใหญ่จะละเว้นเครื่องหมายสแลชสุดท้าย
paths = [ p.replace('/', '') for p in glob('*/') ]
สำหรับผู้ที่ไม่ต้องการที่เฉือนสุดท้ายเขาสามารถใช้นี้
[p[:-1] for p in paths]
เนื่องจากวิธีการแทนที่นั้นจะแทนที่เครื่องหมายสแลชทางลัดใด ๆ ที่หลบหนีในชื่อไฟล์
rstrip
แทนแน่นอนstrip
เนื่องจากหลังจะเปลี่ยนเส้นทางที่ผ่านการรับรองเป็นเส้นทางสัมพัทธ์
strip('/')
จะลบทั้งการเริ่มต้นและการต่อท้าย '/' rstrip('/')
จะลบเฉพาะการติดตามหนึ่งครั้งเท่านั้น
ทำเครื่องหมายที่ " รับรายการไดเรกทอรีย่อยทั้งหมดในไดเรกทอรีปัจจุบัน "
นี่เป็นเวอร์ชั่น Python 3:
import os
dir_list = next(os.walk('.'))[1]
print(dir_list)
(s.rstrip("/") for s in glob(parent_dir+"*/"))
เวลามากขึ้นหรือไม่ ความสงสัยที่ใช้งานง่ายของฉันคือโซลูชันที่stat()
ใช้งานควรเร็วกว่าการโค้งแบบเชลล์ น่าเศร้าที่ฉันขาดความตั้งใจและค้นพบจริง os.walk()
timeit
import os, os.path
ในการรับ (เต็มเส้นทาง) ไดเรกทอรีย่อยทันทีในไดเรกทอรี:
def SubDirPath (d):
return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])
วิธีรับไดเรกทอรีย่อย (ใหม่ที่สุด) ล่าสุด:
def LatestDirectory (d):
return max(SubDirPath(d), key=os.path.getmtime)
list( filter(...) )
เพียงแค่เพิ่ม
os.walk
เป็นเพื่อนของคุณในสถานการณ์นี้
ส่งตรงจากเอกสาร:
walk () สร้างชื่อไฟล์ในแผนผังไดเร็กทอรีโดยการเดินแผนผังทั้งจากบนลงล่างหรือล่างขึ้นบน สำหรับแต่ละไดเร็กทอรีในทรีที่รูทที่ไดเร็กทอรีบนสุด (รวมถึงตัวท็อปเอง) จะให้ผลลัพธ์ 3-tuple (dirpath, dirnames, filenames)
วิธีนี้ไม่ได้ทั้งหมดในครั้งเดียว
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
ใช้โมดูล FilePath ของ 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
เนื่องจากผู้วิจารณ์บางคนถามว่าข้อดีของการใช้ห้องสมุดของ Twisted สำหรับเรื่องนี้คืออะไรฉันจะไปไกลกว่าคำถามเดิมที่นี่
มีเอกสารที่ได้รับการปรับปรุงในสาขาที่อธิบายข้อดีของ FilePath; คุณอาจต้องการอ่านสิ่งนั้น
โดยเฉพาะอย่างยิ่งในตัวอย่างนี้แตกต่างจากรุ่นมาตรฐานห้องสมุด, ฟังก์ชั่นนี้สามารถนำมาใช้กับการนำเข้าไม่มี ฟังก์ชั่น "subdirs" นั้นเป็นเรื่องทั่วไปโดยสิ้นเชิง ในการคัดลอกและย้ายไฟล์โดยใช้ไลบรารีมาตรฐานคุณต้องพึ่งพา " open
" builtin, " listdir
", บางที " isdir
" หรือ " os.walk
" หรือ " shutil.copy
" อาจจะ " os.path.join
" เช่นกัน ไม่ต้องพูดถึงความจริงที่ว่าคุณต้องการสตริงที่ผ่านการโต้แย้งเพื่อระบุไฟล์จริง มาดูการใช้งานแบบเต็มซึ่งจะคัดลอก "index.tpl" ของแต่ละไดเรกทอรีไปที่ "index.html":
def copyTemplates(topdir):
for subdir in subdirs(topdir):
tpl = subdir.child("index.tpl")
if tpl.exists():
tpl.copyTo(subdir.child("index.html"))
ฟังก์ชัน "subdirs" ด้านบนสามารถทำงานกับFilePath
วัตถุที่เหมือนกันได้ ซึ่งหมายความว่าเหนือสิ่งอื่นใดZipPath
วัตถุ น่าเสียดายที่ZipPath
เป็นแบบอ่านอย่างเดียวในขณะนี้ แต่อาจขยายเพื่อรองรับการเขียน
คุณสามารถส่งวัตถุของคุณเองเพื่อการทดสอบ เพื่อที่จะทดสอบ API ที่ใช้ os.path ที่แนะนำที่นี่คุณจะต้องมีชื่อที่นำเข้าและการพึ่งพาโดยนัยและมักจะใช้เวทย์มนตร์ดำเพื่อให้การทดสอบของคุณทำงาน ด้วย FilePath คุณทำสิ่งนี้:
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(...))
ฉันเพิ่งเขียนโค้ดบางอย่างเพื่อย้ายเครื่องเสมือน vmware ไปรอบ ๆ และจบลงด้วยการใช้os.path
และshutil
เพื่อให้การคัดลอกไฟล์ระหว่างไดเรกทอรีย่อยสำเร็จ
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))
มันไม่ได้สง่างามมาก แต่มันใช้งานได้
นี่คือวิธีหนึ่ง:
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')
ฉันต้องพูดถึงไลบรารีpath.pyซึ่งฉันใช้บ่อยมาก
การเรียกไดเรกทอรีย่อยทันทีกลายเป็นเรื่องง่ายเหมือน:
my_dir.dirs()
ตัวอย่างการทำงานเต็มรูปแบบคือ:
from path import Path
my_directory = Path("path/to/my/directory")
subdirs = my_directory.dirs()
หมายเหตุ: my_directory ยังสามารถจัดการเป็นสตริงได้เนื่องจาก Path เป็นคลาสย่อยของสตริง แต่มีวิธีการที่เป็นประโยชน์มากมายสำหรับจัดการเส้นทาง
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
ฟังก์ชั่นต่อไปนี้สามารถเรียกได้ว่า:
get_folders_in_directories_recursively (ไดเรกทอรี, index = 1) -> ให้รายชื่อโฟลเดอร์ในระดับแรก
get_folders_in_directories_recursively (ไดเรกทอรี) -> ให้โฟลเดอร์ย่อยทั้งหมด
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
child_dirs
ฟังก์ชั่นใช้เวลาเส้นทางไดเรกทอรีและกลับรายการของที่ไดเรกทอรีย่อยทันทีในนั้น
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')
สายการบินหนึ่งที่ใช้ pathlib:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]