답변:
모든 현재 하위 디렉토리 의 전체 경로 를 반환하기 위해 다양한 기능에 대한 속도 테스트 를 수행 했습니다 .
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
is : 3 배 이상 walk
, 32 배 이상 listdir
(필터 포함), 35 배 이상 Pathlib
, 36 배 이상 listdir
, 37 배 (!) 빠릅니다 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 개의 하위 폴더가있는 폴더 os.path.join ()을 두 번 수행하지 않고 속도를 높일 수
있는지 궁금한 경우 listdir
에는 기본적으로 차이가 없습니다.
암호:
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
유닉스 스타일의 경로 이름 확장을 사용할 수 있으며, 둘 이상의 경로 이름을 찾는 데 필요한 거의 모든 기능을 수행합니다. 매우 쉽습니다.
from glob import glob
paths = glob('*/')
주 glob
(유닉스처럼) 최종 슬래시 디렉토리를 반환합니다 대부분의 동안path
기반 솔루션이 최종 슬래시를 생략합니다.
paths = [ p.replace('/', '') for p in glob('*/') ]
.
[p[:-1] for p in paths]
replace 메소드는 파일 이름에서 이스케이프 된 슬래시를 대체하기 때문에 마지막 문자를 간단히 잘라내는 것이 더 안전 할 수 있습니다 (공통이 아님).
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()
솔루션을 해야 뿌리깊은 빠른 쉘 스타일의 대체 (globbing)보다. 슬프게도, 나는 의지가 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(...) )
.
Twisted의 FilePath 모듈 사용 :
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의 장점을 설명하는 분기에 개선 된 설명서 가 있습니다 . 당신은 그것을 읽고 싶을 수도 있습니다.
이 예제에서보다 구체적으로 : 표준 라이브러리 버전과 달리이 함수는 imports없이 구현 될 수 있습니다 . "subdirs"함수는 인수에 대해서만 작동한다는 점에서 완전히 일반적입니다. 표준 라이브러리를 사용하여 파일을 복사하고 이동하려면 " open
"내장, " 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
현재 읽기 전용이지만 쓰기를 지원하도록 확장 될 수 있습니다.
테스트 목적으로 자신의 객체를 전달할 수도 있습니다. 여기에 제안 된 os.path 사용 API를 테스트하려면 가져온 이름과 암시 적 종속성을 원숭이로 사용하고 일반적으로 테스트를 수행하기 위해 흑 마법을 수행해야합니다. 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(...))
한 가지 방법이 있습니다.
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')
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 (directory, index = 1)-> 첫 번째 레벨의 폴더 목록을 제공합니다
get_folders_in_directories_recursively (directory)-> 모든 하위 폴더를 제공합니다
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']
pathlib를 사용하는 하나의 라이너 :
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]