Есть ли лучший способ проверить месяцы и дни для печати сезонов в python? Я не могу использовать def и класс или циклы

#python

Вопрос:

Я пишу программу, которая принимает дату в качестве входных данных и выводит сезон даты. Входными данными являются строка, представляющая месяц, и число int, представляющее день. Это для школьного проекта, хотя это работает, это кажется немного излишним, есть ли лучший способ получить те же результаты? без использования def, класса или циклов. Если нет, если бы вы могли просто дать мне знать, что это невозможно.

 input_month = input()
input_day = int(input())

spring = ['March', 'April', 'May', 'June']
summer = ['June', 'July', 'August', 'September']
autumn = ['September', 'October', 'November', 'December']
winter = ['December', 'January', 'February', 'March']
if input_month in spring   summer   autumn   winter:
    if (input_day > 31) or (input_day <= 0):
        print('Invalid')
    elif (input_month == spring[0]) and (input_day >= 20):
        print('Spring')
    elif (input_month == spring[1]) and (input_day <= 30):
        print('Spring')
    elif (input_month == spring[2]) and (input_day >= 31):
        print('Spring')
    elif (input_month == spring[3]) and (input_day <= 20):
        print('Spring')

    elif (input_month == summer[0]) and (input_day >= 21):
        print('Summer')
    elif (input_month == summer[1]) and (input_day <= 31):
        print('Summer')
    elif (input_month == summer[2]) and (input_day <= 31):
        print('Summer')
    elif (input_month == summer[3]) and (input_day <= 21):
        print('Summer')

    elif (input_month == autumn[0]) and (input_day >= 22):
        if input_day >= 31:
            print('Invalid')
        else:
            print('Autumn')
    elif (input_month == autumn[1]) and (input_day <= 31):
        print('Autumn')
    elif (input_month == autumn[2]) and (input_day <= 30):
        print('Autumn')
    elif (input_month == autumn[3]) and (input_day <= 20):
        print('Autumn')

    elif (input_month == winter[0]) and (input_day >= 21):
        print('Winter')
    elif (input_month == winter[1]) and (input_day <= 31):
        print('Winter')
    elif (input_month == winter[2]) and (input_day <= 28):
        print('Winter')
    elif (input_month == winter[3]) and (input_day <= 19):
        print('Winter')
    else:
        print('Invalid')
 

Комментарии:

1. используйте три отступа « для форматирования кода, а не три кавычки ''' . Я не уверен в отступе, все ли операторы elif имеют одинаковый уровень первого оператора if или второго? Если это первый случай, этот код не должен давать никаких результатов, и вы должны отредактировать его, чтобы показать правильный отступ. Кроме того, незначительные ошибки, такие как >=31 для мая и June могут быть 31 .

2. Логические выражения не работают так в Python: if input_month == spring or summer or autumn or winter . Поскольку input_month это строка, вы не можете напрямую сравнить ее равенство с a list строк. Скорее, вам нужно проверить if input_month in spring summer autumn winter . Он объединит все эти списки вместе в один большой список со всеми месяцами. Обратите внимание, что будут дубликаты, но на самом деле это не так уж и важно.

3. Я предлагаю также выполнить всю проверку ваших входных данных сразу после получения входных данных от пользователя, а не разбрасывать их по всей вашей основной логике.

4. можете ли вы привести мне пример, моя цель-посмотреть, смогу ли я сделать это проще. Или если это правильный способ сделать это.

5. если я изменю май и июнь на 31, не будет ли это означать, что если пользовательский ввод был в мае или июне с int ниже 31, он пройдет, потому что он будет ложным? Мне нужно разрешить даты до 31-го числа

Ответ №1:

Чтобы проверить, находится ли входной день в допустимом диапазоне, полезно использовать словарь для сопоставления каждого месяца и дней в этом месяце. Чтобы проверить, к какому сезону относится ввод, вы можете попытаться сравнить дни в году после преобразования границ сезона и ввода в него.

 month_days = {
    "January": 31, "February": 28, "March": 31, "April": 30,
    "May": 31, "June": 30, "July": 31, "August": 31,
    "September": 30, "October": 31, "November": 30, "December": 31
}

# cummulative year days at the start of that month
cum_year_days = {}
summ = 0
for m, d in month_days.items():
    cum_year_days[m] = summ
    summ  = d

# Or you can hard coded it like month_days
# cum_year_days = {
#     'January': 0, 'February': 31, 'March': 59, 'April': 90, 
#     'May': 120, 'June': 151, 'July': 181, 'August': 212, 
#     'September': 243, 'October': 273, 'November': 304, 'December': 334
# }

month = input("Month: ")
day = int(input("Day: "))

if month in month_days and 1 <= day <= month_days[month]:
    # convert input to year days
    year_days = cum_year_days[month]   day
    # convert season boundaries to year days in similar way
    if cum_year_days["March"]   20 <= year_days <= cum_year_days["June"]   20:
        print("Spring")
    elif cum_year_days["June"]   21 <= year_days <= cum_year_days["September"]   21:
        print("Summer")
    elif cum_year_days["September"]   22 <= year_days <= cum_year_days["December"]   20:
        print("Autumn")
    else:
        print("Winter")
else:
    print("Invalid")
 

Примечания к вашему коду и подробности в моем комментарии:

  1. В вашем исходном коде есть строка input_month == spring or summer or autumn or winter . Вы изменили его, но я хочу объяснить, почему это неправильно. Он читается как (input_month == spring) or summer or autumn or winter , первый сравнивает равенство между строкой и списком , так оно и будет False , и непустой список верен, поэтому будет возвращено следующее условие True . Следовательно, это происходит True потому, что у вас есть непустой список вместо допустимого ввода.
  2. Для мая у вас есть условие input_day >= 31 , поэтому с 1 по 30 мая все они недействительны. Так и должно быть <= .
  3. В июне у вас есть условие input_day >= 21 и ограничение с первого заявления if input_day > 31 , поэтому с 21 по 31 июня все они действительны, в то время как 31 июня нет. Вы можете проверить это как 21 <= input_day <= 30 .

Я обнаружил, что использование кортежа для представления даты и выполнение сравнения кортежей может быть более интуитивным.

 month_days = {
    "January": (1, 31), "February": (2, 28), "March": (3, 31), "April": (4, 30),
    "May": (5, 31), "June": (6, 30), "July": (7, 31), "August": (8, 31),
    "September": (9, 30), "October": (10, 31), "November": (11, 30), "December": (12, 31)
}

month = input("Month: ")
day = int(input("Day: "))

if month in month_days and 1 <= day <= month_days[month][1]:
    # convert input to date
    date = (month_days[month][0], day)
    # convert season boundaries to date
    if (3, 20) <= date <= (6, 20):
        print("Spring")
    elif (6, 21) <= date <= (9, 21):
        print("Summer")
    elif (9, 22) <= date <= (12, 20):
        print("Autumn")
    else:
        print("Winter")
else:
    print("Invalid")
 

Кортежи сравниваются лексикографически, то есть сравниваются элемент за элементом, немедленно возвращают результат, когда они не равны. В этом случае он сначала проверит месяц, более ранний месяц с меньшим значением будет < более поздним месяцем. Если месяцы равны, он проверит день, более приятный день с меньшим значением будет < позже. Если оба равны, то они равны == .

Комментарии:

1. FWIW, OP заявляет, что они не хотят использовать циклы.

2. это здорово, я просто не могу использовать циклы, потому что мы их еще не выучили. есть ли способ обойти цикл, но при этом сохранить код небольшим?

3. @DerekPruitt Единственная часть цикла for-это расчет совокупных дней года, и вы можете просто жестко закодировать его, как показано в моем комментарии к коду.

4. @DerekPruitt Я также добавляю альтернативу, используя кортеж для вашей справки.

Ответ №2:

Без использования def или циклов

Да, есть гораздо более простой способ сделать это: воспользоваться преимуществами встроенного dict типа данных. Вам нужно будет немного поработать, но вот один подход (из многих), который может помочь.

Более приятная структура данных

 seasons = {"January": ("winter",),
           "February": ("winter",),
           "March": ("winter", "spring"),
           "April": ("spring",),
           "May": ("spring",),
           "June": ("spring", "summer"),
           "July": ("summer",),
           "August": ("summer",),
           "September": ("summer", "autumn"),
           "October": ("autumn",),
           "November": ("autumn",),
           "December": ("autumn", "winter")}


# Equinox and solstice dates are a little fuzzy, fiddle with these if you want
transitions = {("winter", "spring"): 21,
               ("spring", "summer"): 21,
               ("summer", "autumn"): 23,
               ("autumn", "winter"): 21}


days_in_month = {"January": 31,
                 "February": 28,  # Not accounting for leap years
                 "March": 31,
                 "April": 30,
                 "May": 31,
                 "June": 30,
                 "July": 31,
                 "August": 31,
                 "September": 30,
                 "October": 31,
                 "November": 30,
                 "December": 31}
 

Now you can get user input

 # Get user input
month = input("enter month: ").lower().title()
day = int(input("enter day: "))


# Validate user input
if month not in seasons:
    print("invalid month")
elif not (1 <= day <= days_in_month[month]):
    print("invalid day")
 

One issue here is that if the month is invalid, the program doesn’t do anything about it.

The algorithm

Теперь, используя два seasons и transitions словари, реализуйте этот алгоритм (я оставлю эту часть на ваше усмотрение).:

  1. Получите «сезон», обратившись к season словарю с ключом, значение которого равно month . Это dict возвращает либо 1-кортеж, либо 2-кортеж, в зависимости от того, есть ли сезонный переход month .
  2. Если tuple возвращаемый при season поиске кортеж состоит из 1 (проверьте его длину), вы можете напрямую вернуть первое значение в tuple нем , которое будет соответствовать правильному сезону для этого месяца, и все готово. Если нет, перейдите к шагу 3.
  3. Если tuple возвращаемый при season поиске 2-кортеж, используйте tuple его в качестве ключа в transitions словаре. Это вернет целое число, день перехода сезона, которое вы можете назвать t_day .
  4. Сравните day с t_day . Если day меньше , чем t_day , верните первый элемент в 2-кортеж. В противном случае верните вторую.
  5. Сделано!

Пример:

Если month = March , то season так и будет ("winter", "spring") . Поскольку длина season кортежа равна 2, найдите то t_day , что будет 21 , так как весеннее равноденствие, как правило, считается наступившим 21 марта. Предположим теперь, что day = 13 . Поскольку 13 не больше или равно 21, верните season[0] , что есть "winter" .

Решение

Поскольку другие люди просто дают вам прямые ответы, я полагаю, что могу предоставить свои собственные (я надеялся побудить вас самостоятельно разобраться в базовом алгоритме).:

 season = seasons.get(month)
transition_day = transitions.get(season, 99)
season = season[day >= transition_day]
 

Это следует примерно той же логике, которую я изложил в разделе «Алгоритм», но использует тот факт, что bool это подкласс int , означающий, что вы можете указывать последовательности, используя логические значения, например: "ab"[False] -> "a" False == 0 ).

Используя .get() transition_day словарь и предоставляя запасное значение, мы можем просто проиндексировать season кортеж (который состоит из 1 или 2 элементов) с помощью логической проверки day >= transition_day . Если это логическое условие True , мы возвращаемся season[1] , в противном случае мы возвращаемся season[0] .

Причина, по которой это работает, даже если season есть только один элемент, заключается в том, .get() что он возвращается 99 , если ключ season отсутствует в transitions словаре. Это гарантирует , что day >= transition_day это всегда будет False , когда transition_day есть 99 , что означает, что вы гарантированно всегда будете звонить только season[False] == season[0] в том случае, если это season только 1 кортеж.

Открытие для использования def и циклов

Используя def , вы можете определить функции, которые разделяют логику вашего кода на отдельные части, которые все выполняют одну задачу. Это значительно помогает поддерживать код в чистоте, организованности и простоте изложения.

Используя while цикл, вы можете гарантировать, что пользовательский ввод всегда действителен и что ваш код не будет продолжен до тех пор, пока пользователь не предоставит действительный ввод.

Лучший способ работы с пользовательским вводом

Вот тебе халява. Здесь многое происходит, так что не торопитесь понимать, что делают эти две функции:

 def get_month():
    while True:
        month = input("enter month: ").lower().title()
        if month not in seasons:
            print("invalid month!")
            continue
        return month


def get_day(max_days):
    while True:
        try:
            day = int(input("enter day: "))
        except ValueError:
            print("invalid entry, day must be an integer!")
            continue
        if not (1 <= day <= max_days):
            print(f"invalid day, must be between 1 and {max_days}")
            continue
        return day
 

Encapsulating the season logic

You can do the same thing with the logic for determining the season :

 def get_season(month, day):
    season = seasons.get(month)
    transition_day = transitions.get(season, 99)
    return season[day >= transition_day]
 

Затем ваш основной сценарий сводится к трем вызовам функций:

 if __name__ == "__main__":
    # Get user input
    month = get_month()
    day = get_day(max_days=days_in_month[month])
    
    # Get season
    season = get_season(month, day)
    print(f"{month}, {day}: {season}")
 

Примечания

С помощью этого решения мы сократили 40 строк кода, необходимых для выполнения этой задачи if-elif-else , всего до трех строк кода (для основной логики определения сезона).

Путем обертывания вашей логики в функцию,

 def get_season(month, day):
    season = seasons.get(month)
    transition_day = transitions.get(season, 99)
    return season[day >= transition_day]
 

Теперь у вас есть три явно именованные функции: get_month() , get_day() , и get_season() . Эти функции делают совершенно ясным, какую задачу выполняет каждая из этих функций. Это то, о чем вы хотите думать при написании сценариев или разработке программного обеспечения. Он также известен как принцип единой ответственности и является чрезвычайно важной частью программирования.

Наконец, чтобы протестировать эту функцию и убедиться, что она работает должным образом, вы можете использовать этот небольшой фрагмент для перечисления всех 365 дней в году:

 for month, max_days in days_in_month.items():
    for day in range(1, max_days   1):
        season = get_season(month, day)
        print(f"{month}, {day}: {season}")
    print()
 

Комментарии:

1. Это здорово, я просто еще не выучил петли защиты, это будет на следующей неделе. Также с максимальными днями, если пользователь ввел, скажем, 43, вернет ли это ошибку или сработает, мне нужно убедиться, что они не могут ввести более 31 за день.

2. С get_month функциями get_day и нет, программа не будет работать, пока пользователь не предоставит действительный ввод.

3. Но в первом фрагменте кода, где я определил словари, есть очень простая проверка входных данных.

4. Спасибо за разъяснение, это, похоже, охватывает большую часть материала, который я узнал до сих пор. Я также признателен за раздел, посвященный открытию возможностей использования def и петель. Мне придется еще раз взглянуть на это на следующей неделе, когда мы будем освещать это.

5. @DerekPruitt если бы мой ответ помог вам, были бы вы так любезны принять его (щелкнув галочку)? Таким образом, ваш вопрос будет удален из очереди без ответа. Рад, что смог помочь!