class Person:
    def __init__(self, name):               # Dla [Person()]
        self._name = name                   # Wywołuje __setattr__!

    def __getattr__(self, attr):            # Dla [obiekt.niezdefiniowany]
        print('pobranie: ' + attr)
        if attr == 'name':                  # Przechwycenie name: nie przechowano
            return self._name               # Nie tworzy pętli: prawdziwy atrybut
        else:                               # Pozostałe są błędami
            raise AttributeError(attr)

    def __setattr__(self, attr, value):     # Dla [obiekt.dowolny = wartość]
        print('ustawienie: ' + attr)
        if attr == 'name':
            attr = '_name'                  # Ustawienie nazw wewnętrznych
        self.__dict__[attr] = value         # Tutaj unikanie pętli

    def __delattr__(self, attr):            # Dla [del obiekt.dowolny]
        print('usunięcie: ' + attr)
        if attr == 'name':
            attr = '_name'                  # Tutaj także unikanie pętli
        del self.__dict__[attr]             # jednak jest to o wiele rzadsze

bob = Person('Robert Zielony')              # bob ma zarządzany atrybut
print(bob.name)                             # Wykonuje __getattr__
bob.name = 'Robert A. Zielony'              # Wykonuje __setattr__
print(bob.name)
del bob.name                                # Wykonuje __delattr__

print('-'*20)
anna = Person('Anna Czerwona')              # anna także dziedziczy właściwość
print(anna.name)
#print(Person.name.__doc__)                 # Tutaj brak odpowiednika


