Как я могу получить доступ к «статическим» переменным класса внутри методов класса в Python?

162

Если у меня есть следующий код Python:

class Foo(object):
    bar = 1

    def bah(self):
        print(bar)

f = Foo()
f.bah()

Жалуется

NameError: global name 'bar' is not defined

Как я могу получить доступ к классу / статической переменной barвнутри метода bah?

Росс Роджерс
источник
Более подробную информацию о Python и статиках можно найти здесь: stackoverflow.com/questions/68645/python-static-variable
bedwyr

Ответы:

166

Вместо barиспользования self.barили Foo.bar. Назначение для Foo.barсоздаст статическую переменную, а присвоение self.barсоздаст переменную экземпляра.

Павел Страхов
источник
12
Foo.bar будет работать, но self.bar создает переменную экземпляра, а не статическую.
Bedwyr
47
bedwyr, "print self.bar" не будет создавать никаких переменных экземпляра (хотя присваивание self.bar будет).
Константин
@ Constantin - я этого не понимал, это интересное различие. Спасибо за исправление :-)
bedwyr
2
Но если вы не собираетесь использовать ивар, лучше использовать Foo.classmember.
mk12
2
при использовании статических переменных хорошей идеей будет прочитать отсюда: stackoverflow.com/questions/68645/… . @Constantin дает один из множества гочей.
Тревор Бойд Смит
84

Определите метод класса:

class Foo(object):
    bar = 1
    @classmethod
    def bah(cls):    
        print cls.bar

Теперь, если bah()должен быть метод экземпляра (т.е. иметь доступ к себе), вы все равно можете напрямую получить доступ к переменной класса.

class Foo(object):
    bar = 1
    def bah(self):    
        print self.bar
Vartec
источник
3
Почему бы не просто Foo.bar, а не self.__class__.bar?
mk12
15
@ Mk12: Когда у вас есть наследование классов, «переменная класса» может находиться в базовом классе или подклассе. К чему вы хотите обратиться? Зависит от того, что ты пытаешься сделать. Foo.bar всегда будет ссылаться на атрибут указанного класса, который может быть базовым классом или подклассом. self.__class__.bar будет ссылаться на тот класс, тип которого является экземпляром.
Крейг МакКуин
7
Теперь мы достигли того печального момента, когда стали настолько осведомлены о деталях языка, что смогли различить два точно настроенных варианта использования. Это действительно зависит от того, что вы хотите сделать, но многие люди не будут обращать внимания на такие тонкости.
holdenweb
2
Кстати, это редкое использование «переменной класса». Гораздо более распространено, чтобы это было определено в определенном классе, здесь Foo. Это полезная информация для некоторых сложных ситуаций программирования. Но почти наверняка не то, на что исходный вопрос хотел получить ответ (любой, кто нуждается в ЭТОМ ответе, уже знает, как сделать Foo.bar). +1 потому что я чему-то научился из этого.
ToolmakerSteve
3
Я изучаю, когда использовать переменные и методы класса (в отличие от переменных и методов экземпляра). Если вы хотите получить доступ к переменной класса в методе экземпляра, необходимо ли использовать self.__class__.bar? Я заметил, что self.bar работает, хотя bar является переменной класса, а не переменной экземпляра.
Билл
13

Как и во всех хороших примерах, вы упростили то, что вы на самом деле пытаетесь сделать. Это хорошо, но стоит отметить, что python обладает большой гибкостью, когда речь идет о переменных класса и экземпляра. То же самое можно сказать и о методах. Для хорошего списка возможностей я рекомендую прочитать введение в классы нового стиля Михаэля Фетша , особенно разделы со 2 по 6.

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

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

  1. Функция / метод
  2. Текущий модуль
  3. Встроенные команды

{begin pedagogy}

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

>>> class A(object):
        foo = 'foo'
        bar = foo


>>> A.foo
'foo'
>>> A.bar
'foo'

но:

>>> class B(object):
        foo = 'foo'
        def get_foo():
            return foo
        bar = get_foo()



Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    class B(object):
  File "<pyshell#11>", line 5, in B
    bar = get_foo()
  File "<pyshell#11>", line 4, in get_foo
    return foo
NameError: global name 'foo' is not defined

{end pedagogy}

В конце концов, что нужно помнить, что вы действительно имеете доступ к любой из переменных , которые вы хотите получить доступ, но , вероятно , не неявно. Если ваши цели просты и понятны, то, вероятно, будет достаточно перейти на Foo.bar или self.bar. Если ваш пример усложняется, или вы хотите заняться такими причудливыми вещами, как наследование (вы можете наследовать статические методы / методы класса!), Или идея ссылки на имя вашего класса внутри самого класса кажется вам неправильной, проверьте вступление я связал.

Дэвид Бергер
источник
Во IIRC технически найдено 3 (+) пространства имен - локально-функциональное, модульное / глобальное и встроенные. Вложенные области означают, что можно искать несколько локальных областей, но это один из исключительных случаев. (...)
Джефф Шеннон
Кроме того, я бы осторожно сказал «основной модуль», так как ищется модуль, содержащий функцию, а не основной модуль ... И поиск атрибута из экземпляра ref - это другое дело, но этот ответ объясняет, почему вам нужен экземпляр / класс ref.
Джефф Шеннон
10
class Foo(object):
     bar = 1
     def bah(self):
         print Foo.bar

f = Foo() 
f.bah()
Аллан Мвесигва
источник
-2
class Foo(object):    
    bar = 1

    def bah(object_reference):
        object_reference.var = Foo.bar
        return object_reference.var


f = Foo() 
print 'var=', f.bah()
Лопи Дани
источник
4
Хотя этот код может решить вопрос, в том числе объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, приведет к большему количеству голосов. Помните, что вы отвечаете на вопрос для читателей в будущем, а не только для того, кто спрашивает сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснения и указать, какие ограничения и предположения применяются. Из обзора
двойной сигнал