Рассмотрим следующий класс:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
Мои коллеги имеют тенденцию определять это так:
class Person:
name = None
age = None
def __init__(self, name, age):
self.name = name
self.age = age
Основная причина этого заключается в том, что выбранный им редактор показывает свойства для автозаполнения.
Лично мне не нравится последний, потому что нет никакого смысла в том, что класс имеет эти свойства None
.
Какой из них будет лучше практиковать и по каким причинам?
__init__
уже имеющемся автозаполнении и т. Д. Кроме того, использованиеNone
предотвращает IDE выводить лучший тип для атрибута, поэтому вместо этого лучше использовать разумное значение по умолчанию (когда возможно).typing
модуль, который позволяет вам предоставлять подсказки для IDE и линтера, если такие вещи щекотят ваше воображение ...self
. Даже еслиself.name
иself.age
не были назначены в__init__
они не будут отображаться в случаеself
, они появляются только в классеPerson
.Ответы:
Я называю последнюю плохую практику правилом «это не делает то, что вы думаете».
Положение вашего коллеги может быть переписано следующим образом: «Я собираюсь создать группу статических квазиглобальных переменных класса, к которым никогда не обращаются, но которые занимают место в таблицах пространства имен различных классов (
__dict__
), просто чтобы заставить мою IDE делать что-то."источник
-OO
, для тех немногих, кто в этом нуждается.1. Сделайте ваш код легким для понимания
Код читается гораздо чаще, чем написан. Упростите задачу разработчика кода (это может быть и вами в следующем году).
Я не знаю ни о каких жестких правилах, но я предпочитаю, чтобы любое будущее состояние инстанции было четко объявлено. Сбой с
AttributeError
достаточно плохо. Непонятно, как выглядит жизненный цикл атрибута экземпляра. Количество умственной гимнастики, необходимое для восстановления возможных последовательностей вызовов, которые приводят к назначению атрибута, может легко стать нетривиальным, что приведет к ошибкам.Поэтому я обычно не только определяю все в конструкторе, но и стараюсь свести к минимуму количество изменяемых атрибутов.
2. Не смешивайте членов класса и уровня экземпляра
Все, что вы определяете прямо внутри
class
объявления, принадлежит классу и является общим для всех экземпляров класса. Например, когда вы определяете функцию внутри класса, она становится методом, одинаковым для всех экземпляров. То же относится и к членам данных. Это совершенно не похоже на атрибуты экземпляра, в которых вы обычно определяете__init__
.Элементы данных уровня класса наиболее полезны в качестве констант:
источник
self
и не нужноself
быть переданы, в то время как методы класса уровня являются несвязанными и простые функции , как видно наdef
время, и принимает экземпляр в качестве первого аргумента. Так что это разные вещи.AttributeError
хороший сигнал, чем ошибка. В противном случае вы бы проглотили None и получили бы бессмысленные результаты. Это особенно важно в данном случае, когда атрибуты определены в__init__
, поэтому отсутствующий атрибут (но существующий на уровне класса) может быть вызван только ошибочным наследованием.None
а это значение не имеет смысла в момент создания экземпляра, у вас есть проблема в вашей архитектуре, и вам нужно переосмыслить жизненный цикл значения атрибута или его начального значения. Обратите внимание, что, определяя свои атрибуты заранее, вы можете обнаружить такую проблему еще до того, как напишите остальную часть класса, не говоря уже о запуске кода.Лично я определяю членов в методе __ init __ (). Я никогда не думал об их определении в классе. Но то, что я всегда делаю: я инициирую все члены в методе __init__, даже те, которые не нужны в методе __ init__.
Пример:
Я думаю, что важно определить всех участников в одном месте. Это делает код более читабельным. Находится ли он внутри __ init __ () или снаружи, это не так важно. Но для команды важно придерживаться более или менее одинакового стиля кодирования.
О, и вы можете заметить, что я когда-либо добавляю префикс "_" к переменным-членам.
источник
_
: указывать, что это личное! (Почему так много людей в этой теме путают или наполовину путают Python с другими языками?)Это плохая практика. Вам не нужны эти значения, они загромождают код и могут привести к ошибкам.
Рассмотреть возможность:
источник
Я пойду с «немного похожими на строки документации» и объявлю это безвредным, если оно всегда
None
или узкий диапазон других значений неизменны.Он пахнет атавизмом и чрезмерной привязанностью к статически типизированным языкам. И это не так хорошо, как код. Но это имеет незначительную цель, которая остается в документации.
Он документирует ожидаемые имена, поэтому, если я объединю код с кем-то, и у одного из нас будет «username», а у другого user_name, есть подсказка людям, что мы расстались и не используем одни и те же переменные.
Принудительная полная инициализация в качестве политики позволяет добиться того же самого в более питонском ключе, но если в нем есть реальный код
__init__
, это обеспечивает более ясное место для документирования используемых переменных.Очевидно, что БОЛЬШАЯ проблема здесь в том, что это побуждает людей инициализироваться со значениями, отличными от
None
, что может быть плохо:оставляет глобальный след и не создает экземпляр для x.
Но это больше причуды в Python в целом, чем в этой практике, и мы уже должны быть параноиками по этому поводу.
источник