Как я могу написать класс, который ведет себя точно так же, как другой динамически определенный класс? (Python) - PullRequest
0 голосов
/ 17 июля 2011

Я хочу создать объект 'File', который возвращает объекты 'Line', когда вызывается метод ReadLine () вместо просто строк. Я также хочу иметь возможность инициализировать объект File либо строкой, содержащей абсолютный путь к текстовому документу, либо списком строк, и в любом случае результирующий экземпляр будет вести себя одинаково. Единственный способ выяснить, как это сделать, - обернуть объект File вокруг объекта FileDoc или FileList, в зависимости от типа ввода. Вот сокращенная версия решения, которое я имею до сих пор:

class Line(object):
    def __init__(self, line, count, fpath):
        self.text = line
        self.count = count
        self.fname = fpath.split('/')[-1]

class FileBase(object):
    def __init__(self):
        pass

    def Open(self):
        self.count = 0

    def Readline(self):
        pass

    def Get_count(self):
        return self.count

    def Set_count(self, val):
        self.count = val

class FileList(FileBase):
    def __init__(self, lines):
        self.lines = lines
        self.Open()

    def ReadLine(self):
        self.count += 1
        try:
            return Line(line=self.lines[self.count - 1], count=self.count - 1, fpath='list')
        except IndexError:
            raise StopIteration

class FileDoc(FileBase):
    def __init__(self, fpath):
        self.fpath = fpath
        self.Open()

    def Open(self):
        self.count = 0
        self.file = open(self.fpath, 'r')

    def ReadLine(self):
        self.count += 1
        return Line(line=self.file.next(), count=self.count - 1, fpath=self.fpath)

class File(FileBase):
    def __init__(self, input):
        if type(input) == type(''):
            self.actual = FileDoc(input)
        elif type(input) == type([]):
            self.actual = FileList(input)
        else:
            raise NonRecognizedInputError

    def Open(self):
        self.actual.Open()

    def ReadLine(self):
        return self.actual.ReadLine()

    def Get_count(self):
        return self.actual.count

    def Set_count(self, val):
        self.actual.count = val

Однако это кажется неуклюжим и непифоническим, так как мне приходится использовать методы Get_count () и Set_count () для доступа к члену .count объекта File, вместо того, чтобы просто иметь доступ к нему напрямую с помощью instance.count , Есть ли более элегантное решение, которое позволило бы мне получить доступ к члену .count в качестве члена, а не с помощью методов получения и установки?

Кроме того, для получения бонусных баллов я все еще пытаюсь выяснить всю проблему наследования. Есть ли лучший способ структурировать отношения между классами?

Ответы [ 3 ]

1 голос
/ 17 июля 2011

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

Рассмотрим следующий код:

class Line(object):
    def __init__(self, line, count, fpath):
        self.text = line
        self.count = count
        self.fname = fpath

class LineReader(object):
    def __init__(self, iterator, fname):
        self.iterator = iterator
        self.fname = fname
        self.count = 0

    def ReadLine(self):
        line = Line(self.iterator.next(), self.count, self.fname)
        self.count += 1        
        return line

class LineSource(object):
    def __init__(self, input):
        if type(input) == type(''):
            self.reader = LineReader(open(input), input.split('/')[-1])
        elif type(input) == type([]):
            self.reader = LineReader(iter(input), 'list')
        else:
            raise NonRecognizedInputError

    def ReadLine(self):
        return self.reader.ReadLine()

Выглядит гораздо менее сложным для меня и выполняет свою работу.Я понятия не имею, зачем вам нужен доступ к счетчику, так как он записан на объект Line.Вы можете использовать свойство, которое рекомендует @ li.davidm, но только если у вас действительно есть причина изменить внутренний счетчик программы чтения файлов при чтении строк.

1 голос
/ 17 июля 2011

Чтобы упростить свойство count, используйте декоратор property:

@property
def count(self):
    return self._count  # or return self.actual.count

@count.setter
def count(self, value):
    self._count = value  # or self.actual.count = value

Или, если вы не хотите, чтобы он был декоратором:

count = property(Get_count, Set_count)

Какдля вашей схемы наследования, я думаю, что все в порядке;поскольку вы используете File, чтобы скрыть большинство деталей, вам не составит труда изменить его позже, если это будет необходимо.Комментарий @ MannyD - хорошая идея для реструктуризации;обратите внимание, что, например, file объекты являются итеративными, как списки.

В качестве примечания, FileLine может быть лучше, чем collections.namedtuple (просто говоря):

Line = collections.namedtuple('Line', 'line count path')
0 голосов
/ 17 июля 2011

Если нет причин для принудительной реализации иерархии наследования, я бы рассмотрел использование фабричного шаблона и воспользовался тем, что Python динамически типизирован:

def FileFactory(input):
    if isinstance(input, types.StringTypes):
        return FileDoc(input)
    if isinstance(input, (types.ListType, types.TupleType)):
        return FileList(input)
    raise NonRecognizedInputError()

Нет необходимости в классе File (), поскольку он не предлагает ничего по сравнению с базовыми классами, поэтому в данном случае это просто дополнительный код для поддержки. Кроме того, вместо сравнения для определенного типа обычно лучше использовать isinstance(object, type) для работы и с производными классами.

В дополнение к этому, я бы предложил следовать руководству по стилю PEP 8 , поскольку это облегчит чтение вашего кода для других.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...