На python.org.ua один из пользователей посетовал на то что не может понять ООП.
Я решил немного помочь ему в этом, а так же всем начинающим питонщикам с той же проблемой.
Надеюсь хоть кому-то пригодится.
Глава 1 или надо же как-то начинать
Допустим мы хотим написать программу для работы с файлами разных типов. Соответственно в зависимости от типа файла будет добавляться разный функционал, но какой мы пока не знаем.
Что известно точно это те операции что будут производится над файлом в любом случае, в не зависимости от типа. Например такие как "удаление" "копирование" "перенос" а также получение общих атрибутов файла.(размер, имя, тип и т.п)
При написании этой части программы без использования ООП пришлось бы писать просто функцию для каждого действия, просто передавая им путь к файлу. Или использовать библиотечные (входящие в поставку python).
Например так:
- import os
- import shutil
- import filecmp
- # path - путь к файлу который мы будем использовать
- path = "exemple.txt"
- # операции из стандартних библиотек os и shutil
- os.rename(src_path,dst_path) # переименовать
- shutil.copy(src_path,dst_path) # копировать
- shutil.move(src_path,dst_path) # переместить
- os.remove(path) # удалить
- size = os.path.getsize(path) # размер файла в байтах
- filecmp.cmp(path, path2) # сравнить файлы
- # теперь немного сложнее
- def GetModifyTime(path):
- """ функция полученя времни
- последнего изменения файла """
- from datetime import datetime # для преобразования даты в приемлемый формат
- return datetime.fromtimestamp(os.path.getmtime(path))
Все вроде бы просто, и вполне понятно и юзабельно. А теперь попробуем сделать практически тоже само но используя ООП-подход.
Для начала создадим некий класс FileBase - этот класс будет представлять наш файл на диске. Что это значит? Это значит что мы привяжем его к некоторому файлу, и все манипуляции будем производить над классом (точнее над одним из его экземпляров).
Про класс следует думать как про нечто, что представляет файл. Как про юриста что представляет человека :) То есть, как про что-то реальное.
Вот так:
- import os
- import shutil
- from datetime import datetime
- # self - это ссылка внутри класса, обозначает
- # сам класс. То есть ссылка на самого себя.
- class FileBase(object):
- def __init__(self, path):
- """ этот метод (функция класса) вызывается
- при создании копии (экземпляра) класса.
- В нем мы сразу привязываем класс к файлу.
- """
- self.path = path # self.path - это атрибут (параметр) нашего класса в котором записан путь к файлу
- def __str__(self):
- """Возвращаем путь к файлу
- при вызове экземпляра класса """
- return self.path
- def rename(self, newname):
- os.rename(self.path, os.path.join(os.path.dirname(self.path), newname))
- def copy(self, path):
- shutil.copy(self.path,path)
- return FileBase(path) # возвращаем новый объект для файла.
- def move(self,path):
- shutil.move(self.path, path)
- self.path = path
- def delete(self):
- """ удаляем файл """
- os.remove(self.path)
- self.path = None
- def size(self):
- """Возвращаем размер файла"""
- return os.path.getsize(self.path)
- def __cmp__(self, filobject):
- """сравниваем два файла
- если не равны - возвращаем разницу размеров
- """
- if filecmp.cmp(self.path, filobject):
- return 0
- else:
- return self.size - filobject.size
- def getModifyTime(self):
- """возвращаем время последней модификации файла"""
- return datetime.fromtimestamp(os.path.getmtime(self.path))
Сложнее немного не так ли? Но на самом деле это кажущаяся сложность, и почему мы увидим дальше.
Хочу обратить внимание, что rename в классе несколько не тот что в стандартной функции.
- os.rename(src_path,dst_path)
При вызове этой функции переименованный файл будет перемещён в папку dst_path под новым именем. В случае же с классом то вызов метода rename не переместит файл, а просто переименует его в той папке где он находится.
С функцией сравнения тоже есть отличия, я расскажу о них дальше.
Вот теперь давайте посмотрим как произвести одни и те же операции используя наш класс и без него:
- print "без использования ООП"
- # для начала возьмем файл
- path = "c:\\test\\test2\\example.txt"
- print os.path.getsize(path) # выводим размер файла
- print GetModifyTime(path) # выводим дату создания
- # где сейчас файл?
- print path
- # перемещаем файл в другую папку:
- dst = "c:\\test\\"+os.path.basename(path) # путь к новому файлу
- shutil.move(path,dst)
- # где теперь?
- print dst
- # копируем обратно
- shutil.copy(dst,path) # копировать
- # где теперь файлы?
- print "Файл - " + fileobj.path
- print "Копия файла" + copy_fileobj.path
- print GetModifyTime(path) # выводим дату последней модификации копии
- # сравниваем файлы:
- if filecmp.cmp(path, dst):
- print "одинаковые"
- else:
- print "разные"
отлично. Теперь то же но с использованием нашего класса:
- fileobj = FileBase("c:\\test\\test2\\example.txt") # теперь fileobj - наш файл
- print fileobj.size() # выводим размер файла
- print fileobj.getModifyTime() # выводим дату последней модификации
- # где сейчас файл?
- print fileobj
- # перемещаем в другую папку
- fileobj.move("c:\\test\\"+os.path.basename(fileobj))
- # где теперь?
- print fileobj
- # ок, копируем обратно одновременно получая объект копии файла
- copy_fileobj = fileobj.copy("c:\\test\\test2\\example.txt")
- # гд теперь файлы?
- print "Файл - " + fileobj
- print "Копия файла" + copy_fileobj
- # можно посмотреть дату последней подификации копии
- print copy_fileobj.getModifyTime()
- # сравним файл с копией
- if fileobj == copy_fileobj:
- print "одинаковые"
- else:
- print "разные"
Обратите внимание что оба файла сравниваются как обычные переменные. Можно также узнать какой из них больше, а какой меньше, наитивно, как у переменных. В не-ООП стиле пришлось бы писать и явно вызывать спец. функцию.
Я думаю разница уже заметна. Мы делали одни и те же вещи, но в случае с ООП мы оперируем некоторым объектом который представляет файл. Он его представляет и манипулирует им, порождая новые объекты-представители при копировании, изменяя собственные свойства при переносе и т.д. Так если бы мы делали это в файловом менеджере, например.
Без ооп - мы манипулируем файлами, относясь к нему непосредственно через пути, что несколько запутывает саму программу. Разница пока невелика, но стало немного проще.
Самое интересное что улучшение понятности программы при правильном использовании - не самое главное преимущество ООП. В следующем посте я расскажу про преимущество несколько более важное.
Я расскажу про наследование.