Найти - Пользователи
Полная версия: Задачи
Начало » Python для новичков » Задачи
1 2 3
ZerG
Я бы сказал тут просматривается некий предыдущий опыт программирования
Как будто бы на паскале
Palrom
py.user.next
Тут писал подборку материалов.
Тут выполнил один пример от и до.
Как всегда спасибо. Я как-то порывался уже купить Кушниренко для математиков, но не нашёл в продаже, книжки старые уже, 88г., скачал, пробежался по оглавлению и пара сомнений закралось: а не устарела ли инфа? Фортран, который там используется для практики сильно отличается от питона? или просто скипать всю практику?

py.user.next
Много у тебя всяких непитонячих приёмов
ZerG
тут просматривается некий предыдущий опыт программирования
Та нет, нету опыта по большому счёту. С комплюхтером-то знаком давно, делаю на нём всякое, мультимедийное, околоигровое и музыкальное, но во взрослый кодинг полез только два месяца назад. До этого, разве что за Джаву брался тройку лет назад и бросил, да в скриптах поднаторел слегка, на проприетарном недо-языке для игр серии TES. https://tesall.ru/tutorials/the-elder-scrolls-modding/modostroenie-oblivion/485-obse-v-massi
Оттуда может и остались привычки какие-нибудь.
py.user.next
у тебя функции такие маленькие, а в них уже можно запутаться.
Это, думаю, следствие того, что я, не будь дурак, специально сделал так, чтобы некоторые функции могли ходить к одной и той же с разными аргументами и та в свою очередь выдавала разные же результаты. Просто походы такие совершаются у меня иногда в самых неожиданных местах, да, из-за этого получаются спагетти).
Я уже понял, это всё недостатки проектирования, которого не было, вообще. На этой программе я нащупал предел “оперативки” мозга походу. Мне не удалось удержать во внимании всю программу и я не вывез, вернее вывез но не туда)



py.user.next
Palrom
Я как-то порывался уже купить Кушниренко для математиков, но не нашёл в продаже, книжки старые уже, 88г., скачал, пробежался по оглавлению и пара сомнений закралось: а не устарела ли инфа? Фортран, который там используется для практики сильно отличается от питона? или просто скипать всю практику?
Там не используется FORTRAN. Там используется псевдокод на русском языке. Видно, что ты вообще её не открывал, потому что там FORTRAN'а нет нигде, только в конце книги он привёл пример реализации на FORTRAN'е чего-то там. И там главное не код, а концепции, которые он раскрывает.

И её не надо покупать. Ты просто её скачиваешь и занимаешься по ней. Можешь распечатать разные главы в разное время на лазерном принтере. Это дёшево, там всего 150 листов примерно получается. Лазерный принтер печатает с одной кассеты несколько тысяч листов и никогда не высыхает, как струйные. Он может полгода стоять, через полгода его запускаешь, а он печатает, как будто кассету только купили. А в струйных ты через полгода запускаешь его, а он говорит “кассета пустая”, ты думаешь “как пустая?! я же её новую купил хрен знает когда”, смотришь, а эта хуйня высохла и нужно новую брать за хер знает сколько денег. Одну страничку распечатал, через полгода опять всё повторяется. Ну, там есть принтеры с какими-то костылями, чтобы оно там не пересыхало, прочистки какие-то там. Но вот это всё - наличие всей этой мишуры - говорит о том, что струйные принтеры - это говно. Так что бери лазерный принтер чёрно-белый и распечатывай себе хоть какие книжки.

Всё, что там прочитаешь, можно написать на питоне. Но псевдокод, который тебе придётся выучить (есть русский, есть английский), тем и хорош, что с него можно записывать на множестве разных языков, а сам он не требует точного соблюдения множества деталей синтаксиса. Поэтому с одного и того же псевдокода можно записать код и на FORTRAN'е в 1988 году, и на Rust'е в 2022 году. Псевдокод никогда не исчезнет из программирования. Хоть 3000-й год наступит, там всё равно будут использовать псевдокод. Это фундаментальная вещь. И ты ею не владеешь, вот в чём прикол. Английский псевдокод найдёшь на Wikipedia, там его много.

Питон приближен к псевдокоду, его часто используют в роли псевдокода, но питон не выражает всех конструкций языка программирования, которые возможны. А псевдокод выражает вообще всё. Всё, что может быть в любом языке программирования, выражаемо в псевдокоде. Поэтому его нужно выучить и пользоваться им. Чем больше ты им будешь пользоваться, тем точнее и правильнее он у тебя будет получаться. Сначала ты его будешь записывать, на бумажке там, на доске маркерной и так далее, а потом он у тебя будет представляться уже в воображении. И так в воображении можно будет программы писать. Ты лежишь на кровати, смотришь в потолок и пишешь программу на псевдокоде. Написал - встал, пошёл и записал её в комп, переписал из воображения в комп. Вот так это работает. Программирование - это не посиделки за компом, это нечто большее.

Palrom
Это, думаю, следствие того, что я, не будь дурак, специально сделал так, чтобы некоторые функции могли ходить к одной и той же с разными аргументами и та в свою очередь выдавала разные же результаты.
Ну, вот это хрень всё. Там есть правила создания функций и всё это в книжке написано. Как их делать, какие аргументы куда, как что называть. Она не должна выдавать разные результаты. Она должна иметь слабое предусловие и сильное постусловие. И это нужно соблюдать для того, чтобы потом эту функцию присобачивать не только внутри этой программы к другим её появляющимся частям, но и чтобы эту функцию можно было перенести в другие программы, которых на момент написания этой функции ещё не существовало. Потом ты это применяешь уже не к функциям, а к целым программам. И таким образом становится возможным одну программу использовать вообще для чего-то другого, для чего она не планировалась изначально.

У меня вот был пример. Я написал себе программу для вскрытия браузера и вытаскивания из него внутренностей, чтобы потом можно было его открыть и восстановить предыдущее содержимое (то есть не хранить историю в браузере, а достать историю из браузера и сохранить её, но сам браузер при этом очистить). Короче, всё работало несколько месяцев, потом приходит пацан и просит программу, которая бы у него в другом браузере что-то там доставала изнутри, из-под капота. Так я посмотрел и свою программу просто немножко поменял в двух местах и она стала для его браузера работать. Хотя тот браузер внутренне очень сильно отличался от первоначального, для которого эта программа писалась. Просто слабое предусловие программы как функции сработало и программу стало возможно применять к разным входным объектам, про которые ты при первичной разработке даже не задумывался. В этом суть слабого предусловия функции - сделать её вход максимально общим и подходящим для большего множества входных данных. Это повышает коэффициент её переиспользования (как внутри программы, так и снаружи). В итоге программу я ему написал минут за двадцать, просто переделав немного свою готовую программу. А свою эту первую программу я писал там дня два примерно. Если бы я ему с нуля её писал, то тоже ушло бы дня три, а так минут двадцать заняло. Вот это время, сэкономленное соблюдением правила.

Там вообще много таких моментов. Со временем и с опытом применения они начинают работать на тебя, проги получаются быстрее, точнее, надёжнее, ты становишься более уверенным в своих гипотезах, они срабатывают сразу и точно. А без опыта и знаний ты, действительно, не знаешь куда сунуть какую букву, чтобы всё не поломалось ещё больше, чем сейчас.
FishHook
Окей, я обещал ревью. Твой код Palrom написал целиком в процедурном стиле, на это мы не будем обращать внимания, ни слова про ООП не будет. Процедурный, так процедурный, давай посмотрим, насколько он процедурный. И сразу первая функция

Претензия №1
 def goods_demo(demo_type=0, product=None):
    p = 'Товар:'
    s = 'Состав: '
    c = 'Цена за 100 гр.: '
    k = 'Количество (г): '
    if demo_type == 0:
        for i in goods:
            print(i)
        return
    elif demo_type == 1:
        return f'{s}{goods[product][0]}'
    elif demo_type == 2:
        return f'{c}{goods[product][1]}'
    elif demo_type == 3:
        return f'{k}{goods[product][2]}'
    elif demo_type == 4:
        return f'{s}{goods[product][0]}\n' \
               f'{c}{goods[product][1]}\n' \
               f'{k}{goods[product][2]}'
    elif demo_type == 6:
        print('\nОстаток кондитерской:')
        for i in goods:
            print(f'{p} "{i}" {k}{goods[i][2]}')
        return
В питоне нет синтаксического различия между функциями и процедурами, давай обозначим различия умозрительно. Подпрограммы могут быть:
1. Чистые функции. Это такие подпрограммы, которые возвращают детерминированный результат и не имеют побочных эффектов. Чистые функции хорошо тестируются, из них можно собирать цепочки функций с предсказуемым результатом, они легко кешируются и т.д.
2. Чистые процедуры. Подпрограммы которые созданы, чтобы иметь побочный эффект. Пример - функция print. Она не может быть частью цепочки вычислений, она только изменяет состояние потока вывода. Это и есть её побочный эффект.
3. Процедуры с результатом. Например, процедура, которая создаёт новую запись в базе данных, помимо своего побочного эффекта возвращает идентификатор новой записи

Вернёмся к goods_demo. Эта функция при разных аргументах меняет свою сущность - иногда она чистая процедура, так как не делает ничего кроме принта, а иногда она функция, так как только возвращает значение. При этом, я вообще не понимаю как она работает. Вот у нас есть пример её использования в коде

     
def ask_action_type(product):
    ....
    return goods_demo(action, product)
и далее
             
   
user_interest = ask_action_type(user_choice)
if user_interest == 5:
То есть даже если закрыть глаза на неопределенное поведение функции (то печатает в консоль, то возвращает строку) это какая-то ерунда, потому что в коде мы ждем целое число, которого функция не возвращает никогда.

Разбираем эту функцию дальше. Какая же у неё сигнатура
     
def goods_demo(demo_type=0, product=None):
ну то есть вот такой вызов вполне законен
   
def goods_demo(product="Печеньки"):
на что нам эта функция выполнит вот это
   
    if demo_type == 0:
        for i in goods:
            print(i)
        return
О как! Какую бы ерунду я не поместил в product если demo_type не указан, функция будет делать одно и то же. Что совсем лишает её смысла

И самое загадочное в этой функции это “Тайна загадочной пятерки” ибо при demo_type == 5 функция не делает вообще ничего.

Претензия №2
Магические числа. Программа построена вокруг эксплуатации каких-то целых чисел, значения которых для алгоритма не важны, но при этом невозможно понять какая дополнительная смысловая нагрузка на них лежит не вычитывая код
    
if action == 5:
   return action
elif 1 <= action <= 4:
   return goods_demo(action, product)
очевидно, что это некие пункты меню. И так же очевидно, что если я поменяю порядок следования этих пунктов, то программу придется переписывать. Как бы это могло выглядеть более человечно

    
if action == BUY:
   return action
elif action in (PRICE, AMOUNT...):
   return goods_demo(action, product)

Претензия №3
1.
Структура данных. Понятно, что программа учебная и потому допустимы некоторые условности. Было бы чересчур требовать здесь реальную базу данных. Мы считаем, что goods и my_cart заменяют нам БД. Что в этой БД плохо? То что первичным ключом вы сделали потенциально изменяемое поле. Если я переименую “Торт” в “Торт кремовый” это один и тот же товар или уже другой?
2.
   
def add_to_cart(product, price, weight):
    if product in my_cart.keys():
        my_cart[product][0] += price
    else:
        my_cart.update({product: [price, weight]})
Упси! А как так? У нас только что была структура из трех элементов
'торт': ['мука, яйцо, сливки, шоколад', 150, 2000]
а добавляете вы новый продукт и элементов только два, при этом везде в программе вы получаете данные по индексу.
3.
Изменение структуры БД приведет к полному переписыванию программы. У вас это вообще ярко представлено, программа это каша из интерфейсного кода, доступа к данным и бизнес-логики. Должны быть четко разделенные слои: слой представления, слой доступа к данным, слой бизнес-логики

Претензия №4
В коде есть ошибки
1.
 if product in my_cart.keys():
временная сложность получения значения по ключу для хэш-таблицы O(1) а перебор итератора это O(n). Зачем вы это сделали?
2.
   
def check_goods():
    global goods
    new = goods.copy()
    for i in goods:
        if goods[i][2] == 0:
            new.pop(i)
    goods = new.copy()
а теперь объясните, пожалуйста, зачем нужны .copy()? Вы же изменяете глобальную переменную
 goods = new.copy()
то есть вы делаете копию переменной, изменяете эту копию, потом делаете копию с копии, и в итоге присваиваете изначальной переменной указатель на эту копию копии. Зачем??? Что мешает сразу изменять целевую переменную?

Ну и как уже заметили выше, код не питонячий.

Palrom
FishHook
Ого, спасибо! И впрямь развёрнуто. Общий посыл понял, тут даже спорить не о чем, а по некоторым частностям, попробую объясниться:
FishHook
объясните, пожалуйста, зачем нужны .copy()?
Функция check_goods(): это вообще “зашквар”, это я полностью признаю. Если в б/д будет не 3 товара а миллион, то эта функция будет вешать программу на пару секунд при каждом проходе цикла в main(). Это криминал, но другого я не придумал. Чехарда с копированием словаря нужна мне была для того, чтобы проходить по исходному циклом for. Если итерировать и подчищать goods напрямую, то вылезет ошибка “изменение длины во время итерации”. Я просто пробовал несколько подходов, и такой, с копированием оказался самый компактный и понятный..
FishHook
временная сложность получения значения по ключу для хэш-таблицы O(1) а перебор итератора это O(n).
Не догоняю.. Я ведь просто пробегаюсь по списку ключей словаря goods с помощью метода keys. Я где-то начитался, что метод работает очень быстро и для подобных целей нужно использовать именно его. Понятно, что для проверки членства программа встаёт, условно, на паузу и перебирает все ключи, но, а как иначе это можно было бы реализовать? (p.s. Про Биг-О я знаю, когда изучал алгоритмы, сталкивался с этим)
FishHook
а добавляете вы новый продукт и элементов только два, при этом везде в программе вы получаете данные по индексу. Изменение структуры БД приведет к полному переписыванию программы.
Да, если список со значениями будет иметь меньше элементов, то всё, кранты. К созданию списка товаров нужно просто не подпускать криворукого манагера, или сделать функцию, которая проверяла бы словарь на соответствие.
FishHook
Если я переименую “Торт” в “Торт кремовый” это один и тот же товар или уже другой?
Другой.
В продаже имеется:
торт кремовый
пирожное
маффин
Какой из товаров вас интересует?
>>> торт
Такого товара нет, введите товар из списка выше.
Какой из товаров вас интересует?
>>> торт кремовый
Что вы хотели бы узнать о товаре?
(1)-состав, (2)-цена, (3)-количество, (4)-вся инф., (5)-покупка
>>>

FishHook
очевидно, что это некие пункты меню. И так же очевидно, что если я поменяю порядок следования этих пунктов, то программу придется переписывать.
А не надо менять порядок следования) Нужно добавить функционал? Дописывайте дальше по порядку.. Это то самое “Интерфейсную часть от логики надо отделять всегда”, может я её организовал криво-косо, но преследовал именно эту цель.

…to be continued.

Palrom
FishHook
То есть даже если закрыть глаза на неопределенное поведение функции (то печатает в консоль, то возвращает строку) это какая-то ерунда, потому что в коде мы ждем целое число, которого функция не возвращает никогда.
Palrom
специально сделал так, чтобы некоторые функции могли ходить к одной и той же с разными аргументами и та в свою очередь выдавала разные же результаты.
Да, функция просто игнорирует аргумент product если demo_type не требует его для отработки кода внутри функции. Меня уже размотали по этому поводу) По началу эта функция показалась мне удачным решением..
py.user.next
Ну, вот это хрень всё.
FishHook
И самое загадочное в этой функции это “Тайна загадочной пятерки” ибо при demo_type == 5 функция не делает вообще ничего.
А “код 5” к функции goods_demo уже не имеет никакого отношения. 5 отрабатывает в main:
 user_interest = ask_action_type(user_choice)
            if user_interest == 5:
                buy(user_choice)

FishHook
я обещал ревью
В целом, спасибо за анализ, большое, человеческое. Пока разбирал программу свежим взглядом, нащупал опять свою старую проблему, на которую мне указывал py.user.next ещё месяц назад, и от которой, я думал избавился: мои функции очень жёстко завязаны друг на друга, и если хоть в одной что-то не так, и она не подготовила как следует данные для другой функции, то всё, обвал.
Яркий тому пример: функция ask_action_type() принимаeт аргумент product только для того, что бы передать его в goods_demo(), и не дай бог ask_product() заблаговременно не проверит этот самый product на нахождение в словаре goods.. И таких зависимостей в программе много.. Пошёл дальше изучать вопрос.

[бормочет] А ведь во всяких гикбрейнсах обещают ажно вход в профессию за 3-6 месяцев..



Palrom
py.user.next
Ты лежишь на кровати, смотришь в потолок и пишешь программу на псевдокоде. Написал - встал, пошёл и записал её в комп, переписал из воображения в комп. Вот так это работает. Программирование - это не посиделки за компом, это нечто большее.
Жена у меня уже оценила. Пока едем в лифте она мне трижды проговаривает какую-то новость и только на четвёртый вопрос “Что ты думаешь по этому поводу?” я очухиваюсь и перестаю гонять цикл в цикле какой нибудь. Но не смотря на негодование, книжку “Программирование для математиков” мне нашла на озоне, заказала уже.
FishHook
Palrom
Я просто пробовал несколько подходов, и такой, с копированием оказался самый компактный и понятный..

 def check_goods():
    global goods
    goods = {k: v for k, v in goods.items() if v[2] != 0}

непонятно зачем это вообще надо. Первоначально у вас нет товаров с нулевым остатком. Состояние склада меняется по запросу пользователя в функции buy, вот там и удаляйте запись если остаток 0

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

неправильно
     if product in my_cart.keys():
        my_cart[product][0] += price

правильно
     if product in my_cart:
        my_cart[product][0] += price

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

Другой.
Здорово! То есть если у меня в корзине есть товар Торт, а Торт переименовали, то у меня в корзине торт превратился в ошибку времени выполнения. Идентификатор должен быть уникальным и неизменяемым

Palrom
А не надо менять порядок следования)
это не программист решает, а заказчик, скажет поменять - поменяешь

Palrom
Это то самое “Интерфейсную часть от логики надо отделять всегда”
Твой код противоречит этому правилу. Вот смотри

 BUY = "buy"
GET_AMOUNT = "get_amount"
GET_INFO = "info"
MENU = [
   BUY,
   GET_AMOUNT,
   GET_INFO
]
TRANSLATION = {
    BUY: "Купить", 
    GET_AMOUNT: "Остаток товара", 
    GET_INFO: "Товары в наличии"
}
def ask_action():
    for i, m in enumerate(MENU):
        print(i, TRANSLATION[m])
    return MENU[int(input())]
def buy():
   pass
def get_amount():
   pass
def get_info():
   pass
actons = {
    BUY: buy,
    GET_AMOUNT: get_amount,
    GET_INFO: get_info
}
def main():
    action = ask_action()
    procedure = actions[action]    
    procedure()

можешь теперь сортировать меню как угодно, переводить его на китайский язык и добавлять/удалять пункты меню не изменяя существующие функции
FishHook
Palrom
А “код 5” к функции goods_demo уже не имеет никакого отношения. 5 отрабатывает в main:

функции - это независимые повторно используемые подпрограммы. Грубо говоря, я ожидаю, что функцию можно импортировать в другой модуль и применить к каким-то совершенно другим данным. У тебя функции - это просто код сгруппированный в пространства имён более-менее относительно последовательности исполнения, это не независимые юниты
FishHook
Palrom
А ведь во всяких гикбрейнсах обещают ажно вход в профессию за 3-6 месяцев..
вот, а в ВУЗе пять лет учатся дураки, теорию какую-то изучают, математику какую-то дурацкую, надо на гикбрейнсах учиться. Не знаю, как словарь работает? Да и пофиг
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB