Есть ли причина не отправлять super () .__ init __ () словарь вместо ** kwds?

Я только начал писать текстовую игру вчера как упражнение по изучению Python (я использую 3.3). Я говорю «текстовая игра», но я имею в виду больше MUD, чем свое собственное приключение. Во всяком случае, я был очень взволнован, когда понял, как обрабатывать наследование и множественное наследование, используя super() вчера, но я обнаружил, что аргумент-передача действительно загромождала код и требовала жонглирования большим количеством небольших переменных. Кроме того, создание файлов сохранения казалось довольно кошмарным.

Итак, я подумал: «Что, если некоторые иерархии классов просто приняли один аргумент, словарь и просто вернули словарь?» Чтобы привести пример, вот два класса, подрезанные их методами init:

 class Actor: def __init__(self, in_dict,**kwds): super().__init__(**kwds) self._everything = in_dict self._name = in_dict["name"] self._size = in_dict["size"] self._location = in_dict["location"] self._triggers = in_dict["triggers"] self._effects = in_dict["effects"] self._goals = in_dict["goals"] self._action_list = in_dict["action list"] self._last_action = '' self._current_action = '' # both ._last_action and ._current_action get updated by .update_action() class Item(Actor): def __init__(self,in_dict,**kwds) super().__init__(in_dict,**kwds) self._can_contain = in_dict("can contain") #boolean entry self._inventory = in_dict("can contain") #either a list or dict entry class Player(Actor): def __init__(self, in_dict,**kwds): super().__init__(in_dict,**kwds) self._inventory = in_dict["inventory"] #entry should be a Container object self._stats = in_dict["stats"] 

Пример dict, который будет передан:

 playerdict = {'name' : '', 'size' : '0', 'location' : '', 'triggers' : None, 'effects' : None, 'goals' : None, 'action list' = None, 'inventory' : Container(), 'stats' : None,} 

(После того, как словарь был передан, заменить None на {} .)

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

  1. Это делает мой код намного опрятным и более управляемым.
  2. Пока у dicts есть хотя бы одна запись для вызываемого ключа, он не нарушает код. Кроме того, не имеет значения, если данный аргумент никогда не используется.
  3. Кажется, что файл IO просто стал намного проще (словари данных игрока, хранящиеся в виде dicts, словари данных элемента, хранящиеся как dicts и т. Д.),

Я получаю точку **kwds (EDIT: по-видимому, я этого не делал), и это не показалось громоздким при передаче меньших аргументов. Это просто удобный способ справиться с необходимостью большого количества атрибутов при создании каждого экземпляра.

Тем не менее, я все еще большой питон noob. Итак, мой вопрос заключается в следующем: есть ли основная причина, по которой повторение одного и того же dict неоднократно через super () в базовый класс было бы более сложной идеей, чем просто вытягивание ее с неприятными (большими и загроможденными) ** kwds pass? (например, проблемы с интерпретатором, которые кто-то на моем уровне не знал бы.)

РЕДАКТИРОВАТЬ:

Раньше создание нового проигрывателя могло бы выглядеть так: аргумент передавался для каждого атрибута.

 bob = Player('bob', Location = 'here', ... etc.) 

Количество необходимых аргументов взорвалось, и я включил только атрибуты, которые действительно должны были присутствовать, чтобы не прерывать вызовы методов из объекта Engine.

Это впечатление, которое я получаю от ответов и комментариев до сих пор:

Нет ничего «неправильного» при отправке одного и того же словаря, поскольку ничто не имеет возможности изменять его содержимое (Kirk Strauser), и словарь всегда имеет то, что он должен иметь (goncalopp). Настоящий ответ заключается в том, что вопрос был неправильным, а использование in_dict вместо **kwds является избыточным.

Правильно ли это? (Кроме того, спасибо за отличную и разнообразную обратную связь!)

3 Solutions collect form web for “Есть ли причина не отправлять super () .__ init __ () словарь вместо ** kwds?”

Я не уверен, что я точно понимаю ваш вопрос, потому что я не вижу, как выглядел код, прежде чем вы внесли изменение в use_dict. Похоже, вы отправляли десятки ключевых слов в призыв к супер (что, по понятным причинам, не то, что вы хотите), но это не обязательно. Если ваш дочерний класс имеет dict со всей этой информацией, он может быть превращен в kwargs когда вы делаете вызов с **in_dict . Так:

 class Actor: def __init__(self, **kwds): class Item(Actor): def __init__(self, **kwds) self._everything = kwds super().__init__(**kwds) 

Я не вижу причины добавить еще один dict для этого, так как вы можете просто манипулировать и передавать dict, созданный для kwds любом случае

Редактировать:

Что касается вопроса об эффективности использования ** расширения dict и исключения аргументов в явном виде, я сделал очень ненаучный тест времени с этим кодом:

 import time def some_func(**kwargs): for k,v in kwargs.items(): pass def main(): name = 'felix' location = 'here' user_type = 'player' kwds = {'name': name, 'location': location, 'user_type': user_type} start = time.time() for i in range(10000000): some_func(**kwds) end = time.time() print 'Time using expansion:\t{0}s'.format(start - end) start = time.time() for i in range(10000000): some_func(name=name, location=location, user_type=user_type) end = time.time() print 'Time without expansion:\t{0}s'.format(start - end) if __name__ == '__main__': main() 

Выполнение этого 10 000 000 раз дает небольшое (и, вероятно, статистически бессмысленное) преимущество, проходящее вокруг dict и использующее **.

 Time using expansion: -7.9877269268s Time without expansion: -8.06108212471s 

Если мы напечатаем идентификаторы объектов dict (kwds outside и kwargs внутри функции), вы увидите, что python создает новый dict для функции, которая будет использоваться в любом случае, но на самом деле функция получает только один dict навсегда. После первоначального определения функции (где создается kwargs dict) все последующие вызовы просто обновляют значения этого dict, принадлежащие функции, независимо от того, как вы это называете. (См. Также этот просветительский вопрос SO о том, как изменяемые параметры по умолчанию обрабатываются в python, что несколько связано)

Таким образом, с точки зрения производительности, вы можете выбрать то, что имеет смысл для вас. Он не должен оказывать значимого влияния на то, как работает python за кулисами.

Я сделал это сам, где in_dict был dict с большим количеством ключей, или объектом настроек, или каким-то другим «блобом» чего-то с множеством интересных атрибутов. Это нормально, если это делает ваш код более чистым, особенно если вы config_dict его явно как settings_object или config_dict или аналогичным.

Однако это не должно быть обычным делом. Обычно лучше явно передавать небольшой набор отдельных переменных. Это делает код намного чище и проще рассуждать. Возможно, что клиент может in_dict = None пройти in_dict = None , и вы не узнаете, пока какой-либо метод не попытается получить к нему доступ. Предположим, что Actor.__init__ не отслаивался in_dict но просто сохранил его как self.settings = in_dict . Спустя некоторое время Actor.method приходит и пытается получить к нему доступ, а затем бум! Мертвый процесс. Если вы вызываете Actor.__init__(var1, var2, ...) , то вызывающий пользователь будет вызывать исключение намного раньше и предоставить вам больше контекста о том, что на самом деле пошло не так.

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

Это не специфичный для python, но самая большая проблема, которую я вижу с такими аргументами, как это, заключается в том, что он разрушает инкапсуляцию . Любой класс может изменять аргументы, и гораздо сложнее сказать, какие аргументы ожидаются в каждом классе, что затрудняет понимание кода и сложнее отлаживать.

Подумайте о явном потреблении аргументов в каждом классе и вызове __init__ супер в остальных. Вам не нужно делать их явными:

 class ClassA( object ): def __init__(self, arg1, arg2=""): pass class ClassB( ClassA ): def __init__(self, arg3, arg4="", *args, **kwargs): ClassA.__init__(self, *args, **kwargs) ClassB(3,4,1,2) 

Вы также можете оставить переменные неинициализированными и использовать методы для их установки. Затем вы можете использовать разные методы в разных классах, и все подклассы будут иметь доступ к методам суперкласса.

  • Создайте пользователя из командной строки в Django
  • PEP8 - импортировать не поверх файла с sys.path
  • метод pymongo для получения статистики для использования байта коллекции?
  • python asyncio add_done_callback с async def
  • Как удалить объекты HTML в строке в Python 3.1?
  • альтернативы или ускорения для инверсии матрицы mpmath
  • Удаление значения в соседнем столбце
  • Объекты просмотра словаря Python 3.x и matplotlib
  • Python - лучший язык программирования в мире.