Переменные экземпляра и переменные класса в Python - PullRequest
112 голосов
/ 26 апреля 2010

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

Переменные класса:

class MyController(Controller):

  path = "something/"
  children = [AController, BController]

  def action(self, request):
    pass

Переменные экземпляра:

class MyController(Controller):

  def __init__(self):
    self.path = "something/"
    self.children = [AController, BController]

  def action(self, request):
    pass

Ответы [ 4 ]

150 голосов
/ 26 апреля 2010

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

42 голосов
/ 26 апреля 2010

дальнейшее повторение советов Майка и Алекса и добавление моего собственного цвета ...

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

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

  1. локальные
  2. нелокальные
  3. глобальные
  4. встроенные

для доступа к атрибутам,Порядок:

  1. экземпляр
  2. класс
  3. базовых классов, как определено MRO (порядок разрешения методов)

в вашем примеревыше, скажем, вы ищете атрибут path.когда он встречает ссылку типа "self.path", Python сначала проверяет соответствие атрибутов экземпляра;когда это терпит неудачу, он проверяет класс, из которого был создан объект.наконец, он будет искать базовые классы.как сказал alex, если ваш атрибут найден в экземпляре, он не будет зависеть от класса, а значит, сэкономит немного времени.

однако, если вы настаиваете на атрибутах класса, вам придетсяОткажитесь от этой крошечной производительности, или , другой альтернативой является обращение к объекту через класс вместо экземпляра, например, MyController.path вместо self.path.это прямой поиск, который поможет обойти отложенный поиск, но, как упоминает Алекс, это глобальная переменная, поэтому вы теряете тот бит, который, как вы думали, вы собираетесь сохранить (если вы не создадите локальную ссылку на имя класса [global]).

24 голосов
/ 26 апреля 2010

Если вы сомневаетесь, вам, вероятно, нужен атрибут экземпляра.

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

3 голосов
/ 12 октября 2018

Тот же вопрос на Производительность доступа к переменным класса в Python - код здесь адаптирован из @Edward Loper

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

Существует 4 области действия, из которых можно получить доступ к переменным:

  1. Переменные экземпляра (self.varname)
  2. Переменные класса (Classname.varname)
  3. Переменные модуля (VARNAME)
  4. Локальные переменные (имя)

Тест:

import timeit

setup='''
XGLOBAL= 5
class A:
    xclass = 5
    def __init__(self):
        self.xinstance = 5
    def f1(self):
        xlocal = 5
        x = self.xinstance
    def f2(self):
        xlocal = 5
        x = A.xclass
    def f3(self):
        xlocal = 5
        x = XGLOBAL
    def f4(self):
        xlocal = 5
        x = xlocal
a = A()
'''
print('access via instance variable: %.3f' % timeit.timeit('a.f1()', setup=setup, number=300000000) )
print('access via class variable: %.3f' % timeit.timeit('a.f2()', setup=setup, number=300000000) )
print('access via module variable: %.3f' % timeit.timeit('a.f3()', setup=setup, number=300000000) )
print('access via local variable: %.3f' % timeit.timeit('a.f4()', setup=setup, number=300000000) )

Результат:

access via instance variable: 93.456
access via class variable: 82.169
access via module variable: 72.634
access via local variable: 72.199
...