Как я должен объявить значения по умолчанию, например, переменные в Python?

Если я даю свои членские значения по умолчанию класса как это:

class Foo:
    num = 1

или как это?

class Foo:
    def __init__(self):
        self.num = 1

В этом вопросе я обнаружил это в обоих случаях,

bar = Foo()
bar.num += 1

четко определенная операция.

Я понимаю, что первый метод даст мне переменную класса, в то время как второй не будет. Однако, если я не требую переменной класса, но только должен установить значение по умолчанию для моих переменных экземпляра, действительно ли оба метода одинаково хороши? Или один из них больше 'pythonic', чем другой?

Одна вещь, которую я заметил, состоит в том, что в учебном руководстве Django, они используют второй метод для объявления Моделей. Лично я думаю, что второй метод более изящен, но я хотел бы знать, каков 'стандартный' путь.

79
задан Community 23 May 2017 в 11:46
поделиться

4 ответа

Расширяя ответ bp, я хотел показать вам, что он имел в виду, говоря о неизменяемых типах.

Во-первых, это нормально:

>>> class TestB():
...     def __init__(self, attr=1):
...         self.attr = attr
...     
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1

Однако это работает только для неизменяемых (неизменяемых) типов. Если значение по умолчанию было изменяемым (то есть его можно было заменить), то вместо этого произошло бы следующее:

>>> class Test():
...     def __init__(self, attr=[]):
...         self.attr = attr
...     
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>> 

Обратите внимание, что оба a и b имеют общий атрибут. Часто это нежелательно.

Это питонический способ определения значений по умолчанию для переменных экземпляра, когда тип изменяемый:

>>> class TestC():
...     def __init__(self, attr=None):
...         if attr is None:
...             attr = []
...         self.attr = attr
...     
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]

Причина, по которой мой первый фрагмент кода работает, заключается в том, что с неизменяемыми типами Python создает новый экземпляр, когда вы хотите один. Если вам нужно было добавить 1 к 1, Python создаст для вас новые 2, потому что старую 1 изменить нельзя. Думаю, причина в основном в хешировании.

115
ответ дан 24 November 2019 в 10:11
поделиться

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

>>> class c:
...     l = []
... 
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]
3
ответ дан 24 November 2019 в 10:11
поделиться

Эти два фрагмента делают разные вещи, поэтому дело не в вкусе, а в том, как правильно вести себя в вашем контексте. Документация Python объясняет разницу, но вот несколько примеров:

Exhibit A

class Foo:
  def __init__(self):
    self.num = 1

Это связывает num с экземплярами Foo . Изменение этого поля не распространяется на другие экземпляры.

Таким образом:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1

Exhibit B

class Bar:
  num = 1

Это связывает num с классом Bar . Изменения распространяются!

>>> bar1 = Bar()
>>> bar2 = Bar()
>>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
>>> bar2.num
1
>>> Bar.num = 3
>>> bar2.num
3
>>> bar1.num
2
>>> bar1.__class__.num
3

Фактический ответ

Если мне не нужна переменная класса, а нужно только установить значение по умолчанию для моих переменных экземпляра, оба метода одинаково хороши? Или один из них более «питонический», чем другой?

Код на выставке B явно неверен для этого: зачем вам связывать атрибут класса (значение по умолчанию при создании экземпляра) с единственным экземпляром?

Код на выставке А в порядке.

Если вы хотите указать значения по умолчанию для переменных экземпляра в своем конструкторе, я бы сделал следующее:

class Foo:
  def __init__(num = None):
    self.num = num if num is not None else 1

... или даже:

class Foo:
  DEFAULT_NUM = 1
  def __init__(num = None):
    self.num = num if num is not None else DEFAULT_NUM

... или даже: (желательно, но если и только если вы имеете дело с неизменяемыми типами!)

class Foo:
  def __init__(num = 1):
    self.num = num

Таким образом вы можете сделать:

foo1 = Foo(4)
foo2 = Foo() #use default
50
ответ дан 24 November 2019 в 10:11
поделиться

Использование членов класса для задания значений по умолчанию работает очень хорошо, если только вы будете осторожны и будете делать это только с неизменяемыми значениями. Если вы попытаетесь сделать это со списком или изречением, это будет довольно смертельно. Он также работает, когда атрибут экземпляра является ссылкой на класс, если значение по умолчанию - None.

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

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

4
ответ дан 24 November 2019 в 10:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: