Разница между определением члена в __init__ и определением его в теле класса в python? - PullRequest
6 голосов
/ 11 октября 2009

В чем разница между

class a:
   def __init__(self):
       self.val=1

делать

class a:
   val=1
   def __init__(self):
       pass

Ответы [ 3 ]

10 голосов
/ 11 октября 2009
class a:
   def __init__(self):
       self.val=1

это создает класс (в Py2, грубый, устаревший, в старом стиле, не делайте этого! класс; в Py3, старые неприятные старые классы наконец исчезли, так что это будет класс одного-единственного вида - ** хороший *, который требует class a(object): в Py2), так что каждый экземпляр начинается со своей собственной ссылки на целочисленный объект 1.

class a:
   val=1
   def __init__(self):
       pass

это создает класс (того же типа), который сам имеет ссылку на целочисленный объект 1 (его экземпляры начинаются без ссылки на экземпляр).

Для неизменяемых величин, таких как int, трудно увидеть практическую разницу. Например, в любом случае, если позже вы выполните self.val = 2 для одного экземпляра a, это создаст ссылку instance (существующий ответ в этом отношении неверен).

Различие важно для изменяемых объектов, потому что у них есть методы-мутаторы, поэтому очень важно знать, является ли определенный список уникальным для каждого экземпляра или общим для всех экземпляров. Но для неизменных объектов, поскольку вы никогда не сможете изменить сам объект, а только назначить (например, self.val, который всегда будет ссылаться на экземпляр), это довольно незначительно.

Почти единственное существенное различие для неизменяемых: если вы позже назначите a.val = 3, в первом случае это повлияет на то, что каждый экземпляр воспринимает как self.val (за исключением экземпляров, которые имели свои self.val назначено или эквивалентные действия); во втором случае это не повлияет на то, что рассматривается как self.val каким-либо экземпляром (кроме случаев, для которых вы выполнили del self.val или эквивалентные действия).

6 голосов
/ 11 октября 2009

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

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

class Foo(object):
    def __init__(self):
        self.bar = expensivefunction()

myobjs = [Foo() for _ in range(1000000)]

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

Я также часто использую переменные класса при запоминании результатов. Пример:

class Foo(object):
    bazcache = {}

    @classmethod
    def baz(cls, key):
        try:
            result = cls.bazcache[key]
        except KeyError:
            result = expensivefunction(key)
            cls.bazcache[key] = result
        return result

В этом случае baz является методом класса; его результат не зависит от переменных экземпляра. Это означает, что мы можем хранить одну копию кэша результатов в переменной класса, так что 1) вы не сохраняете одни и те же результаты несколько раз, и 2) каждый экземпляр может извлечь выгоду из результатов, которые были кэшированы из других экземпляров.

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

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

5 голосов
/ 11 октября 2009

Как уже упоминалось другими, в одном случае это атрибут класса, в другом - атрибут экземпляра. Имеет ли это значение? Да, в одном случае это так. Как сказал Алекс, если значение изменчиво. Лучшее объяснение - это код, поэтому я добавлю немного кода, чтобы показать его (на самом деле это все, что делает этот ответ):

Сначала класс, определяющий два атрибута экземпляра.

>>> class A(object):
...     def __init__(self):
...         self.number = 45
...         self.letters = ['a', 'b', 'c']
... 

А затем класс, определяющий два атрибута класса.

>>> class B(object):
...     number = 45
...     letters = ['a', 'b', 'c']
... 

Теперь мы их используем:

>>> a1 = A()
>>> a2 = A()
>>> a2.number = 15
>>> a2.letters.append('z')

И все хорошо:

>>> a1.number
45
>>> a1.letters
['a', 'b', 'c']

Теперь используйте вариацию атрибута класса:

>>> b1 = B()
>>> b2 = B()
>>> b2.number = 15
>>> b2.letters.append('z')

И все ... хорошо ...

>>> b1.number
45
>>> b1.letters
['a', 'b', 'c', 'z']

Да, обратите внимание, что когда вы изменили, атрибут изменяемого класса изменился для всех классов. Обычно это не то, что вы хотите.

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

...