Есть много способов решить такую проблему. Многие представляют собой компромисс между простотой (и, возможно, эффективностью), с одной стороны, и удобством (или "magi c"), с другой. Иногда лучше всего быть прямым и явным, даже если это кажется неуклюжим. и связанные с ними driver
экземпляры. На основании вашего вопроса я делаю вывод, что водитель может одновременно находиться только в одной команде, и, если возможно, вы хотите, чтобы это выполнялось как можно «автоматически». Я не уверен, хотите ли вы, чтобы код обеспечивал соблюдение «не более двух гонщиков на команду», что может приводить к ошибке, если вы попытаетесь добавить третьего, поэтому я пока оставлю этот аспект.
class Team:
_membership = {} # Stores all teams and their members at the class level.
# The underscore indicates it's only intended to be used
# internally by the Team class.
def __init__(self, name, drivers=None):
self.name = name
Team._membership[self] = [] # Create an entry in the dictionary, with no drivers
if drivers is None:
drivers = []
for driver in drivers:
self.add_driver(driver) # Defined below
def __repr__(self):
"""Define how to represent this object in output."""
return f"<Team '{self.name}', drivers: {self.drivers}>"
Нам нужно реализовать этот add_driver()
метод. Он проверит, находится ли драйвер в настоящее время в другой команде, и если да, удалит его там:
def add_driver(self, driver):
old_team = Team.find_team_for_driver(driver) # Defined below
if old_team == self:
# If the driver is already on this team, we're done.
return
if old_team is not None:
# If it was on a different team, remove it from that team.
old_team.drivers.remove(driver)
# Lastly, add it to this team.
Team._membership[self].append(driver)
find_team_for_driver()
не привязан к конкретному экземпляру c, а является методом для общего учета класса Team
, так что это метод класса :
@classmethod
def find_team_for_driver(cls, driver):
for team, drivers in cls._membership.items():
if driver in drivers:
return team
# Return None if the driver was not found in a team.
return None
Мы можем использовать декоратор свойств , чтобы упростить просмотр, какие драйверы команда has:
@property
def drivers(self):
return Team._membership[self]
@drivers.setter
def drivers(self, drivers):
Team._membership[self] = []
for driver in drivers:
Team.update_membership(self, driver)
Это означает, что мы все еще можем использовать простой синтаксис team.drivers
, а внутренние компоненты класса Team
будут обрабатывать его выборку из словаря _membership
. Обратите внимание, что, хотя заманчиво думать, что вы могли бы добавить драйверов в команду с team.drivers.append(new_driver)
, они не будут знать, что нужно удалить драйвер из своей предыдущей команды, если таковой имеется. Обязательно используйте вместо него team.add_driver(new_driver)
.
(обратите внимание, что team.drivers.remove(driver_to_remove)
работает , но мы могли бы определить метод remove_driver()
, если хотите, просто для согласованности.)
Хорошо, мы, наконец, добрались до класса Driver
, который мало что изменился. (Я удалил все атрибуты, кроме name
, просто чтобы сделать этот пример кода немного проще.) Мы можем использовать свойство, чтобы узнать, в какой команде находится драйвер, а также переназначить его новой команде:
class Driver:
def __init__(self, name):
self.name = name
def __repr__(self):
return f"<Driver '{self.name}'>"
@property
def team(self):
return Team.find_team_for_driver(self)
@team.setter
def team(self, team):
team.add_driver(self)
Тестирование:
bob = Driver('Bob')
lisa = Driver('Lisa')
kaya = Driver('Kaya')
red = Team('Red Team', [bob, lisa])
print(red)
=> <Team 'Red Team', drivers: [<Driver 'Bob'>, <Driver 'Lisa'>]>
blue = Team('Blue Team')
blue.add_driver(kaya)
print(blue)
=> <Team 'Blue Team', drivers: [<Driver 'Kaya'>]>
Переназначение драйверов:
lisa.team = blue
red.add_driver(kaya)
print(red)
=> <Team 'Red Team', drivers: [<Driver 'Bob'>, <Driver 'Kaya'>]>
print(blue)
=> <Team 'Blue Team', drivers: [<Driver 'Lisa'>]>
Такая инфраструктура может быть полезна для проекта определенного размера и сложности. Для более мелких и простых вещей вам, вероятно, лучше явно определять и устанавливать связи между объектами. (Обратите внимание, что все итерации, хотя Team._membership
медленнее, чем ссылка на явные атрибуты объекта. Во многих случаях этого может быть недостаточно, чтобы иметь значение.)
На другом конце спектра, как Проект становится более сложным, вместо того, чтобы реализовывать все больше и больше этого материала, существующая система, такая как Django, начинает иметь больше смысла. Общая цель Django - быть веб-фреймворком, но даже если вы не делаете ничего связанного с сетью, его объектно-реляционная система реализует всевозможные отношения (один-к-одному, один-ко-многим, многие-ко-многим), с множеством удобных опций, таких как двунаправленность и обеспечение уникальности. См. Этот вопрос , если вы дойдете до точки, где захотите попробовать это. (Я считаю, что SQLAlchemy реализует нечто подобное, но я сам не пробовал.)