Поиск по блогу

четверг, 16 января 2014 г.

Еще раз об обработке потоков (пример Converter, reader, writer)

фрагменты кода из книги М.Лутца гл.30, пример: "Вспомните универсальную функцию обработки потоков данных, которая частично была реализована во введении в ООП в главе 25" (стр.705) ...объекты чтения (reader) и записи (writer) встраиваются в экземпляр класса (композиция), а логика преобразования поставляется в виде подкласса (наследование), а не в виде отдельной функции.
In []:
def processor(reader, converter, writer):
    while 1:
        data = reader.read()
        if not data: break
        data = converter(data)
        writer.write(data)
Однако вместо простой функции мы могли бы реализовать обработку в виде класса, который использует прием композиции, чтобы обеспечить поддержку наследования и более удобную конструкцию программного кода. Одна из возможных реализаций этого класса содержится в файле streams.py и приводится в ниже:
In [9]:
class Processor:
    def __init__(self, reader, writer):
        self.reader = reader
        self.writer = writer
    def process(self):
        while 1:
            data = self.reader.readline()
            if not data: break
            data = self.converter(data)
            self.writer.write(data)
    def converter(self, data):
        assert False, 'converter must be defined' # Или возбудить исключение
Этот класс определяет метод converter, который, как ожидается, будет переопределен в подклассах.
Это пример использования абстрактных суперклассов, с которыми мы познакомились в главе 28 (подробнее об инструкции assert рассказывается в седьмой части книги).
При таком подходе объекты чтения (reader) и записи (writer) встраиваются в экземпляр класса (композиция), а логика преобразования поставляется в виде подкласса (наследование), а не в виде отдельной функции.
Ниже приводится содержимое файла converters.py:
In [6]:
!type result_2.txt # первый раз так распечатываю файл в Windows
12345,456,path

12345,456,path

12345,456,path

12345,456,path

12345,456,path

12345,456,path


In [12]:
#from streams import Processor
class Uppercase(Processor):
    def converter(self, data):
        return data.upper()
if __name__ =='__main__':
    import sys
    obj = Uppercase(open('result_2.txt'), sys.stdout)
    obj.process()
12345,456,PATH

12345,456,PATH

12345,456,PATH


Здесь класс Uppercase наследует логику цикла обработки потока данных (и все остальное, что может присутствовать в суперклассах). В нем необходимо определить лишь то, что будет уникальным для него, – логику преобразования данных.
Если запустить этот файл, он создаст и запустит экземпляр класса Uppercase, который прочитает содержимое файла spam.txt, преобразует все символы в верхний регистр и выведет их в поток stdout:
Для обработки потоков различных видов достаточно передать конструктору класса объекты требуемых типов. Ниже приводится пример реализации вывода в файл вместо стандартного потока вывода:
In [14]:
#C:\lp4e> python
#import converters
prog = Uppercase(open('result_2.txt'), open('result_2.txt', 'w'))
prog.process()
Дальше я снова попытался распечатать result_2.txt ... но не получилось, посмотрел на файл и обнаружил, что он занимает не 1 кб, как раньше, а 0 ... Ага, я это уже проходил. У меня стоит Питон 2.7 (а код примера для 3-го). Предыдущая команда перезаписи файла на закрыла файл, как это сделать?
Попробовал исследовать объект prog (.+клавиша TAB), сконструировал:
In [22]:
prog.reader.close
prog.writer.close
Out[22]:
<function close>
In [20]:
!type result_2.txt
Но файл в проводнике не открывался, а в редакторе N++ он обновился и стал пустым... Значит, нужен другой код... Эту проблему рвссмотрим в последующих постах, а здесь пойдем дальше... Просто возьмем другой файл
In [23]:
!type result_1.txt
12345,456,path

12345,456,path


Но, как предлагалось ранее, мы могли бы также реализовать объекты, обернутые в классы, которые определяют необходимые интерфейсные методы ввода и вывода. Ниже приводится простой пример, где вывод осуществляется через класс, который обертывает выводимый текст в теги HTML:
In [24]:
class HTMLize:
    def write(self, line):
        print '<PRE>%s</PRE>' % line.rstrip()
        
Uppercase(open('result_1.txt'), HTMLize()).process()
<PRE>12345,456,PATH</PRE>
<PRE>12345,456,PATH</PRE>

Если проследить порядок выполнения этого примера, можно заметить, что было получено два варианта преобразований – приведение символов к верхне- му регистру (наследованием) и преобразование в формат HTML (композицией), хотя основная логика обработки в оригинальном суперклассе Processor ничего не знает ни об одном из них.
Программному коду, выполняющему обработку, нужны только метод write – в классах, выполняющих запись, и метод convert. Его совершенно не интересует, что делают эти методы. Такой полиморфизм и инкапсуляция логики составляют основу такой мощи классов.
В этом примере суперкласс Processor реализует только цикл сканирования файла.
Для выполнения более существенных действий его можно было бы расширить, чтобы обеспечить поддержку дополнительных инструментов в его подклассах и постепенно превратить все это в полноценный фреймворк.
Создав такой инструмент один раз, вы сможете многократно использовать его во всех своих программах. Даже в этом простом примере благодаря тому, что с помощью классов можно упаковать и унаследовать так много, все, что нам пришлось сделать, – это реализовать этап преобразования в формат HTML, а все остальное у нас уже и так имелось.
Итак, эти примеры мен понадобятся ... почему бы не использовать идею конвертера? Но сначала разберемся с обнаруженой проблемой. Почему подвисла запись в файл?

Комментариев нет:

Отправить комментарий