У меня есть эти два класса, и они должны оставаться отдельными классами, но я чувствую, что это можно было бы реализовать более эффективно и с меньшим количеством повторяющегося кода. После того, как я погуглил, я все еще не нашел лучшего способа, так что, возможно, кто-то здесь может указать мне. В идеале я хочу получить метод tick () от Clock и просто добавить к нему будильник, а также получить метод set_time () с дополнительными атрибутами для установки времени будильника.
class Clock:
def __init__(self, hours, minutes, seconds):
self.set_time(hours, minutes, seconds)
def set_time(self, hours, minutes, seconds):
if hours < 0:
self.hours = 0
elif hours > 23:
self.hours = 23
else:
self.hours = hours
if minutes < 0:
self.minutes = 0
elif minutes > 59:
self.minutes = 59
else:
self.minutes = minutes
if seconds < 0:
self.seconds = 0
elif seconds > 59:
self.seconds = 59
else:
self.seconds = seconds
def tick(self):
while True:
print(str(self.hours).zfill(2), ":", str(self.minutes).zfill(2), ":", str(self.seconds).zfill(2), sep="")
self.seconds += 1
time.sleep(1)
if self.seconds == 60:
self.seconds = 0
self.minutes += 1
if self.minutes == 60:
self.minutes = 0
self.hours += 1
if self.hours == 24:
self.hours = 0
class AlarmClock(Clock):
def __init__(self, hours, minutes, seconds, alarm_hr, alarm_min, alarm_sec):
super().__init__(hours, minutes, seconds)
self.set_alarm(alarm_hr, alarm_min, alarm_sec)
def set_alarm(self, alarm_hr, alarm_min, alarm_sec):
if alarm_hr < 0:
self.alarm_hr = 0
elif alarm_hr > 23:
self.alarm_hr = 23
else:
self.alarm_hr = alarm_hr
if alarm_min < 0:
self.alarm_min = 0
elif alarm_min > 59:
self.alarm_min = 59
else:
self.alarm_min = alarm_min
if alarm_sec < 0:
self.alarm_sec = 0
elif alarm_sec > 59:
self.alarm_sec = 59
else:
self.alarm_sec = alarm_sec
def alarm(self):
while True:
print(str(self.hours).zfill(2), ":", str(self.minutes).zfill(2), ":", str(self.seconds).zfill(2), sep="")
self.seconds += 1
time.sleep(1)
if self.seconds == 60:
self.seconds = 0
self.minutes += 1
if self.minutes == 60:
self.minutes = 0
self.hours += 1
if self.hours == 24:
self.hours = 0
if self.hours == self.alarm_hr and self.minutes == self.alarm_min and self.seconds == self.alarm_sec:
print("ALARM ALARM ALARM ALARM ALARM!!!!")
break
2 ответа
Я реорганизовал часть вашего кода, обратите внимание, что я игнорирую уже существующие библиотеки, так как предполагаю, что существует конкретная причина (образовательная или иная), стоящая за внедрением Clock
, tick
и т. д. таким образом самостоятельно.
класс Часы:
class Clock:
def __init__(self, hours: int, minutes: int, seconds: int):
self.hours = self.normalize_time_in_range(hours, 0, 23)
self.minutes = self.normalize_time_in_range(minutes, 0, 59)
self.seconds = self.normalize_time_in_range(seconds, 0, 59)
@staticmethod
def normalize_time_in_range(value: int, lower_bound: int, upper_bound: int) -> int:
if value < lower_bound:
return lower_bound
elif value > upper_bound:
return upper_bound
else:
return value
def start_ticking(self):
while True:
self.tick()
def tick(self):
print(f"{str(self.hours).zfill(2)}:{str(self.minutes).zfill(2)}:{str(self.seconds).zfill(2)}")
self.seconds += 1
time.sleep(1)
if self.seconds == 60:
self.seconds = 0
self.minutes += 1
if self.minutes == 60:
self.minutes = 0
self.hours += 1
if self.hours == 24:
self.hours = 0
- normalize_value_in_range: Обратите внимание, что у вас много дублированной логики в
set_time
иset_alarm
. Большая часть логики может быть извлечена из этой более общей реализации. - start_ticking и отметьте: В вашем случае я счел полезным отделить цикл от фактической логики тиков, чтобы вы могли изменять условия цикла в подклассах при повторном использовании
tick
. Вы увидите, почему это полезно вAlarmClock
. - Аннотации типов: Как вы можете видеть в заголовках методов, я добавил несколько подсказок по типам для вновь добавленных методов. Как правило, это хорошая идея для документирования кода для себя и других.
класс AlarmClock:
class AlarmClock(Clock):
def __init__(self, hours, minutes, seconds, alarm_hr, alarm_min, alarm_sec):
super().__init__(hours, minutes, seconds)
self.alarm_hr = self.normalize_value_in_range(alarm_hr, 0, 23)
self.alarm_min = self.normalize_value_in_range(alarm_min, 0, 59)
self.alarm_sec = self.normalize_value_in_range(alarm_sec, 0, 59)
self.ticking = False
def start_ticking(self):
self.ticking = True
while self.ticking:
self.tick()
def tick(self):
super().tick()
if self.hours == self.alarm_hr and self.minutes == self.alarm_min and self.seconds == self.alarm_sec:
print("ALARM ALARM ALARM ALARM ALARM!!!!")
self.ticking = False
- normalize_value_in_range: Теперь мы можем повторно использовать этот метод для установки времени будильника.
- start_ticking и отметьте: Поскольку мы отделили цикл от фактического тика, мы можем использовать
Clock.tick()
в нашем AlarmClock без необходимости дублировать код. Добавление атрибутаself.ticking
кAlarmClock
позволяет нам выйти из цикла между методами. Причина, по которой мы не могли повторно использоватьtick()
раньше было потому, что он содержал бесконечноеwhile True
цикл, тем самым не позволяя нам перехватывать, когда достигается alarm_time.
время учебы:
Наконец, я бы предложил отделить логику времени от логики часов. Это делает часы более лаконичными и позволяет расширить функциональность Time
класс по мере необходимости. Можно также сказать, что это позволяет часам сосредоточиться на их основных функциях. Опять же, я игнорирую существование библиотек для этой задачи, поскольку предполагаю, что вы хотите реализовать всю логику самостоятельно. Специализированные библиотеки, такие как datetime
заслуживают внимания, поскольку они обеспечивают множество удобных функций. Одна реализация может быть следующей:
class Time:
def __init__(self, hours: int, minutes: int, seconds: int):
self.hours = self.normalize_value_in_range(hours, 0, 23)
self.minutes = self.normalize_value_in_range(minutes, 0, 59)
self.seconds = self.normalize_value_in_range(seconds, 0, 59)
@staticmethod
def normalize_value_in_range(value: int, lower_bound: int, upper_bound: int) -> int:
if value < lower_bound:
return lower_bound
elif value > upper_bound:
return upper_bound
else:
return value
def increment(self):
self.seconds += 1
if self.seconds == 60:
self.seconds = 0
self.minutes += 1
if self.minutes == 60:
self.minutes = 0
self.hours += 1
if self.hours == 24:
self.hours = 0
def __eq__(self, other):
return self.hours == other.hours and self.minutes == other.minutes and self.seconds == other.seconds
def __str__(self):
return f"{str(self.hours).zfill(2)}:{str(self.minutes).zfill(2)}:{str(self.seconds).zfill(2)}"
class Clock:
def __init__(self, hours: int, minutes: int, seconds: int):
self.time = Time(hours, minutes, seconds)
def start_ticking(self):
while True:
self.tick()
def tick(self):
print(self.time)
self.time.increment()
time.sleep(1)
class AlarmClock(Clock):
def __init__(self, hours, minutes, seconds, alarm_hr, alarm_min, alarm_sec):
super().__init__(hours, minutes, seconds)
self.alarm_time = Time(alarm_hr, alarm_min, alarm_sec)
self.ticking = False
def start_ticking(self):
self.ticking = True
while self.ticking:
self.tick()
def tick(self):
super().tick()
if self.time == self.alarm_time:
print("ALARM ALARM ALARM ALARM ALARM!!!!")
self.ticking = False
Ваш проект является хорошей иллюстрацией общего принципа написания программного обеспечения: внутреннее хранилище данных (например, как класс Clock отслеживает время) и внешнее представление (например, как время должно быть напечатано для пользователя) — это два разные вещи.
Ваш код можно значительно упростить, сохранив данные в самом простом возможном формате: всего в секундах. Только когда это необходимо для пользы пользователей, вы беспокоитесь о том, чтобы собрать время в HH:MM:SS
формат. Если мы воспользуемся этим подходом, вам понадобится метод для преобразования часов, минут, секунд в общее количество секунд. И нам также нужен способ конвертировать другое направление. Для краткости здесь я игнорирую валидацию:
import time
class Clock:
def __init__(self, hours, minutes, seconds):
# Just store total seconds.
self.total_secs = self.hms_to_seconds(hours, minutes, seconds)
def hms_to_seconds(self, hours, minutes, seconds):
# Simple converter.
return hours * 3600 + minutes * 60 + seconds
# Properties to retrieve user-facing values when needed.
@property
def hours(self):
return self.total_secs // 3600
@property
def minutes(self):
return (self.total_secs - (3600 * self.hours)) // 60
@property
def seconds(self):
return self.total_secs - 3600 * self.hours - 60 * self.minutes
def tick(self):
# Make tick do less, so you can re-use it.
print(f'{self.hours:>02}:{self.minutes:>02}:{self.seconds:>02}')
self.total_secs += 1
time.sleep(1)
def run(self):
# A basic clock just ticks forever.
while True:
self.tick()
class AlarmClock(Clock):
def __init__(self, hours, minutes, seconds, alarm_hr, alarm_min, alarm_sec):
super().__init__(hours, minutes, seconds)
self.alarm_secs = self.hms_to_seconds(alarm_hr, alarm_min, alarm_sec)
def run(self):
# An alarm clock ticks and alarms.
while True:
self.tick()
if self.total_secs >= self.alarm_secs:
print("ALARM ALARM ALARM ALARM ALARM!!!!")
break
AlarmClock(1, 15, 0, 1, 15, 5).run()