Рассмотрим еще один случай использования классов-обходчиков. Когда я впервые написал сценарий cpall.py ранее в этой главе, я не понимал, как можно применить иерархию классов-обходчиков, с которой мы встретились выше, для копирования деревьев каталогов. При копировании необходимо выполнять обход сразу двух деревьев каталогов (оригинального и его копии), а visitor выполняет обход только одного дерева, используя функцию os.walk. Тогда мне казалось, что будет совсем не просто следить за тем, где находится сценарий в копии дерева.
Хитрость, до которой я в конце концов додумался, заключается в том, что следить за позицией в дереве копии вообще не требуется. Вместо этого сценарий в примере 6.22 просто замещает строку пути к каталогу «из» строкой пути к каталогу «в» и добавляет ее перед всеми именами каталогов, полученными от функции os.walk. В результате такой подмены получаются пути, куда должны быть скопированы оригинальные файлы и каталоги.
Пример 6.22. PP4E\Tools\visitor_cpall.py
Использование: “python …\Tools\visitor_cpall.py fromDir toDir trace?” Действует подобно сценарию System\Filetools\cpall.py, но использует классы- обходчики и функцию os.walk; заменяет строку fromDir на toDir перед всеми именами, возвращаемыми обходчиком; предполагается, что изначально каталог toDir не существует;
import os
from visitor import FileVisitor # обходчик в каталоге
from PP4E.System.Filetools.cpall import copyfile # PP4E — в пути поиска
class CpallVisitor(FileVisitor):
def __init__(self, fromDir, toDir, trace=True):
self.fromDirLen = len(fromDir) + 1
self.toDir = toDir
FileVisitor.__init__(self, trace=trace)
toPath = os.path.join(self.toDir, dirpath[self.fromDirLen:]) if self.trace: print(‘d’, dirpath, ‘=>’, toPath) os.mkdir(toPath) self.dcount += 1
def visitfile(self, filepath):
toPath = os.path.join(self.toDir, filepath[self.fromDirLen:]) if self.trace: print(‘f’, filepath, ‘=>’, toPath) copyfile(filepath, toPath) self.fcount += 1
if __name__ == ‘__main__’: import sys, time fromDir, toDir = sys.argv[1:3] trace = len(sys.argv) > 3 print(‘Copying…’) start = time.clock() walker = CpallVisitor(fromDir, toDir, trace) walker.run(startDir=fromDir) print(‘Copied’, walker.fcount, ‘files,’, walker.dcount, ‘directories’, end=’ ‘)
print(‘in’, time.clock() — start, ‘seconds’)
Эта версия достигает почти той же цели, что и оригинал, но в ней сделано несколько допущений, чтобы упростить реализацию. Предполагается, что каталог «в» изначально не существует, а исключения, возникающие в процессе копирования, не игнорируются. Следующий пример демонстрирует копирование дерева примеров для предыдущего издания книги в Windows:
C:\…\PP4E\Tools> set PYTHONPATH
PYTHONPATH=C:\Users\Mark\Stuff\Books\4E\PP4E\dev\Examples
C:\…\PP4E\Tools> rmdir /S copytemp copytemp, Are you sure (Y/N)? y
C:\…\PP4E\Tools> visitor_cpall.py C:\temp\PP3E\Examples copytemp Copying…
Copied 1429 files, 186 directories in 11.1722033777 seconds
C:\…\PP4E\Tools> fc /B copytemp\PP3E\Launcher.py
C:\temp\PP3E\Examples\PP3E\Launcher.py
Comparing files COPYTEMP\PP3E\Launcher.py and C:\TEMP\PP3E\EXAMPLES\PP3E\ LAUNCHER.PY FC: no differences encountered
Несмотря на то, что в этой версии выполняется дополнительная операция извлечения среза из строки, она действует так же быстро, как и оригинал (фактическое отличие можно списать на разницу в нагрузке на систему). Кроме того, эта версия позволяет проследить за всеми путями «из» и «в» в процессе обхода, если передать ей третий аргумент командной строки:
C:\…\PP4E\Tools> rmdir /S copytemp copytemp, Are you sure (Y/N)? y
C:\…\PP4E\Tools> visitor_cpall.py C:\temp\PP3E\Examples copytemp 1
Copying…
d C:\temp\PP3E\Examples => copytemp\
f C:\temp\PP3E\Examples\README-root.txt => copytemp\README-root.txt
d C:\temp\PP3E\Examples\PP3E => copytemp\PP3E
…множество строк опущено: запустите этот сценарий у себя, чтобы увидеть вывод целиком…
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011