Problem z atrybutem utworzonym z pomocą property dziedziczonym z klasy abstrakcyjnej

0

Siema,

Tworze swój pierwszy projekt i udało mi się już natrafić na problem, z którym nie mogę sobie poradzić.
Program przy utworzeniu obiektu klasy Dog wyrzuca mi **AttributeError **o treści **'Dog' object has no attribute '_name' **ale nie do końca rozumiem czemu ten błąd się pojawia.
Z góry dziękuje za pomoc.

from abc import ABC, abstractclassmethod
import random as rd

class Animal(ABC):
    
    def __init__(self, name, date_of_birth, gender, is_vaccinated, is_sterilized):
        self._id = str(rd.randrange(1, 100)) + self.name + str(rd.randrange(1, 100))
        self.name = name
        self.date_of_birth = date_of_birth
        self.gender = gender
        self.is_vaccinated = is_vaccinated
        self.is_sterilized = is_sterilized
    
    
    @property
    def name(self):
        return self._name
    
    @name.setter
    def name(self, value):
        if self._name.isalpha():
            self._name = value
        else:
            print('Name of your pupil should contain only alphabet letters.')


    @property
    def date_of_birth(self):
        return self._date_of_birth

    @date_of_birth.setter
    def date_of_birth(self, value):
        self._date_of_birth = value
        
        
    @property
    def gender(self):
        return self._gender

    @gender.setter
    def gender(self, value):
        if self.gender == 'male' or self.gender == 'female':
            self._gender = value
        else:
            print('Gender must be of type male or female.')


    @property
    def is_vaccinated(self):
        return self._is_vaccinated

    @is_vaccinated.setter
    def is_vaccinated(self, value):
        if self.is_vaccinated == 'yes' or self.is_vaccinated == 'no':
            self._is_vaccinated = value
        else:
            print('Choose yes or no.')


    @property
    def is_sterilized(self):
        return self._is_sterilized

    @is_sterilized.setter
    def is_sterilized(self, value):
        if self.is_sterilized == 'yes' or self.is_sterilized == 'no':
            self._is_sterilized = value
        else:
            print('Choose yes or no.')

    def __str__(self):
        result = self.__class__().__name__() + '\n'
        result += self.name + '\n'
        result += self.date_of_birth + '\n'
        result += self.gender + '\n'
        result += self.is_vaccinated + '\n'
        result += self.is_sterilized + '\n'
        return result


class Dog(Animal):
    
    def __init__(self, name, date_of_birth, gender, is_vaccinated, is_sterilized, dog_sound='HauHau'):
        super().__init__(name, date_of_birth, gender, is_vaccinated, is_sterilized)
        self.dog_sound = dog_sound

    def __str__(self):
        return super().__str__() + self.dog_sound
    
    
    
doggo = Dog('Tofik', 20102011, 'male', 'yes', 'yes')
0

Ogólnie to dziwnie żonglujesz w tym kodzie nazwami, raz u ciebie nazwa 'name' oznacza funkcję, drugi raz atrybut klasy.
W sumie to się tyczy wszystkich nazw w tym kodzie.

0

@Eldorad O.: Rozumiem, ogólnie to w takim razie zagubiłem się trochę w tym użyciu properties ale może napisze po prostu co chciałem osiągnąć z takim zapisem.

Ja to rozumiem tak ze tworze sobie *properte * name, która zwraca mi wartość atrybutu _name, następnie tworzę setter odnoszący się do **name **który tworzy mi atrybut instancji **_name **po jakiejś wcześniejszej walidacji, która dla uproszczenia usunąłem chwilowo. Następnie stworzyłem sobie konstruktor do którego podaje wartość jaką ma przyjąć atrybut _name. W samym już konstruktorze a dokładnie w linijce 8mej wywołuje properte name, która do atrybutu **_name **przypisuje wartość z argumentu konstruktora.

Jeśli chodzi o samą klasę Dog to w jej konstruktorze odwołuję się do konstruktora z klasy macierzystej po przez słówko kluczowe super() i w tym oto konstruktorze wywołuje properte name, która przypisuje mi wartość do atrybutu _name.

Dodam tylko jeszcze, że chciałbym aby atrybut name był prywatny stąd zapis _name.

Gdzie w takim razie leży błąd w moim myśleniu?

from abc import ABC, abstractclassmethod
import random as rd

class Animal(ABC):
    
    def __init__(self, name):
        self._id = str(rd.randrange(1, 100)) + self.name + str(rd.randrange(1, 100))
        self.name = name 
    
    @property
    def name(self):
        return self._name
    
    @name.setter
    def name(self, value):
        self._name = value


    def __str__(self):
        result = self.__class__().__name__() + '\n'
        result += self.name + '\n'
        return result


class Dog(Animal):
    
    def __init__(self, name, dog_sound='HauHau'):
        super().__init__(name)
        self.dog_sound = dog_sound

    def __str__(self):
        return super().__str__() + self.dog_sound
    
     
doggo = Dog('Tofik')
1

linia 7: nie możesz odwołać się przy tworzeniu id do self.name, bo jeszcze nie utworzyłeś tego atrybutu, zamień na name.
linia 8: self.name = name, tu deklarujesz swoją zmienną, ale jako publiczna, powinno być self._name, jeśli chcesz chronioną, self.__name jeśli prywatną.
Dalej w kodzie jeżeli używasz self._name lub self.__name w zależności co tam utworzyłeś, to się odwołujesz bezpośrednio do zmiennej, jeśli natomiast self.name to z wowołaniem settera lub gettera.

Druga sprawa to to Twoje id, przy dużej ilości obiektów, lub przy dużym szczęściu utworzą Ci się obiekty z takim samym id. Utwórz licznik id jako zmienna klasy, dodawaj do niej 1 i przypisuj dla poszczególnych obiektów jako id, to będziesz miał unikalne po kolei. Jak chcesz losowo to użyj uuid.

2

@Lil Keyboard: Spójrz sobie na to, i zastosuj do reszty:

from abc import ABC, abstractclassmethod
import random as rd

class Animal(ABC):

    def __init__(self, name):       
        self.__name = name 
        self.__id = str(rd.randrange(1, 100)) + self.__name + str(rd.randrange(1, 100))

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, value):
        self.__name = value

    def __str__(self):
        result = self.__class__.__name__ + '\n'
        result += self.name + '\n'
        return result

class Dog(Animal):

    def __init__(self, name, dog_sound='HauHau'):
        super().__init__(name)
        self.dog_sound = dog_sound

    def __str__(self):
        return super().__str__() + self.dog_sound

doggo = Dog('Tofik')

print(doggo)
0

@Eldorad O.: No dobra ale przy zapisie w linijce 7mej self.__name = name tracę chyba sens tworzenia mojego settera, w którym chciałbym przeprowadzić walidację. Jak w takim razie sprawić abym mógł korzystać z tej walidacji i jednocześnie mieć swoją zmienną chronioną?(Pisząc ten post miałem na myśli zmienną chronioną)

0
def __init__(self, name):       
        self.name = name
        self.__id = str(rd.randrange(1, 100)) + self.__name + str(rd.randrange(1, 100))
0

@Eldorad O.: Przy takim zapisie niestety wyrzuca mi błąd AttributeError: 'Dog' object has no attribute '_name'

from abc import ABC, abstractclassmethod
import random as rd

class Animal(ABC):

    def __init__(self, name):       
        self.name = name 
        self.__id = str(rd.randrange(1, 100)) + self._name + str(rd.randrange(1, 100))

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        if self._name.isalpha():
            self._name = value
        else:
            print('Blad!')

    def __str__(self):
        result = self.__class__.__name__ + '\n'
        result += self.name + '\n'
        return result

class Dog(Animal):

    def __init__(self, name, dog_sound='HauHau'):
        super().__init__(name)
        self.dog_sound = dog_sound

    def __str__(self):
        return super().__str__() + self.dog_sound

doggo = Dog('Tofik12')

print(doggo)
1

@Lil Keyboard: W setterze sprawdzasz czy pole self._name zawiera wyłącznie litery. W momencie kiedy tworzysz obiekt tej klasy, atrybut _name nie istnieje, więc wywala ci błąd, jeżeli chcesz sprawdzić poprawność wprowadzanej wartości to sprawdzasz tę wprowadzaną wartość:

def name(self, value):
    if value.isalpha():
            self._name = value
        else:
            print('Blad!')

1 użytkowników online, w tym zalogowanych: 0, gości: 1