Различие между двумя типами определения свойств класса?


Есть ли разница между

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

И

class Foo(object):
    def __init__(self):
       ... etc.

Foo.bar = 1

В обоих случаях bar является свойством класса, и оно одинаково для всех экземпляров класса, верно?

3 4

3 ответа:

Я бы сказал, что единственная разница заключается в том, что во втором случае Foo.bar не существует до тех пор, пока оператор Foo.bar = 1 не будет выполнен, в то время как в первом случае он уже доступен при создании объекта класса.

Это, вероятно, небольшая разница без какого-либо эффекта в вашем коде (если только нет кода, который требует Foo.bar, прежде чем он будет доступен во втором случае). Тем не менее, я бы сказал, что первый вариант лучше с точки зрения читаемости, так как вам не нужно прокручивать вниз, чтобы узнать атрибуты для вашего класса, они уже есть.

Если вы пишете свой собственный код, используйте:

class Foo(object):
    bar = 1

Потому что эта версия:

class Foo(object):
    pass

Foo.bar = 1

Даже если это законно

  1. больше похоже на взлом.
  2. в большинстве случаев менее читаем.
  3. У вас могут возникнуть проблемы, если вы попытаетесь получить доступ к атрибуту bar до его создания:

    >>> class Foo:
    ...     pass
    ... 
    >>> f = Foo()
    >>> f.bar
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Foo' object has no attribute 'bar'
    >>> Foo.bar = 1
    >>> f.bar   # but hey, now you're ok!
    1
    

Кроме этого, я не вижу особых различий.

Обе версии одинаковы (см. расшифровку тестов в конце этого ответа), но обратите внимание, что (а) ни в том, ни в другом случае не является bar одинаковым для всех экземпляров Foo.

В момент создания экземпляра чтение bar на экземпляре будет считывать значение в Foo, а присвоения Foo.bar будут отражаться в экземпляре до тех пор, пока instance.bar не будет присвоено.В этот момент присваивания экземпляр получает свою собственную запись __dict__, которая полностью независима от класса.

In [62]: class Foo: pass

In [63]: Foo.bar = 1

In [64]: Foo.bar
Out[64]: 1

In [65]: f = Foo()

In [66]: f.bar
Out[66]: 1

In [67]: f.bar +=1

In [68]: f.bar
Out[68]: 2

In [69]: Foo.bar
Out[69]: 1

In [70]: Foo.bar +=3

In [71]: Foo.bar
Out[71]: 4

In [72]: g = Foo()

In [73]: g.bar
Out[73]: 4

In [74]: class Qux: bar = 1

In [75]: Qux.bar
Out[75]: 1

In [76]: q = Qux()

In [77]: q.bar
Out[77]: 1

In [78]: q.bar+=1

In [79]: q.bar
Out[79]: 2

In [80]: Qux.bar
Out[80]: 1

In [81]: Qux.bar +=1

In [82]: r = Qux()

In [83]: r.bar
Out[83]: 2

In [84]: q.bar
Out[84]: 2

In [85]: s = Qux()

In [87]: s.__dict__
Out[87]: {}

In [88]: q.__dict__
Out[88]: {'bar': 2}

In [89]: Qux.bar = 'foo'

In [90]: Qux.bar
Out[90]: 'foo'

In [91]: s.bar
Out[91]: 'foo'