Python 混淆了如何在实例之间共享和修改类级数据。

Python 混淆了如何在实例之间共享和修改类级数据。,python,Python,我不理解Python中“静态”类数据是如何工作的。我试图复制我在Java中经常使用的一种模式,在这种模式中,我创建了一个中心faux数据库类,并将其实例提供给任何需要它的类 然而,在这里我对Python感到迷茫,似乎每当我尝试访问“静态”变量时,我都会创建一个实例级别的变量。这很令人困惑 代码如下: import csv class Database(object): data = list() def __init__(self): pass de

我不理解Python中“静态”类数据是如何工作的。我试图复制我在Java中经常使用的一种模式,在这种模式中,我创建了一个中心faux数据库类,并将其实例提供给任何需要它的类

然而,在这里我对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))
正如你所见,非常直截了当。我在init之外声明数据,我认为它是类级别的。然后我循环浏览一个csv文件,并将每行的单元格字段附加到数据列表中

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

因此,我修改了代码如下

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
这与预期的一样。由于前面的切片操作,它有143个条目

现在,我有另一个类,它有一个数据库实例,但是切片没有任何效果

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个元素——因此切片操作实际上并没有修改类级数据


我错过了什么?我是否试图错误地修改类级别变量或其他什么

关于如何执行此操作,您基本上是对的,但是,列表切片会创建一个副本,因此行:

self.data = self.data[1:]  
创建数据的副本,然后将其指定给新的实例属性

所有这些都是这样工作的:当为实例查找属性时,python首先在实例的dict中查找属性。如果它不在实例的dict中,python接下来将按照方法解析顺序查找类的dict,然后查找父类的dict。因此,当您第一次开始访问self.data以附加到它时,在实例的dict中找不到数据,因此python使用的是类的dict中的数据。但是,当您显式地分配给self.dict时,您突然将一个条目添加到实例的_dict中,从那时起将使用该条目

有两种变通方法:

Database.data = self.data[1:]
可以很好地工作,或者您可以使用slice assignment修改self.data列表:

self.data[:] = self.data[1:]
self.data.pop(0)
或修改列表的任何其他方法:

self.data[:] = self.data[1:]
self.data.pop(0)
最后一个选项(可能是最明确的选项)是更改连接到classmethod:

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

database_instance = Database()
database_instance.connect()

Database.connect()
在评论中,提到了使用一个模块来完成这类事情。一个模块也可以非常简单地在整个程序中传输状态,它们最终的作用非常类似于一个单例——事实上,它们通常被推荐用于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

诸如此类。

关于如何进行此操作,您基本上是对的,但是,列表切片会创建一个副本,因此行:

self.data = self.data[1:]  
创建数据的副本,然后将其指定给新的实例属性

所有这些都是这样工作的:当为实例查找属性时,python首先在实例的dict中查找属性。如果它不在实例的dict中,python接下来将按照方法解析顺序查找类的dict,然后查找父类的dict。因此,当您第一次开始访问self.data以附加到它时,在实例的dict中找不到数据,因此python使用的是类的dict中的数据。但是,当您显式地分配给self.dict时,您突然将一个条目添加到实例的_dict中,从那时起将使用该条目

有两种变通方法:

Database.data = self.data[1:]
可以很好地工作,或者您可以使用slice assignment修改self.data列表:

self.data[:] = self.data[1:]
self.data.pop(0)
或修改列表的任何其他方法:

self.data[:] = self.data[1:]
self.data.pop(0)
最后一个选项(可能是最明确的选项)是更改连接到classmethod:

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

database_instance = Database()
database_instance.connect()

Database.connect()
在评论中,提到了使用一个模块来完成这类事情。一个模块也可以非常简单地在整个程序中传输状态,它们最终的作用非常类似于一个单例——事实上,它们通常被推荐用于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

等等等等。

Python不是Java。。。这通常不是一个好主意去尝试
在Python中复制Java模式,通常根本不需要它们。但是跳过第一行而不将其附加到原始列表中也会更有效,而不是读取其中的所有内容,然后制作一个新的副本来删除第一行。。。在Python中尝试和复制Java模式通常不是一个好主意,它们通常根本不需要。但是如果跳过第一行,而不将其附加到原始列表中,也会更有效,与其读入所有内容,还不如制作一个新的副本来删除第一行。mgilson很好地解释为什么会发生这种情况,但我认为您需要使用一个数据库拥有的全局变量module@XavierCombelle:你为什么这么想?类变量在这里很好,只是他缺少一些基本概念。Gilson似乎找到了答案。我只是想插嘴说,我发现使用Database.data而不是self.data,依我看,是一种更清晰的方式来引用什么是类属性;在子类中,classmethod中的cls始终是typeself,而Database.data硬编码到特定类的路径。在类方法中使用cls时,子类将获得自己的数据副本。@Abhijit因为模块级的东西是强大的概念,在javamgilson中没有等价物。请解释为什么会发生这种情况,但我认为您希望使用数据库拥有的全局变量module@XavierCombelle:你为什么这么想?类变量在这里很好,只是他缺少一些基本概念。Gilson似乎找到了答案。我只是想插嘴说,我发现使用Database.data而不是self.data,依我看,是一种更清晰的方式来引用什么是类属性;在子类中,classmethod中的cls始终是typeself,而Database.data硬编码到特定类的路径。在类方法中使用cls时,子类将获得自己的数据副本。@Abhijit因为模块级的东西是强大的概念,在java中没有等价物