суббота, 1 марта 2008 г.

ООП как способ сделать мир понятнее. Часть 1.

На python.org.ua один из пользователей посетовал на то что не может понять ООП.
Я решил немного помочь ему в этом, а так же всем начинающим питонщикам с той же проблемой.
Надеюсь хоть кому-то пригодится.

Глава 1 или надо же как-то начинать

Допустим мы хотим написать программу для работы с файлами разных типов. Соответственно в зависимости от типа файла будет добавляться разный функционал, но какой мы пока не знаем.
Что известно точно это те операции что будут производится над файлом в любом случае, в не зависимости от типа. Например такие как "удаление" "копирование" "перенос" а также получение общих атрибутов файла.(размер, имя, тип и т.п)
При написании этой части программы без использования ООП пришлось бы писать просто функцию для каждого действия, просто передавая им путь к файлу. Или использовать библиотечные (входящие в поставку python).
Например так:

  1. import os  
  2. import shutil  
  3. import filecmp  
  4.   
  5. # path - путь к файлу который мы будем использовать  
  6. path = "exemple.txt"  
  7.   
  8. # операции из стандартних библиотек os и shutil  
  9. os.rename(src_path,dst_path)     # переименовать  
  10. shutil.copy(src_path,dst_path)   # копировать  
  11. shutil.move(src_path,dst_path)   # переместить  
  12. os.remove(path)                  # удалить  
  13. size = os.path.getsize(path)     # размер файла в байтах  
  14. filecmp.cmp(path, path2)         # сравнить файлы  
  15.   
  16. # теперь немного сложнее  
  17. def GetModifyTime(path):  
  18.     """ функция полученя времни  
  19.     последнего изменения файла """  
  20.     from datetime import datetime   # для преобразования даты в приемлемый формат  
  21.     return datetime.fromtimestamp(os.path.getmtime(path))  

Все вроде бы просто, и вполне понятно и юзабельно. А теперь попробуем сделать практически тоже само но используя ООП-подход.
Для начала создадим некий класс FileBase - этот класс будет представлять наш файл на диске. Что это значит? Это значит что мы привяжем его к некоторому файлу, и все манипуляции будем производить над классом (точнее над одним из его экземпляров).
Про класс следует думать как про нечто, что представляет файл. Как про юриста что представляет человека :) То есть, как про что-то реальное.

Вот так:
  1. import os  
  2. import shutil  
  3. from datetime import datetime  
  4.   
  5. # self - это ссылка внутри класса, обозначает  
  6. # сам класс. То есть ссылка на самого себя.  
  7. class FileBase(object):  
  8.     def __init__(self, path):  
  9.         """ этот метод (функция класса) вызывается  
  10.         при создании копии (экземпляра) класса.  
  11.         В нем мы сразу привязываем класс к файлу. 
  12.         """  
  13.         self.path = path # self.path - это атрибут (параметр) нашего класса в котором записан путь к файлу  
  14.   
  15.     def __str__(self):  
  16.         """Возвращаем путь к файлу  
  17.         при вызове экземпляра класса """  
  18.         return self.path  
  19.   
  20.     def rename(self, newname):  
  21.         os.rename(self.path, os.path.join(os.path.dirname(self.path), newname))  
  22.   
  23.     def copy(self, path):  
  24.         shutil.copy(self.path,path)  
  25.         return FileBase(path) # возвращаем новый объект для файла.  
  26.   
  27.     def move(self,path):  
  28.         shutil.move(self.path, path)  
  29.         self.path = path  
  30.   
  31.     def delete(self):  
  32.         """ удаляем файл """  
  33.         os.remove(self.path)  
  34.         self.path = None  
  35.   
  36.     def size(self):  
  37.         """Возвращаем размер файла"""  
  38.         return os.path.getsize(self.path)  
  39.   
  40.     def __cmp__(self, filobject):  
  41.         """сравниваем два файла 
  42.         если не равны - возвращаем разницу размеров 
  43.         """  
  44.         if filecmp.cmp(self.path, filobject):  
  45.             return 0  
  46.         else:  
  47.             return self.size - filobject.size  
  48.   
  49.     def getModifyTime(self):  
  50.         """возвращаем время последней модификации файла"""  
  51.         return datetime.fromtimestamp(os.path.getmtime(self.path))  

Сложнее немного не так ли? Но на самом деле это кажущаяся сложность, и почему мы увидим дальше.
Хочу обратить внимание, что rename в классе несколько не тот что в стандартной функции.
  1. os.rename(src_path,dst_path)   

При вызове этой функции переименованный файл будет перемещён в папку dst_path под новым именем. В случае же с классом то вызов метода rename не переместит файл, а просто переименует его в той папке где он находится.
С функцией сравнения тоже есть отличия, я расскажу о них дальше.

Вот теперь давайте посмотрим как произвести одни и те же операции используя наш класс и без него:
  1. print "без использования ООП"  
  2. # для начала возьмем файл  
  3. path = "c:\\test\\test2\\example.txt"  
  4. print os.path.getsize(path) # выводим размер файла  
  5. print GetModifyTime(path) # выводим дату создания  
  6. # где сейчас файл?  
  7. print path  
  8. # перемещаем файл в другую папку:  
  9. dst = "c:\\test\\"+os.path.basename(path) # путь к новому файлу  
  10. shutil.move(path,dst)  
  11. # где теперь?  
  12. print dst  
  13. # копируем обратно  
  14. shutil.copy(dst,path)   # копировать  
  15. # где теперь файлы?  
  16. print "Файл - " + fileobj.path  
  17. print "Копия файла" + copy_fileobj.path  
  18. print GetModifyTime(path) # выводим дату последней модификации копии  
  19. # сравниваем файлы:  
  20. if filecmp.cmp(path, dst):  
  21.     print "одинаковые"  
  22. else:  
  23.     print "разные"  

отлично. Теперь то же но с использованием нашего класса:
  1. fileobj = FileBase("c:\\test\\test2\\example.txt"# теперь fileobj - наш файл  
  2. print fileobj.size() # выводим размер файла  
  3. print fileobj.getModifyTime() # выводим дату последней модификации  
  4. # где сейчас файл?  
  5. print fileobj  
  6. # перемещаем в другую папку  
  7. fileobj.move("c:\\test\\"+os.path.basename(fileobj))  
  8. # где теперь?  
  9. print fileobj  
  10. # ок, копируем обратно одновременно получая объект копии файла  
  11. copy_fileobj = fileobj.copy("c:\\test\\test2\\example.txt")  
  12. # гд теперь файлы?  
  13. print "Файл - " + fileobj  
  14. print "Копия файла" + copy_fileobj  
  15. # можно посмотреть дату последней подификации копии  
  16. print copy_fileobj.getModifyTime()  
  17. # сравним файл с копией  
  18. if fileobj == copy_fileobj:  
  19.     print "одинаковые"  
  20. else:  
  21.     print "разные"  

Обратите внимание что оба файла сравниваются как обычные переменные. Можно также узнать какой из них больше, а какой меньше, наитивно, как у переменных. В не-ООП стиле пришлось бы писать и явно вызывать спец. функцию.

Я думаю разница уже заметна. Мы делали одни и те же вещи, но в случае с ООП мы оперируем некоторым объектом который представляет файл. Он его представляет и манипулирует им, порождая новые объекты-представители при копировании, изменяя собственные свойства при переносе и т.д. Так если бы мы делали это в файловом менеджере, например.
Без ооп - мы манипулируем файлами, относясь к нему непосредственно через пути, что несколько запутывает саму программу. Разница пока невелика, но стало немного проще.
Самое интересное что улучшение понятности программы при правильном использовании - не самое главное преимущество ООП. В следующем посте я расскажу про преимущество несколько более важное.
Я расскажу про наследование.

понедельник, 11 февраля 2008 г.

Поставил Sabayon Linux

После долгих спотыканий и попыток поставить Gentoo, в результате поставил себе Sabayon Linux - это дистрибутив на базе того же Gentoo со всеми вытекающими последствиями. Что понравилось - работа из коробки + хороший инсталятор +отличная поддержка граф. плат. Никаких танцев с бубном, все запустилось и сразу заработало со всеми эффектами и ускорением. Не понравилось (точнее просто немного расстроило) - слишком много всего ставится сразу. Я бы все-таки хотел ставить не все, а только то что хочу. Учитывая практическое отсутствие интеренета дома, это не большая проблема, - дойдут руки, удалю лишнее. Ну и хотелось бы xfce вместо кде/гнома, но это я придираюсь :)
Попробуйте, для старта в gentoo - самое то.
Единственное что меня реально смущает в линуксе - отсутсвие нормального файлового менеджера. Ну не привык я мышкой тыкать, мне бы что-то типа TotalCommander, или Far'а. mc - не то.
Кто чем пользуется?

воскресенье, 9 декабря 2007 г.

Syntax Highlighter в Blogger'е

При написании предыдущего поста первая проблема которая у меня возникла - это подсветка кода.
После довольно долгих поисков было найдено отличное решение - Syntax Highligter Widget на базе SyntaxHighlighter 1.5.1 , за что огоромное спасибо Carl Mason'у aka FaziBear.
Просто нажмите Add to Blogger.
Как пользоваться написано здесь

суббота, 8 декабря 2007 г.

Интересный tip пр работе с словарями в темплейтах Dajngo

Есть маленький "tip" при работе с словарями в темплейтах Джанги - если вы используете словарь списков. Например вот так запишем праздничные дни в месяце.

  1. monthlist = {'April':(1,2,3), 'May':(5,6,8)}  

Теперь нам нужно добраться до каждой записи словаря в темплейте.
  1. {% for monthname in monthlist.items %}  
  2.   {{monthname.0}} // названия месяца  
  3.   {% for holiday in monthname.1 %} // получаем все записи из списка  
  4.      {{ holiday}}  
  5.   {% endfor %}  
  6. {% endfor %}  

Для меня было не очевидно.