Як я можу отримати доступ до "статичних" змінних класу в межах методів класу в 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
Але якщо ви не маєте наміру існувати ivar, зрозуміліше використовувати 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

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 працює, хоча бар є змінною класу, а не змінною екземпляра.
Білль

13

Як і у всіх хороших прикладах, ви спростили те, що насправді намагаєтесь зробити. Це добре, але варто зазначити, що python має велику гнучкість, коли мова йде про змінні класу проти екземплярів. Те саме можна сказати і про методи. Для гарного переліку можливостей рекомендую ознайомитись із введенням нового класу Майкла Фотча , особливо розділи 2 по 6.

Одне, що потребує великої праці, щоб пам’ятати, коли почати, це те, що пітон не є 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 (+) простору імен - функціонально-локальний, модульний / глобальний та вбудований. Вкладені області означають, що можна шукати кілька локальних областей, але це один з виняткових випадків. (...)
Джефф Шеннон

Крім того, я буду обережно говорити "головний модуль", оскільки це модуль, що містить функцію, який шукається, а не головний модуль ... І пошук атрибуту з рефлектора екземпляра - різна річ, але ця відповідь пояснює, чому вам потрібна посилання на екземпляр / клас
Джефф Шеннон


-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
Хоча цей код може вирішити питання, включаючи пояснення, як і чому це вирішує проблему, справді допоможе покращити якість вашої публікації та, ймовірно, призведе до збільшення кількості голосів. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, а не лише про людину, яка зараз задає питання. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення та вказати, які обмеження та припущення застосовуються. З огляду
подвійний звуковий сигнал
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.