为什么我的班级表现得像静态班?

时间:2009-01-27 11:36:22

标签: python

我有一个模块(实际上是单个.py文件),有一个名为HashedDir的类。

当我导入文件并实例化该类的2个实例时,当我检查对象的字段时,它们总是相同的,即使两个对象应该不同。

例如:

 h1 = HashedDir('/path/to/dir')
 print h1.getList()['files'] # /path/to/dir
 h2 = HashedDir('some/other/path')
 print h1.getList()['files'] # some/other/path
 print h2.getList()['files'] # some/other/path

有什么想法吗?

这是班级:

from os  import walk
from os import path
from hashlib import md5
import re

class HashedDir:
    """
    A list of files with associated md5 hashes generated retrieving thou
    a recursive walk in the directory tree starting from a provided root
    directory. Also stores the dirs in each dir
    """

    #  {'files': [
    #    ('/path/to/file1', '52bc309e11259af15e4623c7a0abc28c'),
    #    ('/path/to/file2', '52bc309e11259af15e4623c7a0abc28c'),
    #    ('/path/to/dir/file3', '52bc309e11259af15e4623c7a0abc28c')
    #   ],
    #   'dirs': ['/path/to/dir1', '/path/to/dir2']
    #  }
    fileList = {'files': [], 'dirs': []}
    ignoreList = []

    def __init__(self, rootDir, ignoreList=[]):
        """
        ignoreList is a list of regular expressions. If a file or a dir matches
        that regular expression, don't count it
        """
        self.ignoreList = ignoreList

        for dirpath, dirnames, filenames in walk(rootDir):
            for fileName in filenames:
                completeName = path.join(dirpath,fileName)
                hash = md5(open(completeName).read()).hexdigest()
                relativePath = self._relativePath(completeName, rootDir)
                if not self._toBeIgnored(relativePath):
                    self.fileList['files'].append((relativePath, hash))
            for dirName in dirnames:
                completeName = path.join(dirpath, dirName)
                relativePath = self._relativePath(completeName, rootDir)
                if not self._toBeIgnored(relativePath):
                    self.fileList['dirs'].append(relativePath)

    def _relativePath(self, path, base):
        return path.replace(base, '')

    def _toBeIgnored(self, path):
        for regex in self.ignoreList:
            if re.compile(regex).search(path) != None:
                return True
        return False

    def getList(self):
        return self.fileList

提前致谢

6 个答案:

答案 0 :(得分:10)

一个类中有两种变量:

  • 类变量,在类级别定义,并且对所有实例都是通用的

  • 实例变量,在类方法中定义(通常为__init__)并由实例限定(通常为self.)。

实施例

class SomeClass( object ):
    classVariable = 0
    def __init__( self ):
        self.instanceVariable= 0

名为classVariable的变量是类的一部分,对所有实例都是通用的。由于Python的搜索方式,它可以作为self.classVariable的成员以及SomeClass.classVariable使用。

名为instanceVariable的变量是实例(self.)的一部分,并且对每个实例都是唯一的。

请注意。还有第三种,全球性的,但这不是你要问的。

答案 1 :(得分:6)

你在谈论文件列表吗?你将它作为一个类变量,使它成为你需要做的实例变量:

self.fileList = {'files': [], 'dirs': []}

你__ init __ function。

答案 2 :(得分:2)

class块中声明的内容是类属性,类属性也可以通过实例访问。 (事实上​​,这个原则是方法的约束方式。)不仅如此,函数的默认参数仅在定义函数时进行求值。那么,举一个例子说明这两点:

class C(object):
    list_a = []
    def __init__(self, list_b=[]):
        self.list_b = list_b

    def __str__(self):
        return '%r %r' % (self.list_a, self.list_b)

c1 = C()
c2 = C()
c2.list_a = []
c3 = C([])

c1.list_a.append(1)
c1.list_b.append(2)
print c1
print c2
print c3

这个输出是:

[1] [2]
[] [2]
[1] []

c1和c3共享相同的list_a,因为它是一个类属性;它没有被像c2这样的实例属性所遮蔽。 c1和c2共享相同的list_b,因为list_b中只有一个__init__默认值;每次调用函数时都不会创建新列表,但传入您自己的新列表会起作用。

答案 3 :(得分:1)

如果在类方法之外的类方法中声明变量,它们将成为“类变量”并且对所有类实例都是通用的。要获取实例变量,请在 init 函数中声明它们,并将它们绑定到'self',即当前实例的处理程序。

答案 4 :(得分:1)

正如其他人所指出的那样,你的问题是fileList是一个你正在变异的类变量。

然而,值得注意的是您的代码中可能导致类似问题的另一个潜在缺陷(尽管在您的具体示例中没有):

def __init__(self, rootDir, ignoreList=[]):

请注意将可变参数(例如此列表)作为默认参数传递。该列表仅创建一次(当您定义__init__函数时。这意味着使用默认值构造的类的所有实例将使用相同的列表。

在你的例子中,列表永远不会被修改,所以这不会产生任何影响,但是如果(就像你对fileList那样)你附加到self.ignoreList,那么这会影响所有这样的实例,导致类似的问题对你正在看的人。

这是一个非常普遍的初学者 - 要避免它,最好将这样的代码写成:

def __init__(self, rootDir, ignoreList=None):
    if ignoreList is None:
        ignoreList = []  # This will create a new empty list for every instance.

答案 5 :(得分:0)

如果您可以发布完整的工作(或失败!)示例,那么它可能会很有用。

如果我做了我认为必要的事情(即将它包装在Class HashedDir(object)中)并在 init <内设置self.fileList = {'files':[],'dirs':[]} / strong>那它似乎确实有效。

您指的是哪些项目为self.value?根据sykora的上一篇文章,您需要区分为每个实例运行的代码(在 init 中)和所有实例共有的代码。