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__属性を検索します。インスタンスの にない場合__dict__、python は次にクラスの を調べ、__dict__次に親クラスの を調べます__dict__(メソッドの解決順序に従います)。したがって、最初にアクセスを開始する(それに追加する) ときは、インスタンスのにself.data見つからないため、python はクラスの から使用しています。しかし、明示的に に割り当てると、その時点から使用されるインスタンスにエントリが突然追加されます。data__dict__data__dict__self.dict__dict__

いくつかの回避策があります。

Database.data = self.data[1:]

self.dataまたは、スライス割り当てを使用してリストを変更することもできます。

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

または、リストを適切に変更するその他の方法:

self.data.pop(0)

おそらく最も明白な最後のオプションは、次のように変更connectすることclassmethodです。

@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:]

メソッドへの最初の引数はインスタンスではなくクラスであるため、慣習を同様に慣習に変更しました(私も見ましたが)。このメソッドは、インスタンスまたはクラス自体から呼び出すことができます。selfclsklass

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 に答える