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


Я должен дать моим членам класса значения по умолчанию такой:

class Foo:
    num = 1

или такой?

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

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

bar = Foo()
bar.num += 1

- это четко определенные операции.

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

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

5 64

5 ответов:

расширяя ответ 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 имеют общий атрибут. Это часто нежелательно.

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

>>> 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 не может быть изменен. Причина в основном для хеширования, я считаю.

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

Выставка

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

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

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

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

выставки B

class Bar:
  num = 1

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

>>> 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

правильный ответ

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

код в экспонате 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

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

>>> class c:
...     l = []
... 
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]

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

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

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

вы также можете объявить переменные класса как None, которые будут препятствовать распространению. Это полезно, когда вам нужен хорошо определенный класс и вы хотите предотвратить AttributeErrors. Например:

>>> class TestClass(object):
...     t = None
... 
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>

также, если вам нужны значения по умолчанию:

>>> class TestClassDefaults(object):
...    t = None
...    def __init__(self, t=None):
...       self.t = t
... 
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>

конечно, все еще следуйте информации в других ответах об использовании изменяемых и неизменяемых типов по умолчанию в __init__.