Подтвердить что ты не робот

Subclassing dict: должен ли вызывать.__ init __()?

Вот двоякий вопрос с теоретической частью и практический:

При подклассификации dict:

class ImageDB(dict):
    def __init__(self, directory):
        dict.__init__(self)  # Necessary?? 
        ...

следует dict.__init__(self) вызывать, как меру "безопасности" (например, если есть некоторые нетривиальные детали реализации, которые имеют значение)? существует ли риск того, что код сломается с будущей версией Python, если dict.__init__() не вызван? Я ищу фундаментальную причину делать то или другое, здесь (практически, вызов dict.__init__() безопасен).

Я предполагаю, что при вызове ImageDB.__init__(self, directory) self уже является новым пустым объектом dict и поэтому нет необходимости вызывать dict.__init__ (сначала я хочу, чтобы dict был пустым). Правильно ли это?

Edit

Более практический вопрос, лежащий в основе фундаментального вопроса выше, заключается в следующем. Я думал о подклассе dict, потому что я часто использовал синтаксис db [...] (вместо того, чтобы делать db.contents [...] все время); объект только данные (атрибут) действительно действительно является dict. Я хочу добавить несколько методов в базу данных (например, get_image_by_name() или get_image_by_code()), и только переопределить __init__(), потому что база данных изображений определяется каталогом, которая ее содержит.

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

"Заводы" упоминались во многих ответах. Поэтому я предполагаю, что все это сводится к: вы подклассифицируете dict, переопределяете __init__() и добавляете методы, или вы пишете функцию (factory), которая возвращает dict, к которой вы добавляете методы? Я склонен предпочесть первое решение, потому что функция factory возвращает объект, тип которого не указывает, что он имеет дополнительные семантики и методы, но что вы думаете?

Изменить 2:

Я собираюсь от всех ответить, что это не очень хорошая идея для подкласса dict, когда новый класс "не является словарем", и в частности, когда его метод __init__ не может принимать те же аргументы, что и dict __init__ (который имеет место в "практическом вопросе" выше). Другими словами, если я правильно понимаю, консенсус представляется следующим: когда вы подклассом, все методы (включая инициализацию) должны иметь одинаковую подпись, как методы базового класса. Это позволяет isinstance (subclass_instance, dict) гарантировать, что subclass_instance.__init__() может использоваться, например, как dict.__init__().

Затем появляется другой практический вопрос: как должен реализоваться класс, похожий на dict, за исключением его метода инициализации? без подкласса? для этого потребуется какой-то докучливый код шаблона, не?

4b9b3361

Ответ 1

Вероятно, вы должны называть dict.__init__(self) при подклассификации; на самом деле, вы не знаете, что происходит именно в dict (поскольку оно встроено), и это может варьироваться в разных версиях и реализациях. Не вызывать это может привести к неправильному поведению, поскольку вы не можете знать, где dict держит свои внутренние структуры данных.

Кстати, вы не сказали нам, что хотите делать; если вам нужен класс с поведением dict (mapping), и вам действительно не нужен диктофон (например, нет кода, делающего isinstance(x, dict) в любом месте вашего программного обеспечения, как и должно быть), вам, вероятно, лучше использовать UserDict.UserDict или UserDict.DictMixin, если вы находитесь на python <= 2.5 или collections.MutableMapping, если вы находитесь на python >= 2.6. Это обеспечит вашему классу отличное поведение диктата.

EDIT: я прочитал в другом комментарии, что вы не переопределяете какой-либо метод dict! Тогда нет никакого смысла в подклассе вообще, не делайте этого.

def createImageDb(directory):
    d = {}
    # do something to fill in the dict
    return d

EDIT 2: вы хотите наследовать от dict, чтобы добавлять новые методы, но вам не нужно переопределять какие-либо. Чем может быть хороший выбор:

class MyContainer(dict):
    def newmethod1(self, args):
        pass

    def newmethod2(self, args2):
        pass


def createImageDb(directory):
    d = MyContainer()
    # fill the container
    return d
Кстати, какие методы вы добавляете? Вы уверены, что создали хорошую абстракцию? Возможно, вам лучше использовать класс, который определяет методы, которые вам нужны, и использовать внутри него "нормальный" файл.

Factory func: http://en.wikipedia.org/wiki/Factory_method_pattern

Это просто способ делегирования конструкции экземпляра функции вместо переопределения/изменения ее конструкторов.

Ответ 2

Обычно вы должны вызвать базовый класс '__init__, поэтому зачем делать исключение здесь?

Либо не переопределяйте __init__, либо если вам нужно переопределить __init__ базовый класс вызова __init__, если вы беспокоитесь о аргументах, просто передайте * args, ** kwargs или ничего, если вы хотите, чтобы пустой dict eg

class MyDict(dict):
    def __init__(self, *args, **kwargs ):
        myparam = kwargs.pop('myparam', '')
        dict.__init__(self, *args, **kwargs )

Мы не должны предполагать, что baseclass делает или не делает, неправильно не вызывать базовый класс __init__

Ответ 3

Остерегайтесь травления при подклассификации dict; это, например, требует __getnewargs__ в 2.7, и, возможно, __getstate__ __setstate__ в более старых версиях. (Я не знаю, почему.)

class Dotdict( dict ):
    """ d.key == d["key"] """

    def __init__(self, *args, **kwargs):
        dict.__init__( self, *args, **kwargs )
        self.__dict__ = self

    def __getnewargs__(self):  # for cPickle.dump( d, file, protocol=-1)
        return tuple(self)

Ответ 4

PEP 372 имеет дело с добавлением упорядоченного dict в модуль коллекций.

Он предупреждает, что "подклассификация dict - это нетривиальная задача, и многие реализации не переопределяют все методы, которые могут привести к неожиданным результатам".

Предлагаемый (и принятый) patch для python3.1 использует __init__, который выглядит следующим образом:

+class OrderedDict(dict, MutableMapping):
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+        if not hasattr(self, '_keys'):
+            self._keys = []
+        self.update(*args, **kwds)

Исходя из этого, похоже, что dict.__init__() не нужно вызывать.

Изменить: Если вы не переопределяете или не расширяете какие-либо из методов dict, то я согласен с Аланом Францони: используйте dict factory вместо подкласса:

def makeImageDB(*args,**kwargs):
   d = {}
   # modify d
   return d

Ответ 5

Если вы планируете UserDict подкласс для чего-то вроде базового типа dict вы также можете рассмотреть UserDict из коллекций. UserDict предназначен для использования в подклассах.