Na co pozwala __dict__ obiektu klasy

Co to jest __dict__? Jest to słownik właściwości danego obiektu np.:

from dataclasses import dataclass

@dataclass()
class User:
    name: str
    age: str

Jak widzisz, klasa User zawiera dwie właściwości, name oraz age. Teraz stwórzmy nowy obiekt tej klasy.

user = User('John', 25)

Aby odczytać wartości tych właściwości należało by zrobić np tak:

print(user.name)
#John

Zamiast tego możemy wykorzystać słownik który tworzy się automatycznie w momencie tworzenia nowego obiektu klasy.

print(user.__dict__['name'])
#John

No i po co mi to, nie dość że zapis dłuższy to jeszcze trochę słabo wygląda, może i tak, ale odczytywanie wartości właściwości to tylko wierzchołek, idźmy dalej. Może wylistujmy sobie wszystkie wartości:

print(user.__dict__.items())
#dict_items([('name', 'John'), ('age', 25)])

Wrzućmy to w pętlę


for key, value in user.__dict__.items():
    print(f"{key} = {value}")
    
'''
name = John
age = 25
'''

A może skonwertujemy te wartości na JSON?
Zaimportujmy bibliotekę json.

from dataclasses import dataclass
import json

@dataclass
class User:
    name: str
    age: str

user = User('John', 25)

print(json.dumps(user.__dict__))

#{"name": "John", "age": 25}

Ciągle jeszcze mało, może zróbmy klonowanie obiektu.

user = User('John', 25)
user2 = User(**user.__dict__)

print(user2.name)
#John

Jak widzisz, nie muszę wprowadzać każdej właściwości osobno, wrzucam cały słownik.

A może by tak spróbować zaktualizować kilka właściwości w obiekcie?

update = {"age" : 30, "name" : "Tom"}
user.__dict__.update(update)

print(user.__dict__)
#{'name': 'Tom', 'age': 30}

Kojarzysz parametr frozen dekoratora @dataclass? Parametr ten określa obiekt klasy jako niemożliwy do zmodyfikowania. Co to znaczy? Tylko to, że nie będziesz miał możliwości zmiany wartości właściwości obiektu.

from dataclasses import dataclass

@dataclass(frozen=True)
class User:
    name: str
    age: str

user = User('John', 25)
user.name = "Tom"

Takie działanie spowoduje wystąpienie błędu

dataclasses.FrozenInstanceError: cannot assign to field 'name'

Dzięki __dict__ możemy obejść tą blokadę

from dataclasses import dataclass

@dataclass(frozen=True)
class User:
    name: str
    age: str

user = User('John', 25)
user.__dict__['name'] = "Tom";

print(user.name)
#Tom

Taką operację można przeprowadzić również za pomocą object.__setattr__

user = User('John', 25)
object.__setattr__(user, 'name', "Tom")

print(user.name)
#Tom

__dict__ pozwala nam również na wygnerowanie dynamicznych właściwości klasy


fields = ["x", "y", "z"]
@dataclass
class Point:
    def __init__(self, values):
        self.__dict__.update(dict(zip(fields, values)))


p = Point([1,2,3])
print(p.__dict__)

Może ten fragment wydaje Ci sie niejasny?

self.__dict__.update(dict(zip(fields, values)))

funkcja zip() łączy dwie (lub więcej) listy element po elemencie, w naszym przypadku połączy listę fields oraz wartości przekazane w konstruktorze klasy Point.

funkcja dict() zamienia te połączone listy w słownik

self.__dict__.update() aktualizuje właściwości obiektu.

Efektem działania tego kodu jest to:

{'x': 1, 'y': 2, 'z': 3}

Możemy również szybko modyfikować __repr__ i __str__

class SomeClass:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
        
    def __repr__(self):
        return f"{self.__class__.__name__} debug: ({self.__dict__})"
    
some_object = SomeClass(name="Tom", age="30")
print(some_object)

#SomeClass debug: ({'name': 'Tom', 'age': '30'})

dodajmy do tego metode __str__

 def __str__(self):
        return f"{self.__dict__['name']} ma {self.__dict__['age']} lat"

Po uruchomieniu kodu w konsoli zobaczymy teraz

Tom ma 30 lat

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *