Uzyskujesz dostęp do zmiennych składowych klasy w Pythonie?

85
class Example(object):
    def the_example(self):
        itsProblem = "problem"

theExample = Example()
print(theExample.itsProblem)

Jak uzyskać dostęp do zmiennej klasy? Próbowałem dodać tę definicję:

def return_itsProblem(self):
    return itsProblem

Ale to też zawodzi.

Adam
źródło
1
Tytuł edytowany, pytanie rzeczywiście o klasy "static" zmienne: stackoverflow.com/questions/707380/...
Ciro Santilli郝海东冠状病六四事件法轮功

Odpowiedzi:

144

Odpowiedź w kilku słowach

W twoim przykładzie itsProblemjest zmienną lokalną.

Musisz użyć selfdo ustawiania i pobierania zmiennych instancji. Możesz to ustawić w __init__metodzie. Wtedy twój kod wyglądałby tak:

class Example(object):
    def __init__(self):
        self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

Ale jeśli chcesz prawdziwej zmiennej klasy, użyj bezpośrednio nazwy klasy:

class Example(object):
    itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)
print (Example.itsProblem)

Ale uważaj na tę zmienną , ponieważ theExample.itsProblemjest automatycznie ustawiana na równą Example.itsProblem, ale nie jest wcale tą samą zmienną i może być zmieniana niezależnie.

Kilka wyjaśnień

W Pythonie zmienne można tworzyć dynamicznie. Dlatego możesz wykonać następujące czynności:

class Example(object):
    pass

Example.itsProblem = "problem"

e = Example()
e.itsSecondProblem = "problem"

print Example.itsProblem == e.itsSecondProblem 

wydruki

Prawdziwe

Dlatego właśnie to robisz w poprzednich przykładach.

Rzeczywiście, w Pythonie używamy selfas this, ale to trochę więcej. selfjest pierwszym argumentem dowolnej metody obiektu, ponieważ pierwszy argument jest zawsze odwołaniem do obiektu. Jest to automatyczne, czy tak nazywasz, selfczy nie.

Co oznacza, że ​​możesz:

class Example(object):
    def __init__(self):
        self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

lub:

class Example(object):
    def __init__(my_super_self):
        my_super_self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

To jest dokładnie to samo. Pierwszym argumentem metody obiektowej ANY jest obiekt bieżący, nazywamy to selftylko konwencją. Dodajesz tylko zmienną do tego obiektu, tak samo jak robisz to z zewnątrz.

A teraz o zmiennych klasowych.

Kiedy robisz:

class Example(object):
    itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

Zauważysz, że najpierw ustawiliśmy zmienną klasy , a następnie uzyskujemy dostęp do zmiennej obiektu (instancji) . Nigdy nie ustawiliśmy tej zmiennej obiektu, ale działa, jak to możliwe?

Cóż, Python próbuje najpierw pobrać zmienną obiektu, ale jeśli nie może jej znaleźć, poda zmienną klasy. Ostrzeżenie: zmienna klasy jest wspólna dla instancji, a zmienna obiektu nie.

Podsumowując, nigdy nie używaj zmiennych klas do ustawiania domyślnych wartości zmiennych obiektowych. Użyj __init__do tego.

W końcu dowiesz się, że klasy Pythona są instancjami, a zatem same obiektami, co daje nowy wgląd w zrozumienie powyższego. Wróć i przeczytaj to ponownie później, kiedy już to zrozumiesz.

e-satis
źródło
mówisz: „theExample.itsProblem jest automatycznie ustawiana na wartość Example.itsProblem, ale nie jest to w ogóle ta sama zmienna i może być zmieniana niezależnie” - ale to nie jest do końca poprawne, a twoje zdanie jest mylące. Ufam, że wiesz, co się tam dzieje, więc proponuję przeformułować: „jest to ta sama zmienna, ale można ją zmienić niezależnie dla każdego obiektu”.
jsbueno
Tak, ale wiązanie jest koncepcją kogoś dla innego języka programowania, takiego jak Java lub C (jak podejrzewam, że jest OP), który jest całkowicie nieznany. Następnie chciałbym wyjaśnić, czym jest wiązanie, później wiązanie późne, a następnie problem z odwołaniami do obiektów zmiennych. To by było za długie. Myślę, że kiedyś trzeba poświęcić precyzję na ołtarzu zrozumienia.
e-satis
Jest też (myślę, że to może być 2.7+) dekorator @classmethod, któremu warto się przyjrzeć. interesująca dyskusja tutaj - stackoverflow.com/questions/12179271/…
jsh
1
wiążące nazwę: myślę, że składanie fałszywych oświadczeń, bez względu na poziom, jest złym pomysłem. Proponuję tę niewielką edycję: ale uważaj na tę, ponieważ theExample.itsProblemjest automatycznie ustawiana na równą Example.itsProblem, ale z praktycznego punktu widzenia * nie jest wcale tą samą zmienną i można ją zmienić niezależnie. *: właściwie zaczyna się od tego samego obiektu, ale bardzo łatwo jest przypadkowo zmienić to, jeśli nie rozumiesz powiązania nazw
n611x007
11

Deklarujesz zmienną lokalną, a nie zmienną klasową. Aby ustawić zmienną instancji (atrybut), użyj

class Example(object):
    def the_example(self):
        self.itsProblem = "problem"  # <-- remember the 'self.'

theExample = Example()
theExample.the_example()
print(theExample.itsProblem)

Aby ustawić zmienną klasy (znaną również jako element statyczny), użyj

class Example(object):
    def the_example(self):
        Example.itsProblem = "problem"
        # or, type(self).itsProblem = "problem"
        # depending what you want to do when the class is derived.
kennytm
źródło
1
Tyle tylko, że zmienne klasowe są deklarowane raz na poziomie klasy . Twój sposób zresetuje go przy każdym wystąpieniu, naprawdę nie jest to, czego chcesz. Zobacz także stackoverflow.com/questions/2709821/python-self-explained, aby zapoznać się z uzasadnieniem jawnego ja.
2

Jeśli masz funkcję instancji (tj. Taką, która jest przekazywana self), możesz użyć self, aby uzyskać referencję do klasy za pomocą self.__class__

Na przykład w poniższym kodzie tornado tworzy instancję do obsługi żądań get, ale możemy przejąć get_handlerklasę i użyć jej do przechowywania klienta riak, więc nie musimy tworzyć go dla każdego żądania.

import tornado.web
import riak

class get_handler(tornado.web.requestHandler):
    riak_client = None

    def post(self):
        cls = self.__class__
        if cls.riak_client is None:
            cls.riak_client = riak.RiakClient(pb_port=8087, protocol='pbc')
        # Additional code to send response to the request ...
    
pasztet andrewski
źródło
To bardzo dobry przykład, aby zademonstrować pytanie z oryginalnego tytułu PO. W rzeczywistości przykład PO nie pasuje do tytułu, a inne odpowiedzi odnoszą się do przykładu. W każdym razie jest to najlepszy sposób na dostęp do zmiennej na poziomie klasy, ponieważ nie narusza ona zasady DRY. Podobnie jak w przypadku niektórych innych przykładów. Lepiej jest użyć self .__ class__ zamiast powtarzać nazwę klasy. Dzięki temu kod jest przyszłościowy, refaktoryzacja jest łatwiejsza, a jeśli odważysz się używać podklas, może to mieć również zalety.
mit
Czytelników należy ostrzec, że użycie programu obsługi, takiego jak powyżej, może prowadzić do problemów nie tylko w aplikacjach innych niż jednowątkowe. Wiele instancji klasy mogłoby teoretycznie używać tego samego programu obsługi iin równolegle, a jeśli procedura obsługi nie jest do tego zaprojektowana, co zależy od jej wewnętrznej struktury, może nie działać. „Równolegle” w aplikacji jednowątkowej oznacza, że ​​pierwsza instancja klasy nie zakończyła korzystania z procedury obsługi, a następnie druga instancja zaczyna jej używać. W takich przypadkach warto zamiast tego użyć zmiennej instancji.
mit
0

Zaimplementuj instrukcję return, jak w przykładzie poniżej! Powinnaś być dobra. Mam nadzieję, że to komuś pomoże ...

class Example(object):
    def the_example(self):
        itsProblem = "problem"
        return itsProblem 


theExample = Example()
print theExample.the_example()
burmistrz
źródło
1
Należy naprawić wcięcie kodu. Istnieją również lepsze odpowiedzi na to pytanie, a Twoja odpowiedź jest podstawową odmianą tych odpowiedzi, a nie alternatywnym rozwiązaniem ...
HEADLESS_0NE