Объекты как атрибуты
Мы уже видели примеры классов, у которых списки были атрибутами. Раз ничто не мешает нам включать изменяемые объекты в качестве атрибутов в наши классы, мы точно так же можем использовать экземпляры наших собственных классов как атрибуты в других классах, которые мы определили сами. В следующих примерах мы определим классы Course, Student и CompletedCourse. Завершенный курс использует первые два класса. Определения классов очень короткие и простые, чтобы легче было сосредоточиться на технике использования экземпляров наших собственных классов как атрибутов.
Будем считать, что каждый класс определен в отдельном файле.
Сначала определим класс Course в файле с именем course.py:
class Course:
def __init__(self, name: str, code: str, credits: int):
self.name = name
self.code = code
self.credits = creditsЗатем класс Student в файле с именем student.py:
class Student:
def __init__(self, name: str, student_number: str, credits: int):
self.name = name
self.student_number = student_number
self.credits = creditsНаконец, класс CompletedCourse определяется в файле с именем completedcourse.py. Поскольку он использует два других класса, их нужно импортировать перед использованием:
from course import Course
from student import Student
class CompletedCourse:
def __init__(self, student: Student, course: Course, grade: int):
self.student = student
self.course = course
self.grade = gradeВот пример главной функции, которая добавляет несколько завершенных курсов в список:
from completedcourse import CompletedCourse
from course import Course
from student import Student
# Create a list of students
students = []
students.append(Student("Ollie", "1234", 10))
students.append(Student("Peter", "3210", 23))
students.append(Student("Lena", "9999", 43))
students.append(Student("Tina", "3333", 8))
# Create a course named Introduction to Programming
itp = Course("Introduction to Programming", "itp1", 5)
# Add completed courses for each student, with grade 3 for all
completed = []
for student in students:
completed.append(CompletedCourse(student, itp, 3))
# Print out the name of the student for each completed course
for course in completed:
print(course.student.name)Ollie Peter Lena Tina
Что именно происходит со всеми точками в строке print(course.student.name)?
course— это экземпляр классаCompletedCoursestudentссылается на атрибут объектаCompletedCourse, который является объектом типаStudent- атрибут
nameв объектеStudentсодержит имя студента
Когда нужен импорт?
В примерах выше инструкция import появлялась довольно много раз:
from completedcourse import CompletedCourse
from course import Course
from student import Student
# rest of the main functionИнструкция import нужна только тогда, когда используется код, определенный где-то вне текущего файла (или вне текущей сессии интерпретатора Python). Сюда входят и ситуации, когда мы хотим использовать что-то, определенное в стандартной библиотеке Python. Например, модуль math содержит некоторые математические операции:
import math
x = 10
print(f"the square root of {x} is {math.sqrt(x)}")В примере выше мы предполагали, что три класса определены каждый в отдельном файле, а главная функция запускается из еще одного файла. Именно поэтому инструкции import были необходимы.
Если весь код программы написан в одном файле, как советуют делать в большинстве упражнений этого курса, вам не понадобятся инструкции import, чтобы использовать классы, которые вы определили.
Если вы ловите себя на том, что пишете что-то вроде
from person import Person
# more code goes hereскорее всего, что-то пошло не так. Если нужно освежить память, инструкция import впервые вводилась в части 7 материалов этого курса.
Список объектов как атрибут объекта
В примерах выше мы использовали отдельные экземпляры других классов как атрибуты: у Person есть один Pet как атрибут, а у CompletedCourse есть один Student и один Course как атрибуты.
В объектно-ориентированном программировании часто бывает так, что мы хотим иметь коллекцию объектов в качестве атрибута. Например, связь между спортивной командой и ее игроками следует именно этому шаблону:
class Player:
def __init__(self, name: str, goals: int):
self.name = name
self.goals = goals
def __str__(self):
return f"{self.name} ({self.goals} goals)"
class Team:
def __init__(self, name: str):
self.name = name
self.players = []
def add_player(self, player: Player):
self.players.append(player)
def summary(self):
goals = []
for player in self.players:
goals.append(player.goals)
print("Team:", self.name)
print("Players:", len(self.players))
print("Goals scored by each player:", goals)Пример работы нашего класса:
ca = Team("Campus Allstars")
ca.add_player(Player("Eric", 10))
ca.add_player(Player("Emily", 22))
ca.add_player(Player("Andy", 1))
ca.summary()Team: Campus Allstars Players: 3 Goals scored by each player: [10, 22, 1]
None: ссылка в никуда
В программировании на Python все инициализированные переменные ссылаются на объект. Однако неизбежно бывают ситуации, когда нужно сослаться на что-то, чего не существует, не вызывая ошибок. Ключевое слово None представляет именно такой "пустой" объект.
Продолжая пример с Team и Player выше, предположим, что мы хотим добавить метод для поиска игроков команды по имени игрока. Если такой игрок не найден, разумно вернуть None:
class Player:
def __init__(self, name: str, goals: int):
self.name = name
self.goals = goals
def __str__(self):
return f"{self.name} ({self.goals} goals)"
class Team:
def __init__(self, name: str):
self.name = name
self.players = []
def add_player(self, player: Player):
self.players.append(player)
def find_player(self, name: str):
for player in self.players:
if player.name == name:
return player
return NoneПроверим нашу функцию:
ca = Team("Campus Allstars")
ca.add_player(Player("Eric", 10))
ca.add_player(Player("Amily", 22))
ca.add_player(Player("Andy", 1))
player1 = ca.find_player("Andy")
print(player1)
player2 = ca.find_player("Charlie")
print(player2)Andy (1 goals) None
Однако будьте осторожны с None. Иногда он создает больше проблем, чем решает. Распространенная ошибка программирования — попытаться обратиться к методу или атрибуту через ссылку, которая имеет значение None:
ca = Team("Campus Allstars")
ca.add_player(Player("Eric", 10))
player = ca.find_player("Charlie")
print(f"Goals by Charlie: {player.goals}")Выполнение кода выше вызовет ошибку:
Хорошая идея — проверять значение на None перед тем, как пытаться обращаться к атрибутам или методам возвращаемых значений:
ca = Team("Campus Allstars")
ca.add_player(Player("Eric", 10))
player = ca.find_player("Charlie")
if player is not None:
print(f"Goals by Charlie: {player.goals}")
else:
print(f"Charlie doesn't play in Campus Allstars :(")Charlie doesn't play in Campus Allstars :(
Текущие баллы можно посмотреть в синем индикаторе в правом нижнем углу страницы.