Tüm alt dizinleri (birkaç istisna dışında) index.html için bir index.tpl kopyalayacak basit bir Python komut dosyası yazmaya çalışıyorum.
Altdizinlerin listesini almaya çalışıyorum.
Tüm alt dizinleri (birkaç istisna dışında) index.html için bir index.tpl kopyalayacak basit bir Python komut dosyası yazmaya çalışıyorum.
Altdizinlerin listesini almaya çalışıyorum.
Yanıtlar:
Geçerli tüm alt dizinlere tam yolu döndürmek için çeşitli işlevler üzerinde bazı hız testleri yaptım .
tl; dr:
Her zaman kullan scandir
:
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
Bonus: Bunun yerine scandir
sadece klasör adlarını kullanarak da alabilirsiniz .f.name
f.path
Bu (aşağıdaki diğer tüm işlevlerin yanı sıra) doğal sıralama kullanmaz . Bu, sonuçların şu şekilde sıralanacağı anlamına gelir: 1, 10, 2. Doğal sıralama (1, 2, 10) elde etmek için lütfen https://stackoverflow.com/a/48030307/2441026 adresine bakın.
Sonuçlar :
scandir
is: 3x daha hızlı walk
, 32x daha hızlı listdir
(filtre ile), 35x daha hızlı Pathlib
ve 36x daha hızlı listdir
ve 37x (!) Daha hızlı 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 ile test edilmiştir. 440 alt klasör içeren klasör. Os.path.join () öğesini iki kez yapmadan hızlanıp hızlanamayacağınızı
merak ediyorsanız listdir
, evet, ancak fark temelde mevcut değildir.
Kod:
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))]
Kimse neden bahsetmedi glob
? glob
Unix tarzı yol adı genişletmesini kullanmanızı sağlar ve birden fazla yol adı bulması gereken hemen hemen her şey için işlevime gider. Bunu çok kolaylaştırır:
from glob import glob
paths = glob('*/')
glob
Çoğu path
tabanlı çözüm son eğik çizgiyi atlarken dizinin son eğik çizgiyle (unix gibi) döneceğini unutmayın .
paths = [ p.replace('/', '') for p in glob('*/') ]
.
[p[:-1] for p in paths]
, çünkü replace yöntemi dosya adındaki kaçan ileri eğik çizgilerin de yerini alacaktır (bunlar yaygın değildir).
rstrip
yerine kullanmak istersiniz strip
, çünkü ikincisi tam nitelikli yolları göreli yollara dönüştürecektir.
strip('/')
hem başlangıç hem de sondaki '/' karakterini rstrip('/')
kaldıracak, sadece sondaki olanı kaldıracak
" Geçerli dizindeki tüm alt dizinlerin listesini alma " " seçeneğini işaretleyin.
İşte bir Python 3 sürümü:
import os
dir_list = next(os.walk('.'))[1]
print(dir_list)
(s.rstrip("/") for s in glob(parent_dir+"*/"))
daha fazla zaman verimli olup olmadığını merak ediyorum . Benim sezgisel şüphe, a- stat()
tabanlı bir os.walk()
çözümün kabuk tarzı globbingten çok daha hızlı olması gerektiğidir . Ne yazık ki, irade eksikliği timeit
ve aslında öğrenmek.
import os, os.path
Bir dizindeki (tam yol) hemen alt dizinleri almak için:
def SubDirPath (d):
return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])
En son (en yeni) alt dizini almak için:
def LatestDirectory (d):
return max(SubDirPath(d), key=os.path.getmtime)
list( filter(...) )
.
os.walk
bu durumda arkadaşın.
Doğrudan dokümantasyondan:
walk (), ağacı aşağıdan yukarıya veya aşağıya doğru yürüterek bir dizin ağacında dosya adları oluşturur. Ağaçtaki üst dizin (üstte dahil) kökündeki her dizin için 3-demet (dirpath, dirnames, dosya adları) verir.
Bu yöntem hepsini tek seferde güzel yapar.
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
Twisted'in FilePath modülünü kullanarak:
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
Bazı yorumcular bunun için Twisted kütüphanelerini kullanmanın avantajlarının ne olduğunu sorduğundan, buradaki orijinal sorunun biraz ötesine geçeceğim.
Orada bazı gelişmiş dokümantasyon FilePath avantajlarını açıklayan bir dalda; bunu okumak isteyebilirsiniz.
Daha spesifik olarak bu örnekte: standart kütüphane versiyonundan farklı olarak, bu fonksiyon içe aktarma olmadan uygulanabilir . "Altdiziler" işlevi tamamen geneldir, çünkü argümanından başka bir şey üzerinde çalışmaz. Standart kitaplığı kullanarak dosyaları kopyalamak ve taşımak için " open
" yerleşik listdir
",", belki " isdir
" veya " os.walk
" veya " shutil.copy
" öğelerine bağlı olmanız gerekir . Belki de " os.path.join
". Bir dizeye ihtiyacınız olduğu gerçeğinden bahsetmemek gerekirse, gerçek dosyayı tanımlamak için bir argüman geçti. Her dizinin "index.tpl" dosyasını "index.html" dosyasına kopyalayacak tam uygulamaya bakalım:
def copyTemplates(topdir):
for subdir in subdirs(topdir):
tpl = subdir.child("index.tpl")
if tpl.exists():
tpl.copyTo(subdir.child("index.html"))
Yukarıdaki "alt dizinler" işlevi herhangi bir benzer FilePath
nesne üzerinde çalışabilir . Bu, diğer şeylerin yanı sıra ZipPath
nesneler anlamına gelir . Ne yazık ki ZipPath
şu anda salt okunur, ancak yazmayı desteklemek için genişletilebilir.
Test amacıyla kendi nesnelerinizi de geçirebilirsiniz. Burada önerilen os.path kullanan API'leri test etmek için, içe aktarılan adlar ve örtülü bağımlılıklar ile maymunluk yapmalısınız ve testlerinizin çalışması için genellikle kara büyü yapmalısınız. FilePath ile böyle bir şey yaparsınız:
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(...))
Sadece vmware sanal makineleri taşımak için bazı kod yazdım ve kullanarak os.path
ve shutil
alt dizinler arasında dosya kopyalama gerçekleştirmek için sona erdi .
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))
Çok zarif değil, ama işe yarıyor.
İşte bir yol:
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'den bahsetmek zorundayımÇok sık kullandığım kütüphanesinden .
Hemen alt dizinleri getirmek şu kadar basit hale gelir:
my_dir.dirs()
Tam çalışma örneği:
from path import Path
my_directory = Path("path/to/my/directory")
subdirs = my_directory.dirs()
Not: Yolum dizenin bir alt sınıfı olduğundan, ancak yolları işlemek için bir dizi yararlı yöntem sağladığından, dizinim hala bir dize olarak işlenebilir
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
Aşağıdaki işlev şu şekilde çağrılabilir:
get_folders_in_directories_recursively (dizin, dizin = 1) -> birinci düzeydeki klasörlerin listesini verir
get_folders_in_directories_recursively (dizin) -> tüm alt klasörleri verir
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
İşlevi, bir yol bir dizin alır ve bir listesini döndürür hemen alt dizinleri içinde.
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 kullanan bir astar:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]