1

我不明白 Python 中的“静态”类数据是如何工作的。我正在尝试复制我在 Java 中经常使用的模式,在该模式中我创建了一个中央仿数据库类,并将它的实例提供给任何需要它的类。

但是,这就是我迷失 Python 的地方,似乎每当我尝试访问“静态”变量时,我都会创建一个实例级别的实例。这很令人困惑。

这是代码:

import csv 

class Database(object):
    data = list()
    def __init__(self):
        pass

    def connect(self, location=None):
        path = '.\\resources\\database\\'
        with open(path + 'info.csv', 'rU') as f:
            csv_data = csv.reader(f, delimiter=',')
            for row in csv_data:
                self.data.append(Entry(*row))

如您所见,非常简单。我data在 之外声明init,我相信这使它成为类级别。然后我遍历一个 csv 文件并将每一行的单元格字段附加到data列表中。

这就是我感到困惑的地方。我想做的第一件事是对data列表中的第一个条目进行切片(这只是从 excel 表中命名的列)。但在这样做时,它似乎创建了一个实例级别版本,data而不是修改data我想要的类级别。

所以,我修改了代码如下

import csv 

class Database(object):
    data = list()
    def __init__(self):
        pass

    def connect(self, location=None):
        path = '.\\resources\\database\\'
        with open(path + 'info.csv', 'rU') as f:
            csv_data = csv.reader(f, delimiter=',')
            for row in csv_data:
                self.data.append(Entry(*row))
            self.data = self.data[1:]  ## <--- NEW LINE HERE

如您所见,我尝试像对任何其他列表一样对其进行切片,但这样做时,它(似乎)成为了本地版本data

在我的main()中,我有以下代码来测试一些属性:

def main():
    settings = Settings()

    db = csv_loader.Database()
    db.connect(settings.database)
    print 'gui.py:', db.data[0].num
    print 'gui.py:', db.data[0].name
    print 'gui.py:', len(db.data)

这与预期的一样。由于之前的切片操作,它有 143 个条目。

现在,我有另一个类,它有一个实例,Database() 切片没有效果。

self.db = Database()
self.settings = Settings()
print 'tabOne.py:', self.db.data[0].conf_num
print 'tabOne.py:', self.db.data[0].conf_name
print len(self.db.data)

该类的打印输出显示列表中有 144 个元素——因此切片操作实际上并未修改类级别data

我错过了什么?我是在尝试错误地修改类级别变量还是什么?

4

1 回答 1

4

您基本上是正确的,但是,列表切片会创建一个副本,因此该行:

self.data = self.data[1:]  

创建数据的副本,然后将其分配给新的实例属性

以下是所有这些的工作原理:当为实例查找属性时,python 首先在实例中__dict__查找该属性。如果它不在实例的 s__dict__中,python 接下来会在类中​​查找,__dict__然后在父类的__dict__s 中查找(按照方法解析顺序)。因此,当您第一次开始访问self.data(附加到它)时,您不会在data实例__dict__的. 但是,当您明确分配给 时,您会突然在实例中添加一个条目,该条目将从那时起使用。data__dict__self.dict__dict__

有几个解决方法:

Database.data = self.data[1:]

可以正常工作,或者您可以使用切片分配来修改self.data列表:

self.data[:] = self.data[1:]

或任何其他修改列表的方法:

self.data.pop(0)

最后一个选项,可能是最明确的选项是更改connectclassmethod

@classmethod
def connect(cls, location=None):
    path = '.\\resources\\database\\'
    with open(path + 'info.csv', 'rU') as f:
        csv_data = csv.reader(f, delimiter=',')
        for row in csv_data:
            cls.data.append(Entry(*row))
        cls.data = cls.data[1:]

现在该方法的第一个参数是,而不是实例,因此我们将习惯更改self为同样习惯cls(尽管我也见过klass)。可以从实例或类本身调用此方法:

database_instance = Database()
database_instance.connect()

Database.connect()


在评论中,提到了使用模块来处理这类事情。Amodule也可以非常简单地在整个程序中传输状态,并且它们最终表现得非常像单例——事实上,它们通常被推荐用于 python 而不是单例:

"""
Module `Database` (found in Database.py)
"""
data = []

def connect(self, location=None):
    global data
    path = '.\\resources\\database\\'
    with open(path + 'info.csv', 'rU') as f:
        csv_data = csv.reader(f, delimiter=',')
        for row in csv_data:
            data.append(Entry(*row))
        data = data[1:]

现在在另一个模块中,您只需:

import Database
Database.connect()
print Database.data

等等等等

于 2013-01-15T18:06:30.193 回答