如何编写一个行为与另一个动态确定的类完全相同的类? (Python)

发布于 2024-11-25 04:28:48 字数 2010 浏览 1 评论 0原文

我想创建一个“File”对象,在调用 ReadLine() 方法时返回“Line”对象,而不仅仅是字符串。我还希望能够使用包含文本文档绝对路径的字符串或字符串列表来初始化 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

但是,这看起来很笨重且非 Pythonic,因为我必须使用 Get_count() 和 Set_count() 方法来访问 File 对象的 .count 成员,而不仅仅是能够通过instance.count直接访问它。是否有一种更优雅的解决方案,允许我以成员身份访问 .count 成员,而不是使用 getter 和 setter?

另外,为了加分,我仍在试图弄清楚整个继承问题。有没有更好的方法来构建类之间的关系?

I want to create a 'File' object that returns 'Line' objects when the ReadLine() method is invoked instead of just strings. I also want to be able to initialize the File object with either a string containing the absolute path of a text document, or with a list of a strings, and have the resulting instance behave identically in either case. The only way that I could figure out how to do this is by wrapping a File object around either a FileDoc or a FileList object, depending on the input type. Here is an abbreviated version of the solution I have so far:

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

However, this seems clunky and non-Pythonic as I have to use the Get_count() and Set_count() methods to access the .count member of the File object, instead of just being able to access it directly with instance.count. Is there a more elegant solution, one which would allow me to access the .count member as a member instead of with getters and setters?

Also, for bonus points, I'm still trying to figure out the whole inheritance thing. Is there a better way to structure the relationships between the classes?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

红尘作伴 2024-12-02 04:28:48

要简化 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')

To simplify the count property, use the property decorator:

@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

Or if you don't want it to be a decorator:

count = property(Get_count, Set_count)

As for your inheritance scheme, I think it's okay; since you're using File to hide most of the details, it shouldn't be too hard to change it later if need be. @MannyD's comment is a good idea for restructuring; note that, for instance, file objects are iterables, just like lists.

As a side note, FileLine may be better as a collections.namedtuple (just saying):

Line = collections.namedtuple('Line', 'line count path')
落叶缤纷 2024-12-02 04:28:48

我个人认为你的类层次结构太复杂了。我不确定您如何在代码中使用它,但“文件”一词似乎被过度使用了。字符串列表根本不是文件,因此我创建了类似 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 的建议使用属性,但前提是您确实有理由在读取行时更改文件读取器的内部计数器。

Personally I think that you class hierarchy is way too complicated. I'm not sure how you use it in your code, but the word File seems to be overused. The list of strings is not file at all, hence I'd create something like LineReader to be able to read from different sources and than feed it with either a file or a list iterator.

Consider the following code:

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()

Looks far less complicated for me and does the job. I have no idea why you need access to counter as it's written to Line object. You can use a property as @li.davidm recommends, but only if you really have a reason to change the internal counter of file reader while reading the lines.

一杆小烟枪 2024-12-02 04:28:48

除非有理由强制执行继承层次结构,否则我会考虑使用工厂模式并利用 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 风格指南它将使您的代码更容易被其他人阅读。

Unless there is a reason to enforce the inheritance hierarchy, I'd consider using a factory pattern and take advantage of the fact that Python is dynamically typed:

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

There is no need for the File() class as it does not offer anything over the base classes so in this case it is just extra code to maintain. Also, rather than comparing for a specific type, it's usually better to use isinstance(object, type) to work with derived classes as well.

On a side note, I'd suggest following the PEP 8 style guide as it will make your code easier to read for others.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文