Код в Python работает неправильно
Первая проблема заключается в том, что код слишком большой. А вторая проблема в том, что переменная должна уменьшиться только один раз, а она уменьшается 3 раза.
list = ['MacBook'], ['iPhone'], ['iPad'], ['AirPods'], ['AppleWatch'], ['iMac'] moneys = 10000 buy = input('Введите товар который хотите купить: ') price = int(input('Введите цену за которую хотите купить товар: ')) if buy == 'MacBook' or 'iPhone': print ('Вы купили ', buy, 'за', price, '$') moneys -= price print (moneys, '$ у вас осталось после покупки') if buy == 'iPad' or 'AirPods': print ('Вы купили ', buy, 'за', price, '$') moneys -= price print (moneys, '$ у вас осталось после покупки') if buy == 'AppleWatch' or 'iMac': print ('Вы купили ', buy, 'за', price, '$') moneys -= price print (moneys, '$ у вас осталось после покупки') else: print('Товара нет в наличии.') if price < 1500: print ('Цена слишком маленькая.')
Подскажите пожалуйста, как можно решить эти проблемы.
Отслеживать
задан 22 янв в 11:40
27 4 4 бронзовых знака
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
- У вас условия выглядит так:
if buy == 'MacBook' or 'iPhone': print ('Вы купили ', buy, 'за', price, '$') if buy == 'iPad' or 'AirPods': print ('Вы купили ', buy, 'за', price, '$') if buy == 'AppleWatch' or 'iMac': print ('Вы купили ', buy, 'за', price, '$')
Для питона условие if "Какая-то строка" всегда будет истиной, поэтому надо уточнять:
if buy == 'MacBook' or buy == 'iPhone': print ('Вы купили ', buy, 'за', price, '$') if buy == 'iPad' or buy == 'AirPods': print ('Вы купили ', buy, 'за', price, '$') if buy == 'AppleWatch' or buy == 'iMac': print ('Вы купили ', buy, 'за', price, '$')
Но так мы делать не будем, так как все возможные товары у нас лежат в списке list, поэтому, правильно сделать так:
list = ['MacBook', 'iPhone', 'iPad', 'AirPods', 'AppleWatch', 'iMac'] moneys = 10000 buy = input('Введите товар который хотите купить: ') price = int(input('Введите цену за которую хотите купить товар: ')) if price > 1500: if buy in list: print (f'Вы купили за $') moneys -= price print (f'$ у вас осталось после покупки') else: print('Товара нет в наличии.') else: print ('Цена слишком маленькая.')
Программа не работает. Что делать?
Моя программа не работает! Что делать? В данной статье я постараюсь собрать наиболее частые ошибки начинающих программировать на python 3, а также расскажу, как их исправлять.
Проблема: Моя программа не запускается. На доли секунды появляется чёрное окошко, а затем исчезает.
Причина: после окончания выполнения программы (после выполнения всего кода или при возникновении исключения программа закрывается. И если вы её вызвали двойным кликом по иконке (а вы, скорее всего, вызвали её именно так), то она закроется вместе с окошком, в котором находится вывод программы.
Решение: запускать программу через IDLE или через консоль.
Проблема: Не работает функция input. Пишет SyntaxError.
Пример кода:
Причина: Вы запустили Python 2.
Проблема: Где-то увидел простую программу, а она не работает.
Пример кода:
Ошибка:
Причина: Вам подсунули программу на Python 2.
Решение: Прочитать об отличиях Python 2 от Python 3. Переписать её на Python 3. Например, данная программа на Python 3 будет выглядеть так:
Проблема: TypeError: Can't convert 'int' object to str implicitly.
Пример кода:
Причина: Нельзя складывать строку с числом.
Решение: Привести строку к числу с помощью функции int(). Кстати, заметьте, что функция input() всегда возвращает строку!
Проблема: SyntaxError: invalid syntax.
Пример кода:
Ошибка:
Причина: Забыто двоеточие.
Проблема: SyntaxError: invalid syntax.
Пример кода:
Ошибка:
Причина: Забыто равно.
Проблема: NameError: name 'a' is not defined.
Пример кода:
Причина: Переменная "a" не существует. Возможно, вы опечатались в названии или забыли инициализировать её.
Решение: Исправить опечатку.
Проблема: IndentationError: expected an indented block.
Пример кода:
Причина: Нужен отступ.
Проблема: TabError: inconsistent use of tabs and spaces in indentation.
Пример кода:
Ошибка:
Причина: Смешение пробелов и табуляции в отступах.
Решение: Исправить отступы.
Проблема: UnboundLocalError: local variable 'a' referenced before assignment.
Пример кода:
Ошибка:
Причина: Попытка обратиться к локальной переменной, которая ещё не создана.
Проблема: Программа выполнилась, но в файл ничего не записалось / записалось не всё.
Пример кода:
Причина: Не закрыт файл, часть данных могла остаться в буфере.
Проблема: Здесь может быть ваша проблема. Комментарии чуть ниже 🙂
Также вам может быть полезно это описание:
Для вставки кода на Python в комментарий заключайте его в теги
- Модуль csv - чтение и запись CSV файлов
- Создаём сайт на Django, используя хорошие практики. Часть 1: создаём проект
- Онлайн-обучение Python: сравнение популярных программ
- Книги о Python
- GUI (графический интерфейс пользователя)
- Курсы Python
- Модули
- Новости мира Python
- NumPy
- Обработка данных
- Основы программирования
- Примеры программ
- Типы данных в Python
- Видео
- Python для Web
- Работа для Python-программистов
- Сделай свой вклад в развитие сайта!
- Самоучитель Python
- Карта сайта
- Отзывы на книги по Python
- Реклама на сайте
Глючный код на Python: 10 самых распространенных ошибок, которые допускают разработчики
Python — это интерпретируемый, объектно-ориентированный язык программирования высокого уровня с динамической семантикой. Встроенные структуры данных высокого уровня в сочетании с динамической типизацией и динамическим связыванием делают его очень привлекательным для БРПС (быстрой разработки прикладных средств), а также для использования в качестве скриптового и связующего языка для подключения существующих компонентов или сервисов. Python поддерживает модули и пакеты, тем самым поощряя модульность программы и повторное использование кода.
О данной статье
Простота и легкость в освоении данного языка может ввести разработчиков в заблуждение (особенно тех, кто еще только начинает изучать Python), так что можно упустить из виду некоторые важные тонкости и недооценить силу разнообразия возможных решений с помощью Python.
Имея это в виду, в этой статье представлен «топ-10» тонких, трудных для обнаружения ошибок, которые могут допустить даже продвинутые разработчики Python.
Ошибка № 1: неправильное использование выражений в качестве значений по умолчанию для аргументов функций
Python позволяет указывать, что у функции могут быть необязательные аргументы, путем задания для них значения по умолчанию. Это, конечно, очень удобная особенность языка, но может привести к неприятным последствиям, если тип такого значения будет изменяемым. Например, рассмотрим следующее определение функции:
>>> def foo(bar=[]): # bar - это необязательный аргумент # и по умолчанию равен пустому списку. . bar.append("baz") # эта строка может стать проблемой. . return bar
Распространенная ошибка в данном случае — это думать, что значение необязательного аргумента будет устанавливаться в значение по умолчанию каждый раз, как функция будет вызываться без значения для этого аргумента. В приведенном выше коде, например, можно предположить, что повторно вызывая функцию foo() (то есть без указания значения для агрумента bar), она всегда будет возвращать «baz», поскольку предполагается, что каждый раз, когда вызывается foo () (без указания аргумента bar), bar устанавливается в [ ] (т. е. новый пустой список).
Но давайте посмотрим что же будет происходить на самом деле:
>>> foo() ["baz"] >>> foo() ["baz", "baz"] >>> foo() ["baz", "baz", "baz"]
А? Почему функция продолжает добавлять значение по умолчанию «baz» к существующему списку каждый раз, когда вызывается foo(), вместо того, чтобы каждый раз создавать новый список?
Ответом на данный вопрос будет более глубокое понимание того, что творится у Python «под капотом». А именно: значение по умолчанию для функции инициализируется только один раз, во время определения функции. Таким образом, аргумент bar инициализируется по умолчанию (т. е. пустым списком) только тогда, когда foo() определен впервые, но последующие вызовы foo() (т. е. без указания аргумента bar) продолжат использовать тот же список, который был создан для аргумента bar в момент первого определения функции.
Для справки, распространенным «обходным путем» для этой ошибки является следующее определение:
>>> def foo(bar=None): . if bar is None: # or if not bar: . bar = [] . bar.append("baz") . return bar . >>> foo() ["baz"] >>> foo() ["baz"] >>> foo() ["baz"]
Ошибка № 2: неправильное использование переменных класса
Рассмотрим следующий пример:
>>> class A(object): . x = 1 . >>> class B(A): . pass . >>> class C(A): . pass . >>> print A.x, B.x, C.x 1 1 1
Вроде все в порядке.
>>> B.x = 2 >>> print A.x, B.x, C.x 1 2 1
Ага, все как и ожидалось.
>>> A.x = 3 >>> print A.x, B.x, C.x 3 2 3
Что за черт?! Мы же только изменили A.x. Почему же C.x тоже изменилось?
В Python переменные класса обрабатываются как словари и следуют тому, что часто называют Порядком разрешения методов (MRO). Таким образом, в приведенном выше коде, поскольку атрибут x не найден в классе C, он будет найден в его базовых классах (только A в приведенном выше примере, хотя Python поддерживает множественное наследование). Другими словами, C не имеет своего собственного свойства x, независимого от A. Таким образом, ссылки на C.x фактически являются ссылками на A.x. Это будет вызывать проблемы, если не обрабатывать такие случаи должным образом. Так что при изучении Python обратите особое внимание на аттрибуты класса и работу с ними.
Ошибка № 3: неправильное указание параметров для блока исключения
Предположим, что у вас есть следующий кусок кода:
>>> try: . l = ["a", "b"] . int(l[2]) . except ValueError, IndexError: # To catch both exceptions, right? . pass . Traceback (most recent call last): File "", line 3, in IndexError: list index out of range
Проблема здесь заключается в том, что выражение except не принимает список исключений, указанных таким образом. Скорее, в Python 2.x выражение «except Exception, e» используется для привязки исключения к необязательному второму заданному второму параметру (в данном случае e), чтобы сделать его доступным для дальнейшей проверки. В результате в приведенном выше коде исключение IndexError не перехватывается выражением except; скорее, вместо этого исключение заканчивается привязкой к параметру с именем IndexError.
Правильный способ перехвата нескольких исключений с помощью выражения except — указать первый параметр в виде кортежа, содержащего все исключения, которые нужно перехватить. Кроме того, для максимальной совместимости используйте ключевое слово as, так как этот синтаксис поддерживается как в Python 2, так и в Python 3:
>>> try: . l = ["a", "b"] . int(l[2]) . except (ValueError, IndexError) as e: . pass . >>>
Ошибка № 4: непонимание правил области видимости Python
Область видимости в Python основана на так называемом правиле LEGB, которое является аббревиатурой Local (имена, назначенные любым способом внутри функции (def или lambda), и не объявленные глобальными в этой функции), Enclosing (имя в локальной области действия любых статически включающих функций (def или lambda), от внутреннего к внешнему), Global (имена, назначенные на верхнем уровне файла модуля, или путем выполнения global инструкции в def внутри файла), Built-in (имена, предварительно назначенные в модуле встроенных имен: open, range, SyntaxError . ). Кажется достаточно просто, верно? Ну, на самом деле, есть некоторые тонкости в том, как это работает в Python, что подводит нас к общей более сложной проблеме программирования на Python ниже. Рассмотрим следующей пример:
>>> x = 10 >>> def foo(): . x += 1 . print x . >>> foo() Traceback (most recent call last): File "", line 1, in File "", line 2, in foo UnboundLocalError: local variable 'x' referenced before assignment
Вышеуказанная ошибка возникает потому, что, когда вы присваиваете переменную в области видимости, Python автоматически считает ее локальной для этой области и скрывает любую переменную с аналогичным именем в любой вышестоящей области.
Таким образом, многие удивляются, когда получают UnboundLocalError в ранее работающем коде, когда он модифицируется путем добавления оператора присваивания где-нибудь в теле функции.
Эта особенность особенно сбивает разработчиков с толку при использовании списков. Рассмотрим следующий пример:
>>> lst = [1, 2, 3] >>> def foo1(): . lst.append(5) # Это работает нормально. . >>> foo1() >>> lst [1, 2, 3, 5] >>> lst = [1, 2, 3] >>> def foo2(): . lst += [5] # . а вот это падает! . >>> foo2() Traceback (most recent call last): File "", line 1, in File "", line 2, in foo UnboundLocalError: local variable 'lst' referenced before assignment
А? Почему foo2 падает, в то время как foo1 работает нормально?
Ответ такой же, как в предыдущем примере, но, по распространенному мнению, здесь ситуация более тонкая. foo1 не применяет оператор присваивания к lst, тогда как foo2 — да. Помня, что lst + = [5] на самом деле является просто сокращением для lst = lst + [5], мы видим, что мы пытаемся присвоить значение lst (поэтому Python предполагает, что он находится в локальной области видимости). Однако значение, которое мы хотим присвоить lst, основано на самом lst (опять же, теперь предполагается, что он находится в локальной области видимости), который еще не был определен. И мы получаем ошибку.
Ошибка № 5: изменение списка во время итерации по нему
Проблема в следующем куске кода должна быть достаточно очевидной:
>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> for i in range(len(numbers)): . if odd(numbers[i]): . del numbers[i] # BAD: Deleting item from a list while iterating over it . Traceback (most recent call last): File "", line 2, in IndexError: list index out of range
Удаление элемента из списка или массива во время итерации по нему — это проблема Python, которая хорошо известна любому опытному разработчику программного обеспечения. Но, хотя приведенный выше пример может быть достаточно очевидным, даже опытные разработчики могут встать на эти грабли в гораздо более сложном коде.
К счастью, Python включает в себя ряд элегантных парадигм программирования, которые при правильном использовании могут привести к значительному упрощению и оптимизации кода. Дополнительным приятным следствием этого является то, что в более простом коде вероятность попасться на ошибку случайного удаления элемента списка во время итерации по нему значительно меньше. Одна из таких парадигм — генераторы списков. Кроме того, понимание работы генераторов списков особенно полезны для избежания этой конкретной проблемы, как показано в этой альтернативной реализацией приведенного выше кода, которая прекрасно работает:
>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all >>> numbers [0, 2, 4, 6, 8]
Ошибка № 6: непонимание того, как Python связывает переменные в замыканиях
Рассмотрим следующий пример:
>>> def create_multipliers(): . return [lambda x : i * x for i in range(5)] >>> for multiplier in create_multipliers(): . print multiplier(2) .
Вы можете ожидать следующий вывод:
0 2 4 6 8
Но на самом деле вы получите вот что:
8 8 8 8 8
Это происходит из-за поздней привязки в Python, которое заключается в том, что значения переменных, используемых в замыканиях, ищутся во время вызова внутренней функции. Таким образом, в приведенном выше коде всякий раз, когда вызывается какая-либо из возвращаемых функций, значение i ищется в окружающей области видимости во время ее вызова (а к тому времени цикл уже завершился, поэтому i уже был присвоен конечный результат — значение 4).
Решение этой распространенной проблемы с Python будет таким:
>>> def create_multipliers(): . return [lambda x, i=i : i * x for i in range(5)] . >>> for multiplier in create_multipliers(): . print multiplier(2) . 0 2 4 6 8
Вуаля! Мы используем здесь аргументы по умолчанию для генерации анонимных функций для достижения желаемого поведения. Некоторые назвали бы это решение элегантным. Некоторые —
тонким. Некоторые ненавидят подобные штуки. Но если вы разработчик Python, в любом случае, это важно понимать.
Ошибка № 7: создание циклических зависимостей модуля
Допустим, у вас есть два файла, a.py и b.py, каждый из которых импортирует другой, следующим образом:
import b def f(): return b.x print f()
import a x = 1 def g(): print a.f()
Сначала попробуем импортировать a.py:
>>> import a 1
Сработало просто отлично. Возможно, это вас удивляет. В конце концов, модули циклически импортируют друг друга и это, вероятно, должено быть проблемой, не так ли?
Ответ заключается в том, что простое наличие циклического импорта модулей само по себе не является проблемой в Python. Если модуль уже был импортирован, Python достаточно умен, чтобы не пытаться повторно импортировать его. Однако, в зависимости от точки, в которой каждый модуль пытается получить доступ к функциям или переменным, определенным в другом, вы действительно можете столкнуться с проблемами.
Итак, возвращаясь к нашему примеру, когда мы импортировали a.py, у него не было проблем с импортом b.py, поскольку b.py не требует, чтобы что-либо из a.py было определено во время его импорта. Единственная ссылка в b.py на a — это вызов a.f(). Но этот вызов в g() и ничего в a.py или b.py не вызывает g(). Так что все работает прекрасно.
Но что произойдет, если мы попытаемся импортировать b.py (без предварительного импорта a.py, то есть):
>>> import b Traceback (most recent call last): File "", line 1, in File "b.py", line 1, in import a File "a.py", line 6, in print f() File "a.py", line 4, in f return b.x AttributeError: 'module' object has no attribute 'x'
Ой-ой. Это не хорошо! Проблема здесь в том, что в процессе импорта b.py он пытается импортировать a.py, который, в свою очередь, вызывает f(), который пытается получить доступ к b.x. Но b.x еще не было определено. Отсюда исключение AttributeError.
По крайней мере, одно из решений этой проблемы довольно тривиально. Просто измените b.py, чтобы импортировать a.py в g():
x = 1 def g(): import a # This will be evaluated only when g() is called print a.f()
Теперь, когда мы его импортируем, все нормально:
>>> import b >>> b.g() 1 # Printed a first time since module 'a' calls 'print f()' at the end 1 # Printed a second time, this one is our call to 'g'
Ошибка № 8: пересечение имен с именами модулями стандартной библиотеки Python
Одна из прелестей Python — это множество модулей, которые поставляются «из коробки». Но в результате, если вы сознательно не будете за этим следить, можно столкнуться с тем, что имя вашего модуля может быть с тем же именем, что и модуль в стандартной библиотеке, поставляемой с Python (например, в вашем коде может быть модуль с именем email.py, который будет конфликтовать со модулем стандартной библиотеки с таким же именем).
Это может привести к серьезным проблемам. Например, если какой-нибудь из модулей будет пытаться импортировать версию модуля из стандартной библиотеки Python, а у вас в проекте будет модуль с таким же именем, который и будет по ошибке импортирован вместо модуля из стандартной библиотеки.
Поэтому следует проявлять осторожность, чтобы не использовать те же имена, что и в модулях стандартной библиотеки Python. Гораздо проще изменить название модуля в своем проекте, нежели подать запрос на изменение имени модуля в стандартной библиотеке и получить на него одобрение.
Ошибка № 9: неспособность учесть различия Python 2 и Python 3
Рассмотрим следующий файл foo.py:
import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e) bad()
На Python 2 он отработает нормально:
$ python foo.py 1 key error 1 $ python foo.py 2 value error 2
Но теперь давайте посмотрим как он будет работать в Python 3:
$ python3 foo.py 1 key error Traceback (most recent call last): File "foo.py", line 19, in bad() File "foo.py", line 17, in bad print(e) UnboundLocalError: local variable 'e' referenced before assignment
Что здесь только что произошло? «Проблема» в том, что в Python 3 объект в блоке исключения недоступен за его пределами. (Причина этого заключается в том, что в противном случае объекты в этом блоке будут сохраняться в памяти до тех пор, пока сборщик мусора не запустится и не удалит ссылки на них оттуда).
Один из способов избежать этой проблемы — сохранить ссылку на объект блока исключения за пределами этого блока, чтобы он оставался доступным. Вот версия предыдущего примера, которая использует эту технику, тем самым получая код, который подходит как для Python 2, так и для Python 3:
import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def good(): exception = None try: bar(int(sys.argv[1])) except KeyError as e: exception = e print('key error') except ValueError as e: exception = e print('value error') print(exception) good()
Запустим его в Python 3:
$ python3 foo.py 1 key error 1 $ python3 foo.py 2 value error 2
Ошибка № 10: неправильное использование метода __del__
Допустим, у вас есть вот такой файл mod.py:
import foo class Bar(object): . def __del__(self): foo.cleanup(self.myhandle)
И вы пытаетесь сделать вот такое из другого another_mod.py:
import mod mybar = mod.Bar()
И получите ужасный AttributeError.
Почему? Потому что, как сообщается здесь, когда интерпретатор отключается, глобальные переменные модуля все имеют значение None. В результате в приведенном выше примере, в момент вызова __del__, имя foo уже было установлено в None.
Решением этой «задачи со звездочкой» будет использование atexit.register(). Таким образом, когда ваша программа завершает выполнение (то есть при нормальном выходе из нее), ваши handle'ы удаляются до того, как интерпретатор звершает работу.
С учетом этого, исправление для приведенного выше кода mod.py может выглядеть примерно так:
import foo import atexit def cleanup(handle): foo.cleanup(handle) class Bar(object): def __init__(self): . atexit.register(cleanup, self.myhandle)
Подобная реализация обеспечивает простой и надежный способ вызова любой необходимой очистки после обычного завершения программы. Очевидно, что решение о том, как поступить с объектом, который связан с имненем self.myhandle, остается за foo.cleanup, но, думаю, идею вы поняли.
Заключение
Python — это мощный и гибкий язык со множеством механизмов и парадигм, которые могут значительно повысить производительность. Однако, как и в случае с любым программным инструментом или языком, при ограниченном понимании или оценке его возможностей, при разработке могут возникать непредвиденные проблемы.
Ознакомление с нюансами Python, затронутыми в этой статье, поможет оптимизировать использование языка, избегая при этом некоторых распространенных ошибок.
Код с собеседования: как купить подешевле и продать подороже?
Вот типичная задачка на собеседованиях в ИТ-компании. Если вы решите её, будете королём собеседований (или королевой):
Есть список цен, сколько стоил некий товар в разные дни. Например, это ценная бумага. Нужно написать код, который скажет, в какой день нужно было покупать, а в какой — продавать этот товар, чтобы получить максимальную прибыль. Сначала товар нужно купить, потом продать. Если цены идут так, что прибыли не получается, — тоже сообщить об этом.
Примеры списка и ответов:
prices = [7,1,5,3,6,4] ← максимальная прибыль 5, если купить по цене 1 во второй день, а продать за 6 на пятый день;
prices = [7,6,4,3,1] ← прибыли нет (потому что цена всё время снижается).
Логика решения
Разобьём алгоритм на такие этапы:
- Гигиена и безопасность. Проверяем, есть ли вообще цены в списке. Если нет — выводим сообщение.
- Если цены есть, находим максимальную прибыль, которую можно получить. Для этого находим разницу между минимальным и максимальным значением, которые идут именно в таком порядке, слева направо.
- Если прибыль в итоге равна нулю — тоже выводим сообщение, что нет смысла ничего покупать.
- Если прибыль больше нуля — выводим дни, когда нужно покупать и продавать.
Первые три пункта простые, а в четвёртом есть подвох.
Проверяем, есть ли цены в списке
Начнём решение с очевидного: если цен в списке нет, то выводим сообщение про это и останавливаем скрипт:
# список цены на товар в разные дни prices = [7,1,5,1,9,4] # если список цен пустой — выводим сообщение if len(prices) == 0: print('Цен нет') # останавливаем скрипт exit()
Если мы поменяем список цен на пустой ( prices = [] ) и запустим скрипт, то получим сообщение, что там нет цен:

Если цены есть — находим прибыль
Теперь, раз цены есть, посчитаем прибыль. Для этого нам понадобятся три переменные:
- profit — тут будет наша максимальная прибыль, которую можно получить;
- minIndex — номер элемента с наименьшей ценой;
- minBuy — минимальная цена, по которой нужно купить товар для максимальной прибыли.
Сделаем простой цикл, где на каждом шаге мы будем брать очередную пару цен и смотреть — будет ли с ними прибыль больше, чем раньше. Если будет — запоминаем прибыль и номер элемента с минимальной ценой. Добавим новый код к предыдущему условию:
# список цены на товар в разные дни prices = [7,1,5,1,9,4] # если список цен пустой - выводим сообщение if len(prices) == 0: print('Цен нет') # останавливаем скрипт exit() # если цены есть else: # прибыль на старте нулевая profit = 0 # номер элемента с наименьшей ценой minIndex = 0 # первая цена пусть будет на старте минимальной minBuy = prices[0] # перебираем все цены for i in range(len(prices)): # находим прибыль на текущем шаге # если она больше предыдущей — берём новую прибыль if profit < prices[i] - minBuy: profit = prices[i] - minBuy # запоминаем позицию минимальной цены minIndex = prices.index(min(minBuy, prices[i])) # устанавливаем минимальное значение, если текущее меньше минимального minBuy = min(minBuy, prices[i])
Сообщаем о нулевой прибыли
Здесь всё просто: если прибыль равна нулю, то выводим сообщение и тоже останавливаем программу:
# если прибыль нулевая
if profit == 0:
# выводим сообщение и останавливаем программу
print('Нет оптимальных дней для покупки')
exit()
Выводим оптимальные дни для покупки и продажи
А вот на этом этапе на собеседованиях часто допускают ошибку: раз мы знаем прибыль и минимальную стоимость, то легко узнать максимальную стоимость, сложив эти два числа. И раз мы знаем теперь эту стоимость, то можно найти индекс в списке по значению элемента.
Ошибка здесь в том, что это работает только для случаев, когда в списке максимальная нужная стоимость стоит после минимальной. Но если она встречается два раза — до и после минимальной, — то такая логика даёт сбой.
Допустим, у нас список цен выглядит так:
Семёрка здесь встречается два раза — в начале и в середине. Если мы будем использовать такую логику, то команда [prices.index[profit+prices[minIndex]] вернёт только первый найденный элемент, а это будет неправильно.
Чтобы избежать этой ошибки, нам нужно проверять, стоит ли индекс максимальной стоимости после минимальной. Если стоит — его можно заменить на ноль, чтобы он не мешал при дальнейших расчётах. Для этого сделаем копию списка цен и будем менять значения в нём, чтобы не трогать исходные цены:
# копируем список цен prices_copy = prices # перебираем все цены for i in range(len(prices)): # находим первый индекс максимальной цены sell = prices_copy.index(profit+prices[minIndex]) # если максимальная цена стоит раньше минимальной if sell < minIndex: # меняем её на нулевую, чтобы не учитывать её на следующих шагах, и переходим к следующему шагу цикла prices_copy[sell] = 0 pass # если максимальная цена стоит после минимальной — останавливаем цикл, мы нашли то, что хотели else: break # выводим результаты print('Покупка: на ' + str(minIndex+1) + ' день по цене ' + str(prices[minBuy])) print('Продажа: на ' + str(sell+1) + ' день по цене ' + str(prices_copy[sell])) print('Прибыль — ' +str(profit))
При выводе мы прибавляем единицу для того, чтобы компенсировать отчёт элементов списка с нуля — компьютер считает так, а мы считаем с единицы.
Запустим скрипт с исходными данными (7,1,5,1,9,4) и посмотрим, что получилось:

Готовый код
# список цены на товар в разные дни prices = [7,1,5,1,9,4] # если список цен пустой - выводим сообщение if len(prices) == 0: print('Цен нет') # останавливаем скрипт exit() # если цены есть else: # прибыль на старте нулевая profit = 0 # номер элемента с наименьшей ценой minIndex = 0 # первая цена пусть будет на старте минимальной minBuy = prices[0] # перебираем все цены for i in range(len(prices)): # находим прибыль на текущем шаге # если она больше предыдущей — берём новую прибыль if profit < prices[i] - minBuy: profit = prices[i] - minBuy # запоминаем позицию минимальной цены minIndex = prices.index(min(minBuy, prices[i])) # устанавливаем минимальное значение, если текущее меньше минимального minBuy = min(minBuy, prices[i]) # если прибыль нулевая if profit == 0: # выводим сообщение и останавливаем программу print('Нет оптимальных дней для покупки') exit() # копируем список цен prices_copy = prices # перебираем все цены for i in range(len(prices)): # находим первый индекс максимальной цены sell = prices_copy.index(profit+prices[minIndex]) # если максимальная цена стоит раньше минимальной if sell < minIndex: # меняем её на нулевую, чтобы не учитывать её на следующих шагах, и переходим к следующему шагу цикла prices_copy[sell] = 0 pass # если максимальная цена стоит после минимальной — останавливаем цикл, мы нашли то, что хотели else: break # выводим результаты print('Покупка: на ' + str(minIndex+1) + ' день по цене ' + str(prices[minBuy])) print('Продажа: на ' + str(sell+1) + ' день по цене ' + str(prices_copy[sell])) print('Прибыль — ' +str(profit))
Любишь Python? Зарабатывай на нём!
Изучите самый модный язык программирования и станьте крутым бэкенд-разработчиком. Старт — бесплатно.

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
