すべてのサブディレクトリ(いくつかの例外を除く)でindex.tplをindex.htmlにコピーする簡単なPythonスクリプトを記述しようとしています。
サブディレクトリのリストを取得しようとすると、行き詰まってしまいます。
すべてのサブディレクトリ(いくつかの例外を除く)でindex.tplをindex.htmlにコピーする簡単なPythonスクリプトを記述しようとしています。
サブディレクトリのリストを取得しようとすると、行き詰まってしまいます。
回答:
私はいくつかやったテストのスピード返すために、さまざまな機能に完全なパスを現在のすべてのサブディレクトリにします。
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
次のとおりです。速くより3倍walk
速くよりも、32X listdir
(フィルター付き)、35倍よりも速く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個あるフォルダ。os.path.join()を2回実行しないことでスピードアップできる
かどうか疑問に思っている場合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}")
なぜ誰も言及しなかったのglob
ですか?glob
Unixスタイルのパス名展開を使用できるようにします。これは、複数のパス名を見つける必要があるほとんどすべての機能に使用できます。それはそれを非常に簡単にします:
from glob import glob
paths = glob('*/')
glob
ほとんどのpath
ベースのソリューションは最後のスラッシュを省略しますが、UNIXがそうであるように、最後のスラッシュのあるディレクトリを返すことに注意してください。
paths = [ p.replace('/', '') for p in glob('*/') ]
。
[p[:-1] for p in paths]
replaceメソッドはファイル名のエスケープされたフォワードスラッシュも置換するため、単に最後の文字をでカットした方が安全な場合があります(これらは一般的ではありません)。
rstrip
代わりに使用することをお勧めしstrip
ます。
strip('/')
開始と末尾の両方の「/」rstrip('/')
を削除し、末尾の1つだけを削除します
「現在のディレクトリにあるすべてのサブディレクトリのリストを取得する」にチェックを入れます。
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()は、ツリーをトップダウンまたはボトムアップでウォークすることにより、ディレクトリツリー内にファイル名を生成します。ディレクトリtop(ルート自体を含む)をルートとするツリー内の各ディレクトリについて、3つのタプル(dirpath、dirnames、filenames)が生成されます。
このメソッドは、すべてを一度にうまく実行します。
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
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の利点を説明するいくつかの改善されたドキュメントがあります。あなたはそれを読みたいかもしれません。
より具体的には、この例では、標準ライブラリバージョンとは異なり、この関数はインポートなしで実装できます。「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-using 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(...))
私は、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))
それはひどくエレガントではありませんが、動作します。
これが1つの方法です。
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)->第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を使用する1つのライナー:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]