Подкласс дикт: должен диктовать. init () будет называться?


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

При наследовании дикт:

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.содержание [...] все время); только данные объекта (атрибут) - это действительно диктат. Я хочу добавить несколько методов в базу данных (например, get_image_by_name() или get_image_by_code()) и только переопределить __init__(), потому что база данных изображений определяется каталогом, который ее содержит.

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

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

Править 2:

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

Тогда возникает еще один практический вопрос: как должен быть реализован класс, который точно такой же, как dict, за исключением его метода инициализации? без подклассов? это потребует некоторого надоедливого шаблонного кода, не так ли?
4   29   2010-01-09 14:16:00

4 ответа:

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

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

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

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

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

Заводская функция: http://en.wikipedia.org/wiki/Factory_method_pattern

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

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

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

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

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

Остерегайтесь маринования при создании подклассов 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)

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

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

Предложенный (и принятый) патч для 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__() не нужно вызывать.

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

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