2016-01-30 6 views
1

Я бы хотел, чтобы стекировать фильтры вокруг функции open(). Эти фильтры допускают, например, изменение каждого встреченного a символов в b в потоке, считанном из файла.Укладка фильтров вокруг файлового объекта

Например, вот пример кода:

def filter (stream): 
    for line in stream: 
     yield line.replace('a', 'b') 

def add_filter(filter, file): 
    return io.TextIOWrapper(filter(file)) 

def processing_file(f): 
    import sys 
    for line in f: 
     sys.stdout.write("aa: " + line) 

f = open('./example.txt', 'r') 
f = add_filter(filter, f) 
processing_file(f) 

Я предполагаю, что функция filter_a() должна возвращать TextIOWrapper, чтобы имитировать результат open() функции. Но я продолжаю имея следующее сообщение об ошибке:

AttributeError: 'generator' object has no attribute 'readable' 

На самом деле, я понимаю ошибку, но я не знаю, как работать вокруг и заставить его работать должным образом.

+0

https://docs.python.org/2/library/io.html#io.TextIOWrapper показывает, что вам необходимо передать буфер, но вы передаете генератор. – noorul

+0

Да, но как мне превратить генератор в буфер? Думаю, это мой вопрос, как только вы удалили что-нибудь еще. – perror

ответ

0

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

Мое первоначальное намерение состояло в том, чтобы выйти со штабелируемым каркасом фильтров по потоку (open()). Попытка сделать его легким в использовании, также.

В основном я нашел вдохновение в этом answer на StackOverflow, который решал около 90% моей проблемы.

Итак, представьте себе, у нас есть два фильтра (которые кодируются как генераторы):

def tab_filter(stream): 
    for line in stream: 
     yield line.replace ('\t', ' ' * 8) 

def a_filter(stream): 
    for line in stream: 
     yield line.replace ('a', 'z') 

Тогда мы имеем этот класс позволяет обернуть генератор внутри потока:

class IterStream(object): 
    "File-like streaming iterator." 

    def __init__(self, generator): 
     self.generator = generator 
     self.iterator = iter(generator) 
     self.leftover = '' 

    def __len__(self): 
     return self.generator.__len__() 

    def __iter__(self): 
     return self.iterator 

    def next(self): 
     return self.iterator.next() 

    def read(self, size): 
     data = self.leftover 
     count = len(self.leftover) 
     try: 
      while count < size: 
       chunk = self.next() 
       data += chunk 
       count += len(chunk) 
     except StopIteration: 
      self.leftover = '' 
      return data 

     if count > size: 
      self.leftover = data[size:] 

     return data[:size] 

Используя его в коде будут следующие:

import sys 
f = IterStream(a_filter(IterStream(tab_filter(open('Example.txt', 'r'))))) 
for line in f: 
    sys.stdout.write("aa: " + line) 

Но, это еще не полностью удовлетворительно, потому что нам нужно много бесполезной укладки функций. Итак, я решил обернуть его внутри декоратора:

 
def streamfilter(filter): 
    def stream(iostream): 
     return IterStream(filter(iostream)) 
    return stream 

@streamfilter 
def tab_filter(stream): 
    for line in stream: 
     yield line.replace ('\t', ' ' * 8) 

@streamfilter 
def a_filter(stream): 
    for line in stream: 
     yield line.replace ('a', 'z') 

Затем, используя код является намного проще:

import sys 
f = a_filter(tab_filter(open('Example.txt', 'r'))) 
for line in f: 
    sys.stdout.write("aa: " + line) 

Я надеюсь, что некоторые из вас найдут это несколько строк полезно.

3

Вы можете перемещаться непосредственно над генератором фильтра:

with open('./example.txt', 'r') as f: 
    for line in filter(f): 
     sys.stdout.write("aa: " + line) 
+0

На самом деле это не соответствует моей потребности. Я хотел бы иметь возможность добавлять фильтр ** внутри ** TextIOWrapper' и использовать его обычно, поскольку фильтр не существует. Я изменю свой вопрос, чтобы сделать его более понятным. – perror

Смежные вопросы