Python类__init__布局?

时间:2013-04-16 15:17:33

标签: python class init

在python中,写一个__init__定义是不好的形式,如:

class someFileType(object):
    def __init__(self, path):
        self.path = path
        self.filename = self.getFilename()
        self.client = self.getClient()
        self.date = self.getDate()
        self.title = self.getTitle()
        self.filetype = self.getFiletype()
    def getFilename(self):
        '''Returns entire file name without extension'''
        filename = os.path.basename(self.path)
        filename = os.path.splitext(filename)
        filename = filename[0]
        return filename
    def getClient(self):
        '''Returns client name associated with file'''
        client = self.filename.split()
        client = client[1] # Assuming filename is formatted "date client - docTitle"
        return client

其中初始化变量是对返回字符串的函数的调用?或者它被认为是懒惰的编码?每当我想引用文件的某些方面时,主要是为了避免将something.filetype写为something.getFiletype()

此代码用于按客户端将文件排序到文件夹中,然后按文档类型排序,以及基于文件名中的数据进行其他操作。

2 个答案:

答案 0 :(得分:12)

不,我不明白为什么那会是糟糕的形式。实际上,在创建实例时仅计算一次这些值可能是一个好主意。

您还可以使用缓存property s:

推迟计算,直到需要为止
class SomeFileType(object):
    _filename = None
    _client = None

    def __init__(self, path):
        self.path = path

    @property
    def filename(self):
        if self._filename is None: 
            filename = os.path.basename(self.path)
            self._filename = os.path.splitext(filename)[0]
        return self._filename

    @property
    def client(self):
        '''Returns client name associated with file'''
        if self._client is None:
            client = self.filename.split()
            self._client = client[1] # Assuming filename is formatted "date client - docTitle"
        return self._client

现在,访问somefiletypeinstance.client将根据需要触发self.filename的计算,并缓存自己计算的结果。

在这种特定情况下,您可能也希望将.path作为属性;一个用清除缓存值的setter:

class SomeFileType(object):
    _filename = None
    _client = None

    def __init__(self, path):
        self._path = path

    @property
    def path(self):
        return self._path

    @path.setter
    def path(self, value):
        # clear all private instance attributes
        for key in [k for k in vars(self) if k[0] == '_']:
            delattr(self, key)
        self._path = value

    @property
    def filename(self):
        if self._filename is None: 
            filename = os.path.basename(self.path)
            self._filename = os.path.splitext(filename)[0]
        return self._filename

    @property
    def client(self):
        '''Returns client name associated with file'''
        if self._client is None:
            client = self.filename.split()
            self._client = client[1] # Assuming filename is formatted "date client - docTitle"
        return self._client

因为基于property的缓存会增加一些复杂性开销,所以你需要考虑它是否真的值得你花时间;对于您的具体,简单的例子,它可能不是。您的属性的计算成本确实非常低,除非您计划创建大量这些类,否则与必须维护按需缓存属性的心理成本相比,提前计算属性的开销可以忽略不计。

答案 1 :(得分:5)

您的代码正在做两件事:

a)通过将某些计算属性公开为变量而不是函数来简化类API。

b)预先计算它们的值。

第一项任务是属性的用途;直接使用会使您的代码更简单,而不是更复杂,并且(同样重要)会使意图更清晰:

class someFileType(object):
    @property
    def filename(self):
        return os.path.basename(self.path)

然后您可以编写var.filename,然后动态计算路径中的文件名。

@ Martijn的解决方案增加了缓存,它还负责部分b(预计算)。在你的例子中,至少,计算是便宜的,所以我认为没有任何好处。

相反,缓存或预计算会引发一致性问题。请考虑以下代码段:

something = someFileType("/home/me/document.txt")
print something.filename    # prints `document`
...
something.path = "/home/me/document-v2.txt"
print something.filename   # STILL prints `document` if you cache values

最后一句话应该打印什么?如果您缓存计算,仍会获得document而不是document-v2除非您某些,否则任何人都不会尝试更改基本变量,您需要避免缓存,或采取措施以确保一致性。最简单的方法是禁止修改path - 其中一项属性为designed to do.

结论:使用属性简化界面。不要缓存计算,除非出于性能原因需要。如果您缓存,请采取措施以确保一致性,例如将基础值设为只读。

PS。问题类似于数据库规范化(非规范化设计会引发一致性问题),但在python中,您有更多资源可以保持同步。