Czy pola statyczne są takie same dla wszystkich instancji danej klasy?
Często na rozmowach przy okazji ”obiektówki” poruszany jest temat pół i metod statycznych. O ile z samą idą obiektowości kandydaci radzą sobie całkiem nieźle, to temat składowych statycznych stanowi już pewien problem. Dodatkowo w Pythonie jest to troszkę skomplikowane. Dzisiejszy wpis zrobimy w konwencji rozmowy pomiędzy kandydatem, a rekruterem by jeszcze bardziej zwiększyć realizm sytuacji. Zaczniemy delikatnie.
Czy zmienne statyczne są wspólne dla wszystkich instancji danej klasy ?
TAK
Z tym pytaniem przeważnie nie ma problemu. W końcu z definicji jeśli dane pole jest statyczne to znaczy, że należy do klasy, a nie to obiektu. Zatem wszystkie obiekty stworze na bazie naszej klasy będą współdzielić tą zmienną statyczną.
Jak zadeklarować zmienną statyczną w klasie ?
Tu już zazwyczaj podchodzimy do tablicy. I zaczyna się pisanie kodu. Efektem powinno być coś takiego:
class Czlowiek: wiek = 30
Najprostszy przykład klasy z polem statycznym o nazwie wiek.
Czy jeśli na bazie naszej klasy stworzymy dwie instancje, to obydwie będą miały tą samą wartość w polu statycznym ?
Łatwo to sprawdzić. Wystarczy taki prosty przykład:
class Czlowiek: wiek = 30 adam = Czlowiek() rafal = Czlowiek() print(adam.wiek) print(rafal.wiek)
Output:
30 30
No i wszytko się zgadza. Obie instancje mają tą samą wartość w polu wiek. Na tą chwilę nic dziwnego się tu nie wydarzyło. Przecież pole wiek należy do klasy więc obie instancje używają tej samej wartości.
Czy jeśli obie instancje odwołują się do tego samego pola wiek, to czy zmiana wartości jednego z nich zmieni ją dla wszystkich instancji ?
No i kolejny przykład:
class Czlowiek: wiek = 30 adam = Czlowiek() rafal = Czlowiek() print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}') adam.wiek = 43 print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}')
Output:
adam.wiek = 30 rafal.wiek = 30 adam.wiek = 43 rafal.wiek = 30
Ups coś tu się nie zgadza. Definitywnie zmiana pola statycznego w jednej instancji klasy nie spowodowała zmiany wartości w drugiej instancji tej samej klasy. Pytanie do Ciebie jest następujące:
Czemu tak się stało ?
Przecież na początku tej rozmowy ustaliliśmy, że pola statyczne są wspólne dla wszystkich instancji klasy. Wiem wiem zaraz powiecie, że takie wprowadzanie kandydata w błąd jest nie ładne itp. Celowo tak przygotowałem ten przykład by zachęcić do kombinowana co tu się stało. Macie już pomysł ?
Co tu się wydarzyło ?
Rzeczywiście zmiana jednej wartości pola wiek nie spowodowała zmiany w drugiej. Bo tak naprawdę nie zmieniliśmy wartości pola statycznego wiek. Jedyne co nam się udało zrobić to stworzyć nowe pole wiek w instancji klasy przypisanej do zmiennej adam. Nowe pole wiek przysłania statyczne pole wiek. Zatem w obiekcie rafal dalej widzimy statyczne pole wiek. Natomiast w obiekcie adam obserwujemy normalne pole stworzone przez nas. Żeby to udowodnić skasujemy nasze pole wiek i w ten sposób odkryjemy przesłonięte pole statyczne:
class Czlowiek: wiek = 30 adam = Czlowiek() rafal = Czlowiek() print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}') adam.wiek = 43 print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}') del adam.wiek print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}')
Output:
adam.wiek = 30 rafal.wiek = 30 adam.wiek = 43 rafal.wiek = 30 adam.wiek = 30 rafal.wiek = 30
Po skasowaniu naszego pola (tego należącego do obiektu) znowu wartość pola wiek jest równa 30. To dlatego już nic go nie przesłania. Dzięki polu __dict__ można podejrzeć jakie pola ma nasz obiekt (a nie klasa, a zatem ta metoda nie wyświetli nam pól statycznych będących własnością klasy):
class Czlowiek: wiek = 30 adam = Czlowiek() rafal = Czlowiek() print(f'adam.wiek = {adam.__dict__}') adam.wiek = 43 print(f'adam.wiek = {adam.__dict__}') del adam.wiek print(f'adam.wiek = {adam.__dict__}')
Output:
adam.wiek = {} adam.wiek = {'wiek': 43} adam.wiek = {}
Zatem jak sprawić by rzeczywiście zmienić wartość pola statycznego klasy człowiek ?
Należy odwołać się do pola wartość za pomocą klasy, a nie obiektu:
class Czlowiek: wiek = 30 adam = Czlowiek() rafal = Czlowiek() print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}') Czlowiek.wiek = 42 print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}')
Output:
adam.wiek = 30 rafal.wiek = 30 adam.wiek = 42 rafal.wiek = 42
Teraz zmieniliśmy jednocześnie wartość pola wiek dla wszystkich obiektów powstałych na bazie klasy człowiek. Super o to chodziło. No i teraz ostatnie pytanie:
Jak sprawić by zmienić wartość statycznego pola wiek z wewnątrz klasy ?
Do zmiennych statycznych nie można odwoływać się za pomocą self. Self służy do odwoływania się do elementów danego obiektu. My musimy odwołać się do pola będącego własnością klasy, a nie obiektu. Do pól statycznych mogą odwoływać się tylko metody statyczne. W Pythonie mamy dwie opcje aby to zrobić:
- @staticmethod
- @classmethod
Dekorator @staticmethod nie umożliwia nam dostępu do statycznych składowych klasy. Natomiast można się do nich dobrać stosując dekorator @classmethod. Zobaczmy jak zastosować tą wiedzę w praktyce:
class Czlowiek: wiek = 30 @classmethod def zien_pole_statyczne(cls): cls.wiek = 100 adam = Czlowiek() rafal = Czlowiek() print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}') adam.zien_pole_statyczne() print(f'adam.wiek = {adam.wiek}') print(f'rafal.wiek = {rafal.wiek}')
Output:
adam.wiek = 30 rafal.wiek = 30 adam.wiek = 100 rafal.wiek = 100
No i wszytko działa. Mam nadzieję, że przedstawione w tym wpisie informacje usystematyzowały Twoją wiedzę z zakresu pół i metod statycznych. Nie daj się złapać na triku z przesłanianiem pól statycznych poprzez pola należące do instancji klasy.