5 эффективных методов копирования файлов в Python: полное руководство
Для кого эта статья:
- Python-разработчики, желающие улучшить свои навыки работы с файлами
- Опытные программисты, ищущие эффективные методы копирования файлов
Студенты и начинающие специалисты, интересующиеся автоматизацией процессов в Python
Копирование файлов — одна из базовых операций в автоматизации и обработке данных, с которой рано или поздно сталкивается каждый Python-разработчик. Удивительно, но даже опытные программисты иногда упускают из виду тонкости этого процесса, выбирая не самые эффективные методы. Правильно подобранная техника копирования может кардинально повысить производительность ваших скриптов и уберечь от потенциальных ошибок. Давайте разберем 5 проверенных методов копирования файлов, которые должен знать каждый Python-разработчик. 📂✨
Хотите углубить свои знания работы с файлами в Python и стать востребованным разработчиком? Обучение Python-разработке от Skypro предлагает полный курс с практическими заданиями по файловым операциям и автоматизации. Вы не просто изучите теорию, но и создадите реальные проекты под руководством опытных менторов, которые работают с Python ежедневно. Инвестируйте в свои навыки сейчас и забудьте о рутинных задачах навсегда!
Обзор методов копирования файлов в Python
Копирование файлов в Python можно реализовать несколькими способами, каждый из которых имеет свои преимущества и особенности применения. Выбор конкретного метода зависит от задачи: нужно ли вам просто скопировать содержимое файла, сохранить метаданные или обработать целые директории.
Алексей Воронцов, руководитель команды разработки автоматизации:
Однажды нашей команде поручили разработать систему резервного копирования конфигурационных файлов для критически важной инфраструктуры. Первый вариант системы использовал базовые функции открытия и чтения файлов, что приводило к потере метаданных и разрешений. Это стало очевидно, когда при восстановлении из резервной копии приложения отказались запускаться из-за неправильных прав доступа к файлам.
Переход на
shutil.copy2()решил проблему, сохраняя все необходимые атрибуты. Но настоящий прорыв произошел, когда мы внедрилиshutil.copytree()с кастомными обработчиками ошибок — это позволило создать надежную систему, которая элегантно обрабатывала исключения и поддерживала целостность копий даже при сбоях. Правильный выбор метода копирования сэкономил нам недели отладки.
В Python существует несколько встроенных модулей для работы с файловой системой. Основные из них, которые обеспечивают функциональность копирования:
- shutil — высокоуровневый модуль с простыми и мощными функциями для операций с файлами
- os — низкоуровневый доступ к файловой системе с функциями, близкими к системным вызовам
- pathlib — объектно-ориентированный подход к работе с путями файловой системы (Python 3.4+)
- Ручное копирование — чтение и запись содержимого файлов с помощью файловых объектов
Давайте сравним основные методы копирования по ключевым параметрам:
| Метод | Сохраняет метаданные | Перезаписывает существующий | Копирует директории | Сложность использования |
|---|---|---|---|---|
| shutil.copyfile() | Нет | Да | Нет | Низкая |
| shutil.copy() | Частично | Да | Нет | Низкая |
| shutil.copy2() | Да | Да | Нет | Низкая |
| shutil.copytree() | Да | Нет (по умолчанию) | Да | Средняя |
| Ручное копирование | Нет | Контролируемо | Нет | Высокая |
В большинстве случаев модуль shutil предоставляет оптимальный баланс между простотой использования и функциональностью. Однако для специфических задач может потребоваться комбинирование методов или создание собственных функций копирования.

Базовое копирование с модулем shutil
Модуль shutil — это стандартная библиотека Python для высокоуровневых операций с файлами. Он предлагает несколько функций для копирования файлов, различающихся по возможностям и поведению.
Начнем с самой базовой функции — shutil.copyfile():
import shutil
# Простое копирование содержимого файла
shutil.copyfile('source.txt', 'destination.txt')
Эта функция копирует содержимое исходного файла в целевой. Если целевой файл уже существует, он будет перезаписан. Важно отметить, что copyfile() копирует только содержимое файла, но не метаданные вроде прав доступа или времени изменения.
Для более полного копирования файла можно использовать shutil.copy():
# Копирование файла с сохранением режима доступа
shutil.copy('source.txt', 'destination.txt')
# Копирование файла в директорию, сохраняя исходное имя
shutil.copy('source.txt', '/path/to/directory/')
Функция copy() отличается от copyfile() тем, что она сохраняет режим доступа к файлу и позволяет указать директорию в качестве места назначения. В этом случае исходное имя файла будет сохранено.
Для обработки возможных ошибок при копировании рекомендуется использовать блок try-except:
try:
shutil.copy('source.txt', 'destination.txt')
except FileNotFoundError:
print("Исходный файл не найден")
except PermissionError:
print("Недостаточно прав для копирования")
except shutil.SameFileError:
print("Исходный и целевой файлы совпадают")
except Exception as e:
print(f"Произошла ошибка: {e}")
Это позволит вашему скрипту элегантно обрабатывать типичные проблемы, возникающие при копировании файлов.
Если вам нужно проверить, существует ли файл перед копированием, можно использовать комбинацию с модулем os:
import os
import shutil
source_file = 'source.txt'
destination_file = 'destination.txt'
if os.path.exists(source_file):
if os.path.exists(destination_file):
print("Целевой файл будет перезаписан!")
shutil.copy(source_file, destination_file)
print("Файл успешно скопирован")
else:
print("Исходный файл не существует")
Такой подход позволяет добавить дополнительную логику и проверки перед выполнением операции копирования.
| Функция | Что копирует | Особенности | Типичные применения |
|---|---|---|---|
| shutil.copyfile(src, dst) | Только содержимое файла | Требует, чтобы dst был полным путем файла | Простое копирование данных |
| shutil.copy(src, dst) | Содержимое + режим доступа | dst может быть директорией | Копирование с сохранением базовых разрешений |
| os.open() + чтение/запись | Только содержимое (ручное) | Гибкое управление процессом | Модификация содержимого при копировании |
Продвинутые функции shutil для сохранения метаданных
Когда требуется более точное копирование файлов с сохранением всех метаданных, на помощь приходят продвинутые функции модуля shutil. Эти методы особенно полезны при создании бэкапов, миграции данных и разработке систем развертывания.
Наиболее полным методом копирования отдельных файлов является shutil.copy2():
import shutil
# Копирование с сохранением всех метаданных
result = shutil.copy2('config.json', 'config.backup.json')
print(f"Файл скопирован в: {result}")
Функция copy2() сохраняет не только содержимое файла и режим доступа, но и метаданные о времени создания, последнего доступа и модификации. Эта функция пытается максимально точно воспроизвести оригинальный файл, что критически важно для некоторых приложений.
Для копирования целых директорий вместе со всем их содержимым используется shutil.copytree():
# Рекурсивное копирование директории
shutil.copytree('source_dir', 'backup_dir')
# Копирование с игнорированием определенных файлов
shutil.copytree('source_dir', 'backup_dir', ignore=shutil.ignore_patterns('*.pyc', '*.tmp', '.git'))
По умолчанию copytree() не будет перезаписывать существующую директорию. Если вам нужно обновить содержимое существующей директории, можно использовать параметр dirs_exist_ok=True (доступен в Python 3.8+):
# Python 3.8+ – копирование в существующую директорию
shutil.copytree('source_dir', 'existing_dir', dirs_exist_ok=True)
Для более ранних версий Python можно обойти это ограничение, предварительно удалив целевую директорию:
import shutil
import os
target_dir = 'backup_dir'
if os.path.exists(target_dir):
shutil.rmtree(target_dir)
shutil.copytree('source_dir', target_dir)
Одной из мощных возможностей copytree() является возможность настройки поведения при ошибках с помощью параметра copy_function:
def custom_copy(src, dst):
try:
return shutil.copy2(src, dst)
except PermissionError:
print(f"Пропускаю файл с ограниченными правами: {src}")
return dst
# Копирование с использованием кастомной функции
shutil.copytree('source_dir', 'backup_dir', copy_function=custom_copy)
Дмитрий Соколов, DevOps-инженер:
В процессе создания системы непрерывной интеграции для крупного проекта мы столкнулись с проблемой: деплоймент-скрипт должен был точно копировать конфигурационные файлы между серверами, сохраняя все разрешения и временные метки.
Первоначально мы использовали базовые методы копирования, которые не сохраняли все метаданные. Это приводило к тому, что системные сервисы не распознавали обновленные конфигурации, поскольку временные метки не совпадали с ожидаемыми.
Переход на
shutil.copy2()частично решил проблему, но настоящий прорыв произошел, когда мы реализовали кастомную функцию копирования на основеcopy2()с дополнительной логикой для обработки символических ссылок и специальных файлов:PythonСкопировать кодdef enhanced_copy(src, dst): if os.path.islink(src): linkto = os.readlink(src) if os.path.exists(dst): os.unlink(dst) os.symlink(linkto, dst) return dst else: return shutil.copy2(src, dst) shutil.copytree('/etc/app/configs', '/backup/configs', copy_function=enhanced_copy, symlinks=False)Этот подход позволил нам создать надежный механизм резервного копирования и восстановления, который корректно обрабатывал все типы файлов в нашей инфраструктуре. Время на отладку деплоймента сократилось на 70%, а количество инцидентов, связанных с неправильными разрешениями файлов, упало до нуля.
Альтернативные способы с использованием os и pathlib
Помимо модуля shutil, Python предлагает и другие подходы к копированию файлов, которые могут быть более предпочтительными в определенных сценариях. Модули os и pathlib предоставляют низкоуровневые инструменты для работы с файловой системой, дающие больше контроля над процессом.
Один из классических способов копирования файла — ручное чтение и запись содержимого:
def manual_copy(source, destination, buffer_size=1024*1024):
"""
Копирует файл с указанным размером буфера
Args:
source: путь к исходному файлу
destination: путь к целевому файлу
buffer_size: размер буфера в байтах (по умолчанию 1MB)
"""
with open(source, 'rb') as src:
with open(destination, 'wb') as dst:
while True:
buffer = src.read(buffer_size)
if not buffer:
break
dst.write(buffer)
# Использование
manual_copy('large_file.dat', 'large_file_copy.dat')
Этот подход дает полный контроль над процессом копирования и может быть оптимизирован под конкретные задачи. Например, можно регулировать размер буфера в зависимости от типа файлов или добавлять индикатор прогресса:
def manual_copy_with_progress(source, destination, buffer_size=1024*1024):
file_size = os.path.getsize(source)
copied = 0
with open(source, 'rb') as src:
with open(destination, 'wb') as dst:
while True:
buffer = src.read(buffer_size)
if not buffer:
break
dst.write(buffer)
copied += len(buffer)
percent = int(copied * 100 / file_size)
print(f"\rПрогресс: {percent}% [{copied}/{file_size} байт]", end="")
print() # Новая строка после завершения
Начиная с Python 3.4, можно использовать более современный объектно-ориентированный подход с модулем pathlib:
from pathlib import Path
def pathlib_copy(source, destination):
source_path = Path(source)
dest_path = Path(destination)
# Чтение исходного файла целиком и запись в целевой
dest_path.write_bytes(source_path.read_bytes())
# Копирование атрибутов файла (опционально)
os.chmod(destination, os.stat(source).st_mode)
# Использование
pathlib_copy('config.ini', 'config_copy.ini')
Этот метод прост и выразителен, но не оптимален для больших файлов, так как загружает всё содержимое в память. Для больших файлов можно комбинировать pathlib с буферизированным чтением:
from pathlib import Path
def pathlib_buffered_copy(source, destination, buffer_size=1024*1024):
source_path = Path(source)
dest_path = Path(destination)
with source_path.open('rb') as src:
with dest_path.open('wb') as dst:
while True:
buffer = src.read(buffer_size)
if not buffer:
break
dst.write(buffer)
В некоторых случаях полезно создать собственную функцию копирования, которая объединяет преимущества разных подходов:
from pathlib import Path
import os
import shutil
import time
def smart_copy(source, destination, preserve_metadata=True,
buffer_size=4*1024*1024, overwrite=True):
"""
Умное копирование файла с контролем метаданных и буферизацией
Args:
source: путь к исходному файлу
destination: путь к целевому файлу
preserve_metadata: сохранять ли метаданные файла
buffer_size: размер буфера для копирования
overwrite: перезаписывать ли существующий файл
"""
src_path = Path(source)
dst_path = Path(destination)
# Проверка существования исходного файла
if not src_path.exists():
raise FileNotFoundError(f"Исходный файл не найден: {source}")
# Проверка на перезапись
if dst_path.exists() and not overwrite:
raise FileExistsError(f"Целевой файл уже существует: {destination}")
# Проверка, что это не один и тот же файл
if os.path.samefile(src_path, dst_path):
raise shutil.SameFileError("Исходный и целевой файлы идентичны")
# Создание промежуточных директорий
dst_path.parent.mkdir(parents=True, exist_ok=True)
# Копирование содержимого с буферизацией
start_time = time.time()
with src_path.open('rb') as src:
with dst_path.open('wb') as dst:
while True:
buffer = src.read(buffer_size)
if not buffer:
break
dst.write(buffer)
# Копирование метаданных, если требуется
if preserve_metadata:
src_stat = os.stat(src_path)
os.utime(dst_path, (src_stat.st_atime, src_stat.st_mtime))
os.chmod(dst_path, src_stat.st_mode)
duration = time.time() – start_time
size_mb = src_path.stat().st_size / (1024 * 1024)
speed_mbps = size_mb / duration if duration > 0 else 0
return {
"source": str(src_path),
"destination": str(dst_path),
"size_mb": size_mb,
"time_seconds": duration,
"speed_mbps": speed_mbps
}
Автоматизация массового копирования файлов в Python
Реальная ценность Python проявляется при автоматизации массовых операций с файлами. Благодаря возможностям языка можно легко создавать сложные сценарии копирования, обработки и организации файлов. Рассмотрим несколько практических примеров массового копирования файлов.
Начнем с копирования файлов по шаблону с использованием модуля glob:
import shutil
import glob
import os
def copy_files_by_pattern(source_dir, dest_dir, pattern="*.txt"):
"""
Копирует все файлы, соответствующие шаблону, из исходной директории в целевую
"""
# Создаем целевую директорию, если не существует
os.makedirs(dest_dir, exist_ok=True)
# Получаем список файлов по шаблону
matched_files = glob.glob(os.path.join(source_dir, pattern))
# Копируем каждый файл
for file_path in matched_files:
filename = os.path.basename(file_path)
destination = os.path.join(dest_dir, filename)
shutil.copy2(file_path, destination)
print(f"Скопирован файл: {filename}")
return len(matched_files)
# Пример использования
count = copy_files_by_pattern("./data", "./backup", "*.csv")
print(f"Всего скопировано файлов: {count}")
Для более сложных сценариев можно создать функцию с расширенными возможностями фильтрации и обработки файлов:
import os
import shutil
import datetime
def advanced_batch_copy(source_dir, dest_dir,
file_types=None,
date_after=None,
min_size=None,
max_size=None,
rename_pattern=None,
recursive=False):
"""
Расширенное копирование файлов с фильтрацией по типу, размеру, дате и возможностью переименования
Args:
source_dir: исходная директория
dest_dir: целевая директория
file_types: список расширений файлов для копирования (например, ['.jpg', '.png'])
date_after: копировать файлы, созданные после указанной даты (datetime объект)
min_size: минимальный размер файла в байтах
max_size: максимальный размер файла в байтах
rename_pattern: шаблон для переименования (например, 'backup_{filename}')
recursive: рекурсивное копирование вложенных директорий
"""
copied_count = 0
# Создаем целевую директорию
os.makedirs(dest_dir, exist_ok=True)
# Функция для обработки каждого файла
def process_file(file_path, relative_path=""):
nonlocal copied_count
# Получаем информацию о файле
filename = os.path.basename(file_path)
file_stat = os.stat(file_path)
file_size = file_stat.st_size
file_mtime = datetime.datetime.fromtimestamp(file_stat.st_mtime)
file_ext = os.path.splitext(filename)[1].lower()
# Проверяем соответствие фильтрам
if file_types and file_ext not in file_types:
return
if min_size and file_size < min_size:
return
if max_size and file_size > max_size:
return
if date_after and file_mtime < date_after:
return
# Определяем имя файла в целевой директории
if rename_pattern:
new_filename = rename_pattern.format(
filename=filename,
basename=os.path.splitext(filename)[0],
ext=file_ext,
date=file_mtime.strftime('%Y%m%d'),
size=file_size
)
else:
new_filename = filename
# Создаем целевую структуру директорий при рекурсивном копировании
if recursive and relative_path:
target_dir = os.path.join(dest_dir, relative_path)
os.makedirs(target_dir, exist_ok=True)
dest_path = os.path.join(target_dir, new_filename)
else:
dest_path = os.path.join(dest_dir, new_filename)
# Копируем файл
shutil.copy2(file_path, dest_path)
copied_count += 1
print(f"Скопирован: {file_path} -> {dest_path}")
# Обход директорий
if recursive:
for root, dirs, files in os.walk(source_dir):
relative_path = os.path.relpath(root, source_dir)
if relative_path == '.':
relative_path = ''
for filename in files:
file_path = os.path.join(root, filename)
process_file(file_path, relative_path)
else:
for filename in os.listdir(source_dir):
file_path = os.path.join(source_dir, filename)
if os.path.isfile(file_path):
process_file(file_path)
return copied_count
Пример использования этой функции для различных сценариев:
# Копирование только изображений
advanced_batch_copy('./photos', './backup/images',
file_types=['.jpg', '.png', '.gif'])
# Копирование файлов больше 1MB, созданных за последнюю неделю
week_ago = datetime.datetime.now() – datetime.timedelta(days=7)
advanced_batch_copy('./documents', './recent_large_docs',
min_size=1024*1024,
date_after=week_ago)
# Рекурсивное копирование с переименованием
advanced_batch_copy('./project', './backup/project_files',
recursive=True,
rename_pattern='backup_{date}_{filename}')
Для мониторинга прогресса при копировании большого количества файлов можно использовать библиотеку tqdm:
import shutil
import os
from tqdm import tqdm
def copy_with_progress(source_dir, dest_dir, pattern="*"):
"""
Копирование файлов с отображением прогресса
"""
import glob
# Получаем список файлов
files = glob.glob(os.path.join(source_dir, pattern))
# Создаем директорию назначения
os.makedirs(dest_dir, exist_ok=True)
# Копируем с отображением прогресса
for file_path in tqdm(files, desc="Копирование файлов", unit="файл"):
filename = os.path.basename(file_path)
dest_path = os.path.join(dest_dir, filename)
shutil.copy2(file_path, dest_path)
return len(files)
| Сценарий | Рекомендуемый метод | Особенности | Типичное применение |
|---|---|---|---|
| Разовое копирование нескольких файлов | shutil.copy2() в цикле | Простота реализации | Скрипты резервного копирования |
| Копирование больших объемов данных | Буферизированное копирование с прогрессом | Контроль потребления памяти, наглядность | Миграция данных, обработка медиа |
| Создание структурированных копий | Рекурсивное копирование с фильтрацией | Гибкость в выборе файлов | Организация архивов, каталогизация |
| Интеграция в большие системы | Кастомные функции с обработкой ошибок | Надежность, логирование | Производственные системы, CI/CD |
Для регулярных задач копирования можно создать планировщик, который будет выполнять копирование по расписанию:
import schedule
import time
import os
import datetime
import shutil
def daily_backup():
"""Ежедневное резервное копирование важных данных"""
today = datetime.datetime.now().strftime('%Y%m%d')
backup_dir = f"./backups/daily_{today}"
print(f"Запуск ежедневного резервного копирования в {backup_dir}...")
# Здесь используем любую функцию копирования из примеров выше
copy_files_by_pattern("./important_data", backup_dir)
# Очистка старых бэкапов (оставляем только за последние 7 дней)
cleanup_old_backups("./backups", 7)
def cleanup_old_backups(backup_root, keep_days):
"""Удаляет старые резервные копии, оставляя только за указанное количество дней"""
cutoff_date = datetime.datetime.now() – datetime.timedelta(days=keep_days)
for item in os.listdir(backup_root):
item_path = os.path.join(backup_root, item)
if os.path.isdir(item_path) and item.startswith('daily_'):
try:
# Извлекаем дату из имени директории
date_str = item.replace('daily_', '')
backup_date = datetime.datetime.strptime(date_str, '%Y%m%d')
# Удаляем старые резервные копии
if backup_date < cutoff_date:
print(f"Удаление устаревшей резервной копии: {item}")
shutil.rmtree(item_path)
except (ValueError, Exception) as e:
print(f"Ошибка при обработке {item}: {e}")
# Настройка расписания
schedule.every().day.at("23:00").do(daily_backup)
# Запуск планировщика
print("Планировщик резервного копирования запущен...")
while True:
schedule.run_pending()
time.sleep(60)
Освоив различные методы копирования файлов в Python, вы получаете мощный инструмент для автоматизации рутинных задач и создания эффективных решений. От простейшего
shutil.copy()до комплексных систем с мониторингом прогресса и обработкой ошибок — выбор метода зависит от конкретной задачи и требований к производительности. Помните, что правильно выбранный метод копирования не только сэкономит время разработки, но и обеспечит надежность вашего решения в продакшене. Экспериментируйте, комбинируйте подходы и создавайте собственные функции, которые наилучшим образом соответствуют вашим потребностям. 🚀