AttributeError при взаимодействии с атрибутом year из класса предка (у класса есть этот атрибут)
Это весь код моей программы:
import datetime
class Human:
'''
Класс: Human, Родитель
методы: changeName, calcAge
Описание: Класс описывает свойства объекта человек. (name, year)
'''
# Конструктор
def __init__(self, name="", year=0) -> None:
self.name = name
self.year = year
# Метод changeName изменяет имя пользователя
def changeName(self, userName) -> None:
self.name = userName
print(f"Имя измененно на {userName}")
# Метод calcAge вычисляет возраст пользователя
def calcAge(self) -> int:
age = datetime.date.today().year - self.year
return age
class Student(Human):
'''
Класс: Student, Ребёнок
методы: changeCourse, nextCourse
Описание: Класс описывает свойства студента. (course)
'''
# Конструктор
def __init__(self, course=1) -> None:
self.course = course
# Метод changeCourse изменяет года обучения
def changeCourse(self, userCourse) -> str:
if (17 >= self.calcAge() <= 27): self.course = userCourse
else: return print(f"{self.name} не входит в возрастную катигорию")
# Метод nextCourse переводи студента на следующий курс
def nextCourse(self) -> str:
self.course += 1
if (self.course == 5): return print(f"{self.name} закончил/закончила обучение")
human1 = Human(name="Лёша", year=2008)
human2 = Human(name="Маша", year=2008)
student1 = Student(2)
student2 = Student(1)
print("\nТест на смену имени")
student1.changeName("Ваня")
student2.changeName("Соня")
print(student1.name)
print(student2.name)
print("\nТест на изменение курса")
print(f"{student1.year} на {student1.course} курсе\n{student2.name} на {student2.course} курсе")
student1.changeCourse(3)
print(f"{student1.name} на {student1.course} курсе\n{student2.name} на {student2.course} курсе")
В Human.calcAge()
я использую year
для вычисления возраста. Я использую этот метод в классе-наследнике Student
, чтобы получить возраст экземпляра.
Но при использовании этого метода внутри класса-наследника происходит исключение:
AttributeError: 'Student' object has no attribute 'year'.
Причём с атрибутом name
всё нормально: я ссылаюсь на него от экзмепляров student1
и student2
в 55, 56, 60 и 64 строке.
Я хотел решить свою проблему используя метод super()
, но при его использовании атрибут year
равен нулю, чему он, собственно, равен по умолчанию:
def __init__(self, course=1) -> None:
super().__init__()
self.course = course;
Ответы (2 шт):
Вы были на правильном пути - для инициализации атрибутов родительского класса в наследнике можно использовать super().__init__()
В такой __init__
аналогично можно передать аргументы (как year
). Причем наиболее кратко (без повторения аргументов) это можно сделать с помощью *args и **kwargs - конструкций для приёма неопределенного количества позиционных и именованных аргументов.
Указываем их для приёма в наследнике, а потом передаем в метод родительского класса:
class Human:
def __init__(self, name="", year=0) -> None:
self.name = name
self.year = year
class Student(Human):
# используем **kwargs, т.к. в родительском классе именованные аргументы
def __init__(self, course=1, **kwargs) -> None:
super().__init__(**kwargs)
self.course = course
student1 = Student(course=2, year=2008)
print(student1.year)
print(student1.__dict__) # магический метод __dict__ показывает все атрибуты
Вывод:
2008
{'name': '', 'year': 2008, 'course': 2}
Но можно и прямо прописать аргументы для приема, продублировав их:
class Human:
def __init__(self, name="", year=0) -> None:
self.name = name
self.year = year
class Student(Human):
def __init__(self, course=1, name="", year=0) -> None:
super().__init__(name, year)
self.course = course
Такой вариант подойдет? Останется только методы добавить.
class Human:
def __init__(self, name="", year=0) -> None:
self.name = name
self.year = year
class Student(Human):
def __init__(self, course=1, **kwargs) -> None:
super().__init__(**kwargs)
self.course = course
def human(name="", year=0):
_name = name
_year = year
def student(course=1):
return Student(course=course, name=_name, year=_year)
return student
human1 = human(name="Лёша", year=2008)
student1 = human1(course=2)
student2 = human(name="Маша", year=2008)(course=1)
print(student1.name, student1.year, student1.course)
print(student2.name, student2.year, student2.course)
Еще один вариант:
class Human:
def __init__(self, name="", year=0) -> None:
self.name = name
self.year = year
def __call__(self, course=1):
_student = Student(course=course)
setattr(_student,'name',self.name)
setattr(_student,'year',self.year)
return _student
class Student(Human):
def __init__(self, course=1) -> None:
self.course = course
human1 = Human(name="Лёша", year=2008)
student1 = human1(course=2)
student2 = Human(name="Маша", year=2008)(course=1)
print(student1.name, student1.year, student1.course)
print(student2.name, student2.year, student2.course)
Если кровь из носу нужно хранить name и year в классе Human:
class Human:
def __init__(self, name="", year=0) -> None:
self.name = name
self.year = year
self._self = self
def __call__(self, course=1):
_student = Student(course=course)
setattr(_student,'human',self._self)
return _student
class Student(Human):
def __init__(self, course=1) -> None:
self.course = course
@property
def name(self):
_name = ''
if hasattr(self,'human'):
_name = self.human.name
return _name
@property
def year(self):
_year = 0
if hasattr(self,'human'):
_year = self.human.year
return _year
human1 = Human(name="Лёша", year=2008)
student1 = human1(course=2)
student2 = Human(name="Маша", year=2008)(course=1)
print(student1.name, student1.year, student1.course)
print(student2.name, student2.year, student2.course)
human1.year = 2014
print(student1.name, student1.year, student1.course)