Jak mogę uzyskać dostęp do „statycznych” zmiennych klas w ramach metod klas w języku Python?

162

Jeśli mam następujący kod w Pythonie:

class Foo(object):
    bar = 1

    def bah(self):
        print(bar)

f = Foo()
f.bah()

Narzeka

NameError: global name 'bar' is not defined

Jak mogę uzyskać dostęp do zmiennej klasy / statycznej barw metodzie bah?

Ross Rogers
źródło
Więcej informacji o Pythonie i statyce można znaleźć tutaj: stackoverflow.com/questions/68645/python-static-variable
bedwyr

Odpowiedzi:

166

Zamiast barużywać self.barlub Foo.bar. Przypisanie do Foo.barspowoduje utworzenie zmiennej statycznej, a przypisanie do self.barspowoduje utworzenie zmiennej instancji.

Pavel Strakhov
źródło
12
Foo.bar będzie działać, ale self.bar tworzy zmienną instancji, a nie statyczną.
bedwyr
47
bedwyr, "print self.bar" nie utworzy żadnych zmiennych instancji (chociaż przypisanie do self.bar spowoduje).
Constantin
@Constantin - nie zdawałem sobie z tego sprawy, to ciekawe wyróżnienie. Dzięki za korektę :-)
bedwyr
2
Ale jeśli nie zamierzasz istnieć ivar, jaśniej będzie użyć Foo.classmember.
mk12
2
używając zmiennych statycznych, dobrze jest zapoznać się z pułapkami stąd: stackoverflow.com/questions/68645/… . @Constantin daje jeden z wielu problemów.
Trevor Boyd Smith
84

Zdefiniuj metodę klasy:

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

Teraz, jeśli bah()ma to być metoda instancji (tj. Mieć dostęp do siebie), nadal możesz bezpośrednio uzyskać dostęp do zmiennej klasy.

class Foo(object):
    bar = 1
    def bah(self):    
        print self.bar
vartec
źródło
3
Dlaczego nie po prostu Foo.bar zamiast self.__class__.bar?
mk12
15
@ Mk12: Kiedy masz dziedziczenie klas, „zmienna klasy” może znajdować się w klasie bazowej lub podklasie. Do czego chcesz się odnieść? Zależy od tego, co próbujesz zrobić. Foo.bar zawsze odnosiłby się do atrybutu określonej klasy - która może być klasą bazową lub podklasą. self.__class__.bar odwołuje się do dowolnej klasy, której jest typem instancji.
Craig McQueen,
7
Teraz osiągnęliśmy ten niefortunny punkt, kiedy zdaliśmy sobie sprawę ze szczegółów języka, że ​​możemy rozróżnić dwa doskonale dopasowane przypadki użycia. Rzeczywiście zależy to od tego, co chcesz zrobić, ale wiele osób nie będzie zwracać uwagi na takie subtelności.
holdenweb
2
Przy okazji, jest to RZADKO użycie „zmiennej klasy”. O wiele bardziej powszechne jest definiowanie go w określonej klasie, tutaj Foo. Jest to przydatna informacja w niektórych zaawansowanych sytuacjach programistycznych. Ale prawie na pewno nie to, czego wymagało pierwotne pytanie jako odpowiedź (każdy, kto potrzebuje TEJ odpowiedzi, będzie już wiedział, jak zrobić Foo.bar). +1, ponieważ czegoś się z tego nauczyłem.
ToolmakerSteve
3
Dowiaduję się, kiedy używać zmiennych i metod klas (w przeciwieństwie do zmiennych i metod instancji). Jeśli chcesz uzyskać dostęp do zmiennej klasy w metodzie instancji, czy konieczne jest użycie self.__class__.bar? Zauważyłem, że self.bar działa, mimo że bar jest zmienną klasy, a nie zmienną instancji.
Bill
13

Jak w przypadku wszystkich dobrych przykładów, uprościliśmy to, co faktycznie próbujesz zrobić. To dobrze, ale warto zauważyć, że Python ma dużą elastyczność, jeśli chodzi o zmienne klas i instancji. To samo można powiedzieć o metodach. Aby uzyskać dobrą listę możliwości, polecam przeczytanie wprowadzenia do zajęć w nowym stylu Michaela Fötscha , zwłaszcza części od 2 do 6.

Na początku trzeba dużo pracy zapamiętać, że Python to nie java. Więcej niż banał. W Javie kompilowana jest cała klasa, dzięki czemu rozwiązywanie przestrzeni nazw jest naprawdę proste: wszelkie zmienne zadeklarowane poza metodą (gdziekolwiek) są zmiennymi instancji (lub, jeśli są statyczne, klasowe) i są niejawnie dostępne w ramach metod.

W Pythonie główną zasadą jest to, że istnieją trzy przestrzenie nazw, które są przeszukiwane w kolejności pod kątem zmiennych:

  1. Funkcja / metoda
  2. Bieżący moduł
  3. Wbudowane

{begin pedagogy}

Istnieją ograniczone wyjątki od tej reguły. Głównym, który przychodzi mi do głowy, jest to, że podczas ładowania definicji klasy definicja klasy jest jej własną niejawną przestrzenią nazw. Ale to trwa tylko tak długo, jak długo moduł jest ładowany i jest całkowicie pomijane, gdy znajduje się w metodzie. A zatem:

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


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

ale:

>>> 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}

W końcu, rzeczą do zapamiętania jest to, że nie ma dostępu do żadnej z tych zmiennych, które chcesz otworzyć, ale chyba nie w sposób dorozumiany. Jeśli Twoje cele są proste i nieskomplikowane, prawdopodobnie wystarczy Foo.bar lub self.bar. Jeśli Twój przykład staje się bardziej skomplikowany lub chcesz robić wymyślne rzeczy, takie jak dziedziczenie (możesz dziedziczyć metody statyczne / klasowe!), Lub pomysł odwoływania się do nazwy twojej klasy w samej klasie wydaje ci się zły, sprawdź intro, które połączyłem.

David Berger
źródło
IIRC, technicznie rzecz biorąc, przeszukano 3 (+) przestrzenie nazw - lokalną funkcję, moduł / globalną i wbudowaną. Zasięg zagnieżdżony oznacza, że ​​można przeszukiwać wiele zakresów lokalnych, ale jest to jeden z wyjątkowych przypadków. (...)
Jeff Shannon
Byłbym również ostrożny, mówiąc `` moduł główny '', ponieważ przeszukiwany jest moduł zawierający funkcję, a nie moduł główny ... A wyszukiwanie atrybutu z elementu referencyjnego instancji to inna sprawa, ale ta odpowiedź wyjaśnia, dlaczego potrzebujesz instancji / klasy ref.
Jeff Shannon
10
class Foo(object):
     bar = 1
     def bah(self):
         print Foo.bar

f = Foo() 
f.bah()
Allan Mwesigwa
źródło
-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()
Lopi Dani
źródło
4
Chociaż ten kod może rozwiązać problem, w tym wyjaśnienie, jak i dlaczego to rozwiązuje problem, naprawdę pomogłoby poprawić jakość twojego posta i prawdopodobnie zaowocowałoby większą liczbą pozytywnych głosów. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a nie tylko osoba, która zapyta teraz. Proszę edytować swoje odpowiedzi, aby dodać wyjaśnień i dać wskazówkę co zastosować ograniczenia i założenia. Z recenzji
double-beep