Почему мой класс ведет себя как статический класс? - PullRequest
0 голосов
/ 27 января 2009

У меня есть модуль (на самом деле один файл .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 ]

10 голосов
/ 27 января 2009

В классе есть два вида переменных:

  • переменные класса, определенные на уровне класса и общие для всех экземпляров

  • переменные экземпляра, определенные в методе класса (обычно __init__) и уточненные экземпляром (обычно self.).

Пример

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

Переменная с именем classVariable является частью класса, общей для всех экземпляров. Из-за того, как Python выполняет поиск, он доступен как член self.classVariable, а также SomeClass.classVariable.

Переменная с именем instanceVariable является частью экземпляра (self.) и уникальна для каждого экземпляра.

Примечание. Есть третий тип, глобальный, но это не то, о чем вы спрашиваете.

6 голосов
/ 27 января 2009

Вы говорите о fileList? Вы имеете это как переменную класса, чтобы сделать ее переменной экземпляра, которую вам нужно сделать:

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

в вас __ init __ функция.

2 голосов
/ 27 января 2009

Вещи, объявленные в блоке 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, поскольку в __init__ по умолчанию используется только одно значение list_b; новый список создается не каждый раз, когда вызывается функция, но передача вашего нового нового списка работает.

1 голос
/ 27 января 2009

Как уже отмечали другие, ваша проблема в том, что 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.
1 голос
/ 27 января 2009

Если вы объявите свои переменные вне метода класса, внутри тела класса они станут «переменными класса» и станут общими для всех экземпляров класса. Чтобы получить переменные экземпляра, объявите их внутри функции init и привяжите их к 'self', обработчику текущего экземпляра.

0 голосов
/ 27 января 2009

Это может быть полезно, если вы можете опубликовать полный рабочий (или неудачный!) Пример.

Если я делаю то, что считаю необходимым (то есть, оборачиваем это в класс HashedDir (объект): и устанавливаем self.fileList = {'files': [], 'dirs': []} внутри init тогда, похоже, работает.

Какие предметы вы называете self.value? Как и в предыдущем посте sykora, вам нужно различать код, который запускается для каждого экземпляра (в init ), и код, общий для всех экземпляров.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...