company_banner

Объяснение задачи на Python с собеседования

Автор оригинала: Artem Rys
  • Перевод
Салют, хабровчане! В преддверии запуска нового потока по курсу «Web-разработчик на Python» хотим поделиться новым полезным переводом. Поехали!



Снова сходив на несколько собеседований и пройдя тестовые задания, я заметил, что интервьюерам нравятся задания наподобие следующего.

def f(x, l=[]):
    for i in range(x):
        l.append(i * i)
    return l
>>> f(2)
>>> f(3, [0, 1, 2])
>>> f(3)


Вопрос: Что выведет этот код?

Вывод первых двух строк достаточно очевиден, однако результат выполнения третьей строки f(3) не показался мне таким однозначным. Давайте посмотрим, что происходит после инициализации функции f. Чтобы запустить этот код, я воспользуюсь IPython.

>>> f
<function __main__.f(x, l=[])>
>>> f.__defaults__
([],)


Пустой список, который мы видим из результата выполнения f.__defaults__ — это переменная l в коде функции.

>>> f(2)
[0, 1]


Ничего особенного.

>>> f
<function __main__.f(x, l=[0, 1])>
>>> f.__defaults__
([0, 1],)


Однако! Теперь мы видим, что переменная l имеет значение [0, 1] в силу изменчивости объекта списка в Python и передачи аргументов функции в качестве ссылки.

>>> f(3, [0, 1, 2])
[0, 1, 2, 0, 1, 4]
>>> f
<function __main__.f(x, l=[0, 1])>


Тоже ничего особенного. Просто передача объекта list в качестве переменной l.

>>> f(3)
[0, 1, 0, 1, 4]
>>> f
<function __main__.f(x, l=[0, 1, 0, 1, 4])>


А вот теперь самое интересное. Когда вы запускаете f(3), Python не использует пустой список, который определен в коде функции, он использует переменную l со значениями из f.__defaults__ ([0, 1]).

P.S.

Если вам нужна функция, которая использует пустой список после каждого вызова, вам следует использовать что-то вроде этого (установить значение ‘l’ в ‘None’).

def f(x, l=None):
    if l is None:
        l = []
    for i in range(x):
        l.append(i * i)
    return l
>>> f(2)
[0, 1]
>>> f(3, [0, 1, 2])
[0, 1, 2, 0, 1, 4]
>>> f(3)
[0, 1, 4]


Заключение


Вот я и разобрал одну из самых популярных тестовых задач на собеседовании. Этот пост призван показать, что вы не всегда можете полагаться на свою интуицию, впрочем, как и я на свою :-).

Надеемся данный перевод станет полезным для вас. Традиционно ждем комментарии и до встречи на курсе.
OTUS. Онлайн-образование
556,57
Цифровые навыки от ведущих экспертов
Поделиться публикацией

Похожие публикации

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

    +15
    Боюсь подумать, сколько на хабре уже было статей, где упоминался этот злосчастный пустой список в качестве аргумента по-умолчанию в функции.
    По-моему неделю назад подобная была.

    P.S. Прошу прощения, даже меньше недели назад.
      0
      >>> f(2)
      [0, 1]
      >>> f(3, [0, 1, 2])
      [0, 1, 2, 0, 1, 4]   <-- Мне непонятен этот вывод.
      

      1) Откуда тут 4-ка? Если у нас range от 0 до 3 не включительно, т.е. 0,1,2.
      2) Почему порядок 2, 0, 1, 4, а не 0, 1, 2, 4? Команда append добавляет элемент в конце списка.

      Спасибо
        0
        А статью читать не пробовали? Тут фишка в том, что когда вы пишете default-значение в коде, то это не значит, что то, что вы там навычисляли — будет передаваться на вход функции. Оно будет вычислено, да. Один раз. И сохранено в скрытой переменной (на самом деле не слишком — до неё можно даже добраться). Откуда и будет доставаться каждый раз.

        Потому функция не будет получать пустой список на вход в третьем вызове — там будет то, что оставить в этой переменной первый вызов!

        P.S. И да, это «невнятное открытие», почему-то, делается регулярно.
          0
          т.е. функция на вызове f(3) взяла значение l из первого вызова f(2), а вызов f(3, [0,1,2]) как бы не считается.
          Если так, то стало понятно. Благодарю.
            0
            Да, при вызовах типа f(2) будет браться дефолтный list, который постоянно дополняется, а при вызовах типа f(3, [0, 1, 2]) будет использоваться переданный list. Вообще лучше подобным не пользоваться, можно ошибок наделать, да и вообще не такая очевидная вещь, нужно знать устройство языка изнутри.
              0
              Вопрос не в том «стоит ли это использовать или нет» (Style Guide, как правило, такие выкрутасы запрещает — вот, например, версия от Google). Вопрос в том «почему эта конструкция запрещена».

              Ведь рекомендованный вариант:
              def foo(a, b: Optional[Sequence] = None):
                  if b is None:
                      b = []
              
              всяко более сложный и замороченный. И вообще вызывает вопросы. Ведь понятно, что
              def foo(a, b: Sequence = []):
                  b = []
              
              это короче и проще… так зачем мы тут огород городим?

              Вполне осмысленный вопрос с учётом того, что в других языках (C++, JavaScript, PHP и другие) — эта же конструкция вполне безопасна и разумна.
            0
            Возможно я недопонял, но похоже, что на вход функции будет подаваться новое вычисленное значение каждый раз при вызове функции.
            >>> f(3)
            [0, 1, 0, 1, 4]
            >>> f(3)
            [0, 1, 0, 1, 4, 0, 1, 4]
            >>> f(3)
            [0, 1, 0, 1, 4, 0, 1, 4, 0, 1, 4]
            
              0
              На вход функции по прежнему подаётся вот тот самый список, который когда-то был создан пустым и назначен дефолтным значением.
            +2
            1) 4 — так как там append(i * i)
            2) [0, 1, 2] передали и добавили туда 0, 1, 4
              +1
              Да, точно же… не обратил внимание на i*i
              Спасибо
            +9

            Меня всегда удивляли подобные вопросы на собеседовании. Что именно пытаются выяснитаь таким образом? То что человек знает конкретный нюанс языка? Так таких нюансов кучи, теперь на каждом из них будем заморачиваться?
            ИМХО такая задача много говорит о "компетентности" её задающего. Если бы до меня стали так докапываться на собеседовании, то скорее нам с этим человеком не по пути.

              +1
              Мне кажется, этот конкретный нюанс важен. Если у кандидата мало опыта, и он пришел с других языков, где значения аргументов по умолчанию вычисляются заново при каждом вызове, он может на этом выстрелить себе в ногу.
                +3
                Очевидно, что дефолтные мутабельные аргументы совершенно бесполезны в виду этого «нюанса» и должны быть запрещены стайл-гайдом/линтером и т.д. Это как задавать вопросы по UB в С/С++, вместо того, чтобы просто не использовать соотвественные конструкции в коде и дать машине следить за этим вместо человека. Говоря проще, если вы задаете вопросы по говнокоду, значит либо вы пишете говнокод, либо задаете нерелевантные вопросы. И так, и так это вашей конторе чести не делает.
                  0
                  Это как задавать вопросы по UB в С/С++, вместо того, чтобы просто не использовать соотвественные конструкции в коде и дать машине следить за этим вместо человека.
                  Вообще-то за UB должен человек следить, а не машина. Они для программиста написаны, а не для разработчиков компилятора — и умение их не вызывать это важная характеристика уровня разработчика.

                  Очевидно, что дефолтные мутабельные аргументы совершенно бесполезны в виду этого «нюанса» и должны быть запрещены стайл-гайдом/линтером и т.д.
                  Даже если они запрещены стайл-гайдом (Google Style Guide их запрещает), то хорошо бы знать почему.

                  Говоря проще, если вы задаете вопросы по говнокоду, значит либо вы пишете говнокод, либо задаете нерелевантные вопросы. И так, и так это вашей конторе чести не делает.
                  Я бы сказал, что это вопрос философский. Если вы не любите думать и хотите жить в уютном загончике и просто карго-культить с 9 до 5 выполняя 100500 инструкций, смысла которых вы не понимаете — это одно, а если вы хотите понимать — что вы делаете… это другое.

                  Мне, например, Google Style Guides нравятся тем, что там в почти в каждом пункте написаны «за», «против» и «решение».

                  Я могу соглашаться или не соглашаться с «решением» (особенно в своих проектах), но имея «за» и «против» — я делаю осознанный выбор. Могу увидеть, что какие-то «за» и «против» разработчики style guide упустили из виду и попытаться его изменить…

                  А вот если style guide преподносится как данность, «потому что левая пятка Великого Архитектора так захотела, а ты, „вошь“ — теперь исполняй»… то в такую компанию я пойду разве что от безвыходности — и постараюсь сбежать как можно быстрее.
                    +1
                    Вообще-то за UB должен человек следить, а не машина.

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

                    Много чего хорошо бы знать, суть в том, насколько релевантно будущей работе. Если нечто энфорсится внутренней организацией работы (не важно, стайл гайдом, ревью или автоматическими инструментами), то это нет смысла требовать от кандидата на интервью.
                    Google Style Guides нравятся тем, что там в почти в каждом пункте написаны «за», «против» и «решение».

                    Безусловно, это важно для стайл гайда, но никак не для кандидата. Еще раз, в чем смысл задавать вопросы о языковых нюансах которые не понадобятся в работе?
                    Если вы не любите думать и хотите жить в уютном загончике и просто карго-культить с 9 до 5 выполняя 100500 инструкций, смысла которых вы не понимаете — это одно, а если вы хотите понимать — что вы делаете… это другое.

                    Я не очень понял при чем здесь любовь думать. Вы часть задумываетесь над тем, почему у молотка именно такая форма и длина ручки, вместо того, чтоб думать, куда вам гвоздь забить?
                      0
                      Вообще-то за UB должен человек следить, а не машина.
                      Вообще-то на машину надо переложить как можно больше механических функций.
                      Вообще-то в чужой монастырь со своим уставом не ходят. Да, вы можете хотеть не следить за UB — и это ваше право. Для вас есть масса языков: Java, PHP, Python и многие другие. Но если уж вы связались C/C++ — то извольте следить за UB.

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

                      И нормальные иде это делают.
                      И это хорошо — часть своих обязанностей переложить на машину это всегда приятно. Но если вдруг IDE дала «ложное срабатывание» или, наоборот, «пропустила проблему там, где она есть» — то это не значит, что IDE виновата. Виноват всё равно программист. Это его обязанность следить за тем, что происходит у него в программе. IDE — всего лишь помощник.

                      Еще раз, в чем смысл задавать вопросы о языковых нюансах которые не понадобятся в работе?
                      А почему вы решили, что они никогда не понадобятся? То, что они запрещены style guide — вовсе не значит, что их нет. Они могут быть в репозитории «с давних времён», когда Style Guide был устроен иначе. А ещё они могут оказаться в коде по ошибке. Или появиться из-за того, что вы используете какую-нибудь библиотеку, которая не соотвествует вашему Style Guide. И да — это ваша обязанность разобраться во всём этом. А чья ещё?

                      Вы часть задумываетесь над тем, почему у молотка именно такая форма и длина ручки, вместо того, чтоб думать, куда вам гвоздь забить?
                      Нет, конечно. Но если я кровельщик и кто-то использовал вместо кровельного молотка кувалду, чтобы дырку в шифере сделать (что, вообще-то говоря, запрещено Style Guide) — то я должен уметь эту проблему как-то «разрулить». А иначе что я за кровельщик?

                      Я не очень понял при чем здесь любовь думать.
                      Любовь думать тут притом, что наличие Style Guide не заменяет мозги. Если какая-то конструкция запрещена Style Guide — то это не значит, про неё не нужно знать. Наоборот — вы должны знать про неё лучше, чем про конструкции, которые разрешены Style Guide, так как встречаться с ней вы будете реже и потому вероятность того, что вы с ней налажаете — гораздо больше.
                        +2
                        Но если уж вы связались C/C++ — то извольте следить за UB.

                        Да речь вообще не о слежении, а о вопросах на интервью, которые завязаны на каких-то неиспользуемых языковых конструкциях. Это бесполезно в оценке релевантных знаний кандидата. UB в C++- это был просто пример глупого вопроса (типа «а что будет если посчитать i++ + ++i»).

                        Наоборот — вы должны знать про неё лучше, чем про конструкции, которые разрешены Style Guide, так как встречаться с ней вы будете реже и потому вероятность того, что вы с ней налажаете — гораздо больше.

                        Интересная логика. Предлагаю вам задавать вопросы по HTML/CSS вашим кандидатам на бэкенд, а то вдруг им когда-нибудь понадобится накидать мини-сайт с документацией. Это будет редко (если вообще будет), поэтому вероятность, что они налажают, крайне велика.

                        А почему вы решили, что они никогда не понадобятся?

                        Так была же опция — вы вполне можете задавать вопросы по говнокоду, потому, что человеку придется с ним потом работать.
                          0
                          UB в C++- это был просто пример глупого вопроса (типа «а что будет если посчитать i++ + ++i»).
                          Ну почему же вопрос-то глупый? Вопрос как раз вполне себе нормальный. И вполне может показать — понимает человек что такое UB или нет.

                          Так была же опция — вы вполне можете задавать вопросы по говнокоду, потому, что человеку придется с ним потом работать.
                          Было голословное утверждение: «если вы задаете вопросы по говнокоду, значит либо вы пишете говнокод, либо задаете нерелевантные вопросы», это да. Предложений «разобраться в коде, который может вам встретиться, несмотря на то, что он противоречит Style Guide» не было.

                          Предлагаю вам задавать вопросы по HTML/CSS вашим кандидатам на бэкенд, а то вдруг им когда-нибудь понадобится накидать мини-сайт с документацией. Это будет редко (если вообще будет), поэтому вероятность, что они налажают, крайне велика.
                          Вы действительно не видите разницы? Вы можете избежать необходимости работать с HTML/CSS — необходимость в этом не возникает внезапно. А вот необходимость работы со странным кодом — возникает.

                          Ваше же логика скорее похожа на подход известной авиакомпании: «раз в наших правилах написано, что в полёт неисправный самолёт не выпускается, то пилоты и не должны уметь его пилотировать». Почему-то результат применения этой логики на практике никому не понравился — не скажете, почему?
                            0
                            Да речь вообще не о слежении, а о вопросах на интервью, которые завязаны на каких-то неиспользуемых языковых конструкциях. Это бесполезно в оценке релевантных знаний кандидата. UB в C++- это был просто пример глупого вопроса (типа «а что будет если посчитать i++ + ++i»).

                            Для нормального сеньора этот вопрос конечно не релевантный, но для такого «сеньора», который в статье удивился тривиальной специфике поведения питона вопрос оказался «не в бровь а в глаз», позволил сэкономить время и не взять на ответственную позицию человека. который не знает языка.
                            То же можно сказать и про ваш пример с «i++ + ++i». Это неглупый вопрос, это вопрос, который отсеет человека, не знающего специфики синтаксиса и семантики языка. Потом такие ребята часами сидят с отладчиком пытаясь найти причину непонятного поведения асинхронного кода, где сами же не учли специфики i++. Если эта специфика не записалась тебе на подкорку, то никакие линтеры и IDE не помогут быть сеньором.
                              0
                              i++ + ++i
                              Можно один пробел убрать, компилится:
                              i+++ ++i
                              Можно перевернуть и получить другой результат, на 1 больше:
                              ++i + i++
                                0
                                То же можно сказать и про ваш пример с «i++ + ++i». Это неглупый вопрос, это вопрос, который отсеет человека, не знающего специфики синтаксиса и семантики языка.
                                Вообще, это проверяется компилятором, была бы голова у человека.
                                Не считаю, что человека нужно проверять такими примерами.
                                  0
                                  Это смотря кого вы ищете. Если компилятором проверяют наличие ошибок и опечаток — это норм. Если корректность синтаксиса, то это уже звоночек о проф-пригодности на позиции сеньора.
                                    0
                                    Тут хуже: строго говоря компилятор вообще не обязан такие ошибки ловить.

                                    То что синтаксические анализаторы потихоньку завоёвывают мир — не повод забывать о том, какие проблемы они, всё-таки, пытаются решить.
                                      0
                                      Это смотря кого вы ищете.

                                      Разумного человека, который быстро определит в чем проблема.

                                      Есть какое-то противоречие в вашем ответе. Разве, иногда, наличие ошибок и опечаток не ведет к некорректности синтаксиса?

                                      Как я понял, по вашему мнению, синьор сразу определит, что синтаксис корректен и даже будет знать без прогона, что выведет следующий код:

                                      int a[] = {1, 2, 3, 4, 5}; 
                                      int b[] = {5, 4, 3, 2, 1}; 
                                      int c = 3;
                                      
                                      cout << "c[a][b] = " << c[a][b] << endl;

                                      Часто ли такое используется? И нужно ли знать все тонкости синтаксиса?
                                        +1

                                        Вообще‐то синтаксис indexvar[arrayvar] корректен в любых языках, где корректен синтаксис arrayvar[indexvar]. Если ошибка будет, то она будет на этапе проверки типов, а не на этапе синтаксического анализа. А такие нюансы имеет смысл спрашивать либо потому что от этого что‐то действительно зависит, либо потому что они разжёваны в половине учебников, статей о том, как пройти интервью/… и на них просто нельзя не натолкнуться… если, конечно, вы читаете статьи по своей специальности. Код как в вашем примере, конечно, вряд ли кто пишет. Но вот знать это нужно, потому что в коде на C я не раз видел как конструкции вида func(&arr[n]), так и конструкции вида func(arr + n). Если вы не знаете про то, что &arr[n] — это &(*(arr + n)), то вы не сможете понять, почему приведённые мною вызовы эквивалентны и, возможно, некорректно прочитаете код.

                                          0
                                          если, конечно, вы читаете статьи по своей специальности.
                                          Я уже столько всего читал. Какие-то детали со временем меняются(выбрасываются) и не имеет смысла их запоминать. Натолкнешься — исправишь, запомнишь, «переключишь флажок» для данной ситуации и этого языка. Главное — это уметь находить проблему, изолировать ее.
                                          что &arr[n] — это &(*(arr + n))
                                          Да, это знал ранее.
                                          Можно еще индекс для массива отрицательным делать )

                                          А если еще пачку языков изучаешь, то вообще весело получается.
                                          Допустим, у вас есть задача изучить 20 языков программирования. Как вы будете решать эту задачу?
                                            0
                                            Вообще‐то синтаксис indexvar[arrayvar] корректен в любых языках, где корректен синтаксис arrayvar[indexvar].

                                            Ну это выдает ошибку:
                                            a = [1, 2, 3, 4, 5]
                                            print(a[3])
                                            print(3[a])

                                              0

                                              И? Проверку синтаксиса ваш пример вполне переживает, код с синтаксическими ошибками просто не запустится. В вашем примере Python выдаёт ошибку даже не на отсутствующей стадии проверки типов, ошибка появляется, когда код уже скомпилирован и запущен, но реализация операции индексирования обнаруживает, что данная операция не реализована для типа int.

                                                0
                                                Да, вы правы. Ошибка появляется на этапе семантического анализа.
                                                  0
                                                  MaxVetrov, строго говоря нет такого этапа в работе интерпретатора=) Но ваша мысль понятна.
                                                    0
                                                    =) А есть ли такой этап в работе интерпретатора компилирующего типа, производящий байт-код?
                                      0
                                      Проблема в том, что во всех ваших выражениях там может быть вообще какой угодно результат.

                                      Не «на один 1 больше», а «на 100 больше». Или, вообще, нуль. Как карты лягут.

                                      Некоторые компиляторы в этом месте предупреждение могут сделать — но далеко не все.
                                        0
                                        А нет проблемы — это не мои выражения. ) Я бы так не писал — в одном выражении 2 инкрементации делать над одной переменной. Проще надо — KISS.
                                        а «на 100 больше».
                                        Ну тут вы переборщили,) если только какое-нибудь другое выражение.

                                        Некоторые компиляторы в этом месте предупреждение могут сделать — но далеко не все.
                                        А не только на предупреждения смотреть надо. Смотришь, что подаешь на вход и что получаешь на выходе.

                                        Если проверять кого-то, то лучше давать код и спрашивать: «Что в этом коде не так?»
                                        Ну и смотреть реакцию .)
                                          0
                                          а «на 100 больше».
                                          Ну тут вы переборщили,) если только какое-нибудь другое выражение.
                                          UB может приводить к чему угодно. Когда-то люди, точно также, спокойно проверяли на NULL после обращения к указателю (если флаг, который копировался в глобальную переменную до проверки на NULL не использовался — всё даже работало). А потом… вот. Дыра в безопасности…

                                          А не только на предупреждения смотреть надо. Смотришь, что подаешь на вход и что получаешь на выходе.
                                          Вот так, собственно, авторы кода с проверкой на NULL себя и вели. И на выходе всё было хорошо… а потом: оп — и новый компилятор проверку выкинул…

                                          Если проверять кого-то, то лучше давать код и спрашивать: «Что в этом коде не так?»
                                          Ну и смотреть реакцию .)
                                          Но в исходном-то коде «всё так». Документация на Python вполне однозначно описывает — что будет происходить и почему.
                                            0
                                            а «на 100 больше».

                                            Ну тут вы переборщили,) если только какое-нибудь другое выражение.

                                            UB может приводить к чему угодно.

                                            я про эти выражения говорил:
                                            i++ + ++i
                                            ++i + i++
                                            Могут ли они отличаться на 100?

                                            Когда-то люди, точно также, спокойно проверяли на NULL после обращения к указателю
                                            Одно UB, другому UB рознь.

                                            Cогласно этому ответу, неопределенное поведение будет для старых стандартов C++.

                                            Но в исходном-то коде «всё так». Документация на Python вполне однозначно описывает — что будет происходить и почему.
                                            Если в исходном коде «все так», то зачем спрашивать? Добавьте свою какую-то некорректность в код. Найдет ее человек — молодец, найдет то, что вы не увидели — еще лучше.
                                              0
                                              я про эти выражения говорил:
                                              i++ + ++i
                                              ++i + i++
                                              Могут ли они отличаться на 100?
                                              Могут, конечно. Всё зависит от компилятора. Если ваша программа содержит UB, то он может делать что угодно. Делают ли так какие-нибудь компиляторы? Вроде пока нет. Появятся ли такие в будущем? Ну тут мы ничего знать не можем.

                                              Одно UB, другому UB рознь.
                                              Это сегодня. Завтра всё может быть по другому. Не нужно пытаться разделать сорта говна UB. Нужно просто в них не вляпываться.

                                              Cогласно этому ответу, неопределенное поведение будет для старых стандартов C++.
                                              Если бы его прочитали внимательно, то докопались бы и до раздела стандарта C++11
                                              (§1.9/15) If a side effect on a scalar object is unsequenced relative to either
                                              (a) another side effect on the same scalar object
                                              or
                                              (b) a value computation using the value of the same scalar object.

                                              the behaviour is undefined
                                              .Так что нет, в C++11 для этого выражения ровным счётом ничего не изменилось.

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

                                              Если в исходном коде «все так», то зачем спрашивать?
                                              Потому что «всё так» с точки зрения спеки для Python — то есть совсем не так, как в C++, JavaScript, PHP и массе других языков.

                                              Добавьте свою какую-то некорректность в код.
                                              Зачем усложнять вопрос без необходимости? Наша задача — узнать хорошо ли человек понимает, как устроен Python. Как некорректность и другие усложнения вопроса тут помогут?
                                              Вопрос про any(x) and not any(x) тоже хорош: когда такое выражение может иметь смысл и что оно проверяет? Тоже с точки зрения спеки — всё нормально. А точки зрения человека, перешедшего с другого языка — не очень…
                                                0
                                                Могут, конечно. Всё зависит от компилятора. Если ваша программа содержит UB, то он может делать что угодно. Делают ли так какие-нибудь компиляторы? Вроде пока нет. Появятся ли такие в будущем? Ну тут мы ничего знать не можем.
                                                Что-то я сомневаюсь. Во что, к примеру, такие выражения будут компилироваться, условно, «плохим» компилятором, что получится неопределенное поведение?
                                                С NULL все понятно — это страшная вещь.)

                                                Это сегодня. Завтра всё может быть по другому. Не нужно пытаться разделать сорта говна UB. Нужно просто в них не вляпываться.
                                                Не ругайтесь. Да, завтра все может быть по другому, от будущего не застрахуешься, появится какая-нибудь новая версия компилятора и всё — приехали.
                                                Слушайте, ну проблемы тоже нужно классифицировать, и понять какие неопределенности несут больше рисков.
                                                Никто не спорит, что не нужно совершать ошибки, но почему-то люди их совершают. Кто-то больше, кто-то меньше, зависит от квалификации и настроя программиста.

                                                Тоже с точки зрения спеки — всё нормально. А точки зрения человека, перешедшего с другого языка — не очень…
                                                Вы правы, а долго ли такому человеку уяснить отличия, если он на С++, к примеру, до этого программировал, и понимает как это на низком уровне работает?
                                                  0
                                                  Во что, к примеру, такие выражения будут компилироваться, условно, «плохим» компилятором, что получится неопределенное поведение?
                                                  Ну, например… ни во что. Просто взять и выкинуть весь код, который написан после i++ + ++i; — это абсолютно законное поведение.

                                                  С NULL все понятно — это страшная вещь.)
                                                  А какая разница? Когда-то, давным-давно, компиляторы генерировали код, который обрушивал программу в рантайм. А потом — компиляторы научились использовать потенциальные обращения к NULL для оптимизаций. Если есть разименования переменной, значит она не NULL и, соотвественно, лишние проверки на NULL можно удались.

                                                  То же самое и здесь можно сделать: раз переменная в этом выражении модицицируется дважды, то это — неопределённое поведение, зачит этот код никогда не исполняется и, соответственно, может быть удалён.

                                                  Никогда не видели, как компилятор опровергает Теорему Ферма? Тут — тот же принцип.

                                                  Слушайте, ну проблемы тоже нужно классифицировать, и понять какие неопределенности несут больше рисков.
                                                  Нет, не нужно. Если программа может вызвать неопределённое поведение — то это не программа на C или C++ и она должна быть исправлена. Точка. Конец дискуссии. Тут нечего обсуждать.

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

                                                  Если какое-то UB лично вам не очень нравится — можете обсудить с разработчиками компиляторов и добавить соотвествущий флаг. Так появились -ftrapv, -fwrapv и -fno-strict-aliasing. Но если такого флага нет — то UB нужно просто не допускать, а не защищать: дескать я точно знаю, что на x86 доступ к невыровненной переменной из двух байт не вызывает проблем, всё будет хорошо… не будет, не надейтесь.

                                                  Вы правы, а долго ли такому человеку уяснить отличия, если он на С++, к примеру, до этого программировал, и понимает как это на низком уровне работает?
                                                  Нет, недолго, конечно. Достаточно упомянуть, что у скомпилированной функции «внутри» есть объект __default__, который contains default argument values for those arguments that have defaults, or None if no arguments have a default value — и для человека со знанием C++ всё станет очевидно. Однако это одна из тех вещей, которые практически невозможно обнаружить случайно, читая чужой код — и на которую можно легко наступить самому, если ты Python изучал только читая чужой код, а не сходив на курсы и/или не почитав учебник.

                                                  Таких вещей в Python не то, чтобы уж очень-очень много… но их есть, сколько-то, так что прочитать учебник — всё-таки стоит.
                                                    0
                                                    Ну, например… ни во что. Просто взять и выкинуть весь код, который написан после i++ + ++i; — это абсолютно законное поведение.
                                                    Да, может, ну очень плохой компилятор если он еще при этом ошибку не выдаст.)
                                                    А какая разница? Когда-то, давным-давно, компиляторы генерировали код, который обрушивал программу в рантайм.
                                                    По-моему, они и сейчас могут генерить код который в рантайме может обрушится.
                                                    То же самое и здесь можно сделать: раз переменная в этом выражении модицицируется дважды, то это — неопределённое поведение, зачит этот код никогда не исполняется и, соответственно, может быть удалён.
                                                    Я посмотрел уязвимость про которую вы говорили ранее. Мне интересно, а есть ли эксплоит для подобного выражения, инкремента?
                                                    Никогда не видели, как компилятор опровергает Теорему Ферма? Тут — тот же принцип.
                                                    GCC вроде так не хулиганит, флаг O2. А тут как нужно писать, чтобы все нормально было? Определить условия выхода из цикла?
                                                    Нет, не нужно. Если программа может вызвать неопределённое поведение — то это не программа на C или C++ и она должна быть исправлена.
                                                    Все верно говорите, но компилятор позволяет это сделать. Конечно, это не отменяет голову программиста, но если ружье висит на стене почему оно никогда не выстрелит?
                                                    Точка. Конец дискуссии. Тут нечего обсуждать.
                                                    Категорично.
                                                    Конечно. Но реакция на такие ошибки может быть только одна: их нужно исправлять.
                                                    Реакция всем понятна — это очевидно для тех кто любит свою работу.
                                                    Если какое-то UB лично вам не очень нравится — можете обсудить с разработчиками компиляторов и добавить соотвествущий флаг.
                                                    А стандарт языка может перевести эту неопределенную зону в зону определенности?
                                                    не сходив на курсы и/или не почитав учебник.
                                                    Лучше учебник, стандарт и компилятор под рукой.
                                                      0
                                                      Да, может, ну очень плохой компилятор если он еще при этом ошибку не выдаст.)
                                                      Почему нет? Проверки на NULL используются для того же самого без всяких сообщений об ошибках…

                                                      По-моему, они и сейчас могут генерить код который в рантайме может обрушится.
                                                      Могут, но не обязаны. Это разные вещи.

                                                      Мне интересно, а есть ли эксплоит для подобного выражения, инкремента?
                                                      Пока, насколько мне известно, компиляторы так двойное изменение значения не интерпретировали. Всё ещё впереди…

                                                      GCC вроде так не хулиганит, флаг O2. А тут как нужно писать, чтобы все нормально было? Определить условия выхода из цикла?
                                                      Нужно, чтобы вычисления что-то меняли во внешнем мире после выхода из цикла. При этом компилятору стандарт явным образом разрешено выкидывать любные вычисления, которые ничего не меняют «во внешнем мире» — в частности любые циклы задержек. У разных компиляторов просто разные эвристики, которые позволяют им распознавать и оставлять в коде «циклы задержки».

                                                      Конечно, это не отменяет голову программиста, но если ружье висит на стене почему оно никогда не выстрелит?
                                                      Оно висит и стреляет. Но виноват — всегда программист. Это — сознательное решение разработчиков C/C++.

                                                      Цитата:
                                                      However, if any such execution contains an undefined operation,
                                                      this International Standard places no requirement on the
                                                      implementation executing that program with that input
                                                      (not even with regard to operations preceding the
                                                      first undefined operation).
                                                      Если программа содержит UB — то возможно любое поведение. Даже на участке кода, до самого UB (это подчёркнуто особо). Хотите рассуждать о том, что будет (или не будет) делать программа? Избавьтесь от UB — затем говорите.

                                                      А стандарт языка может перевести эту неопределенную зону в зону определенности?
                                                      Может. Как и наоборот. Но это происходит с большим-большим скрипом всегда.
                                      0
                                      Этот вопрос глупый потому, что ответ на него «не пишите так». Например, код «i = (i, ++i, 1) + 1» вроде как не UB, но ответ на него такой же. Если вас реально волнует глубокое знание языка, возьмите пример из своей практики и адаптируйте его. Давать же куски говнокода и просить человека поработать компилятором или справочником по стандарту бессмысленно и неуважительно.

                                      Понятно, что редкоиспользуемые вещи могут изредка вылазить в любых проектах. Нет ничего ужасного в том, что человек это загуглит.
                                        0
                                        А что его побудит загуглить, собственно? Компиляторы, в общем-то, далеко не все тут warning выдают. В style guide об этом тоже ничего нет. Так что, собственно, побудит человека гуглить раньше того момента, когда космическая группировка станет подводной?
                                          0
                                          Уважаемый синьор daiver19 похоже принял слишком близко к сердцу эту проблему.
                                          Почему-то он не приемлет синтетических примеров, ему подавай «из практики». Ну и что, что они на порядок сложнее и дольше в постановке. Школьные учебники с простыми синтетическими примерами его тоже, видимо, не устраивают.

                                          Представляю себе как этот синьор скажет джуну в своей команде:
                                          — Не пиши так.
                                          — Но, мастер, почему..?
                                          — Просто не пиши. Не хочу углубляться в подробности, но так делать нельзя! Я чувствую это.
                                          — Х-хорошо, мастер. Я постараюсь запомнить.
                              0
                              Если «сеньор» не заметил опасный мутабельный дефолт в аргументах на примере простого списка, то более сложной ситуации с кастомным классом он и подавно не увидит.
                              Линтеры же покамест не всесильны.
                                0
                                Если «сеньор» не заметил опасный мутабельный дефолт в аргументах на примере простого списка, то более сложной ситуации с кастомным классом он и подавно не увидит.

                                Так возьмите и задайте адекватный вопрос! Вы же понимаете, что проблема данного примера не в том, что в питоне дефолт реализован особым образом. Проблема в том, что приведенный код совершенно не имеет смысла ни на каком языке. Если у вас на проекте и вправду встречаются подобные проблемы, возьмите и адаптруйте такой пример. Хотя я не вижу как нечто подобное может пройти ревью, например.
                                  0
                                  Да как это не имеет? Ниже я привел пример.
                                  Да, так в продакшн не пишут, но это пример из книжки для чайников. Если этого не понимает синьор, то сливай воду.

                                  Кстати, может быть вам и в школе более реальные задачи надо задавать, а не абстрактные гоняющиеся друг за другом поезда, встречающиеся неизвестно где?
                                  Пример в статье максимально короткий и лаконичный. В нём невозможно не заметить подвох, если знаешь о том как работают дефолты в питоне. Я повторюсь, что синьору задавать такие примеры не релевантно, такой хреновый синьор должен был раньше отсеяться по другим признакам. Однако задача для питона очень простая и вполне адекватная. Она направлена на знание специфики языка. Она есть в первых главах каждого учебника для изучающих язык с нуля.
                                    0
                                    Да, так в продакшн не пишут, но это пример из книжки для чайников.

                                    Так в этом и суть!

                                    В нём невозможно не заметить подвох, если знаешь о том как работают дефолты в питоне.

                                    Т.е. для вас подвох в том, что дефолты там как-то не так в питоне работают? Не в том, что у вас функция изменяет дефолтное значение, которое, очевидно, не доступно вызывающему? Еще раз, код из статьи плох на любом языки поддерживающем дефолтные аргументы. Ваш пример ниже — просто грязный хак. Мы опять возвращаемся к моему первому комментарию — если у вас на проекте такие хаки — это норма, задавайте подобные вопросы сколько угодно.
                                      0
                                      Еще раз, код из статьи плох на любом языки поддерживающем дефолтные аргументы
                                      А вот с этого момента — поподробнее. В C++ такой код не вызывает подобных проблем — потому что «дефолтный» список каждый раз создаётся заново. Как я продемонстрировал чуть ниже.

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

                                        Могу сразу ответить за вас: функция заполняет входной список квадратами чисел. Список — это параметр для вывода значаения, а значит дефолтный параметр ей не нужен. Если у тебя есть функция в которой есть опциональные output-аргументы, то будь добр, инициализируй их None/nullptr/и т.д, а не модифицируй переменную которая уйдет вникуда(в случае С++)/в массив дефолтных значений (в случае с Python).
                                          0
                                          Если у тебя есть функция в которой есть опциональные output-аргументы, то будь добр, инициализируй их None/nullptr/и т.д, а не модифицируй переменную
                                          Цель какая? Кода побольше написать?

                                          которая уйдет вникуда(в случае С++)/в массив дефолтных значений (в случае с Python).
                                          С какого перепугу она «уйдёт в никуда»? Это что за выдумки? В C++, как раз, всё будет нормально: вы либо будете заполнять значениями новый, пустой массив каждый раз, либо дополнять существующий. Никакой путаницы и псевдо-кеша там не будет. Валидный, допустимый код. Вот вообще никаких проблем с ним.

                                          А в Python — таки да. Отсюда и вопросы. Особенно если у вас в ходу и C++ и Python.
                                            0
                                            Цель какая? Кода побольше написать?

                                            Цель — писать понятный и эффективный код.

                                            С какого перепугу она «уйдёт в никуда»?

                                            С такого, что клиент не имеет доступа к этой переменной. Короче, если вы до сих пор не видите проблемы, я не вижу смысла об этом дальше спорить.
                                              0
                                              Похоже нужно постараться конкретизировать спор, а не уязвить оппонента меткой колкостью. Похоже в эту ловушку попался и я.
                                              Давайте четко обозначим наши позиции. Возможно они уже давно не противоречат друг-другу.

                                              Я считаю, что высококвалифицированный опытный программист должен достаточно хорошо знать свой язык программирования, чтобы даже в таком простом синтетическом и оторванном от реальности примере (как в статье) сразу понять поведение интерпретатора. Такое понимание позволит ему правильно понимать его в других жизненных и куда более сложных и неочевидных случаях.
                                                0
                                                Давайте четко обозначим наши позиции.
                                                А надо ли? По моему они уже вполне обозначены.


                                                Зачем всё это нужно в языках не устроенных, так, как Python — остаётся неясным. Ну если не хотите/не умеете/не можете в C++ — вот вот вам PHP «со спамом» (вспомнинаем Монти Пайтона):
                                                <?php
                                                function makecoffee($types = array("cappuccino"),
                                                                    $coffeeMaker = NULL)
                                                {
                                                    $device = is_null($coffeeMaker) ?
                                                      "hands" : $coffeeMaker;
                                                    array_push($types, "spam");
                                                    return "Making a cup of ".join(", ", $types)." with $device.\n";
                                                }
                                                echo makecoffee();
                                                echo makecoffee(array("cappuccino", "lavazza"),
                                                                "teapot");
                                                echo makecoffee();
                                                ?>
                                                ---
                                                Вывод:
                                                Making a cup of cappuccino, spam with hands.
                                                Making a cup of cappuccino, lavazza, spam with teapot.
                                                Making a cup of cappuccino, spam with hands.
                                                (это, между прочим, добавление пары строк в пример из оффициальной документации, который, собственно, рассказывает, как использовать «аргументы по умолчанию»).

                                                И да: как видим — но в PHP, ни в C++ с этим не возникает проблем (хотя причины разные).

                                                И в C++ и в PHP много чего сделано плохо — но вот конкретно передача аргументов по умолчанию там сделана лучше, чем в Python и, соотвественно, костыли, применяемые в Python — там не нужны.

                                                Представить себе, что человек, перешедший с одного из таких языков устроит вам проблему на ровном месте, не выполнив «танец маленьких лебедей» с None и «дополнительной» инициализацией, которая в других языках попросту не нужна — раз плюнуть. Вообще человек, перешедший на Python с других языков (то есть, например, я) всё будет делать несколько «не так», как человек с опытом работы на Python — но понимать: знает ли он Python хотя в достаточной степени, чтобы не создавать подобных «бомб замедленного действия»… бывает очень полезно.

                                                И вот ровно для этого — задаваемый вопрос вполне годится. А вот ответ «Не хочу углубляться в подробности, но так делать нельзя! Я чувствую это» — не годится.

                                                Хотя есть люди, которые считают, что так и надо отвечать. Ага. На собеседовании.
                                                0
                                                С какого перепугу она «уйдёт в никуда»?
                                                С такого, что клиент не имеет доступа к этой переменной. Короче, если вы до сих пор не видите проблемы, я не вижу смысла об этом дальше спорить.
                                                Почему не имеет, если эта переменна возвращается из функции?
                                                  0
                                                  Это *дефолтное* значение. Если вы передадите аргумент в функцию, значит оно не будет использовано.
                                                    0
                                                    Вы это… сейчас… всерьёз? Это реально не попытка придуриться?

                                                    Да, конечно, оно не будет использовано — оно ж не будет создано!

                                                    Вот модификация моего примера:
                                                    #include <iostream>
                                                    #include <list>
                                                    
                                                    template <typename Container>
                                                    void print_container(const Container* container) {
                                                      for (const auto& element : *container) {
                                                        std::cout << element << " ";
                                                      }
                                                      std::cout << std::endl;
                                                      delete container;
                                                    }
                                                    
                                                    auto f(int x, std::list<int>* l =
                                                        (std::cout << "Creating default" << std::endl,
                                                         new std::list<int>())) {
                                                      for (int i = 0; i < x; ++i) {
                                                        l->push_back(i * i);
                                                      }
                                                      return l;
                                                    }
                                                    
                                                    int main() {
                                                      print_container(f(2));
                                                      print_container(f(3, new std::list{0, 1, 2}));
                                                      print_container(f(3));
                                                    }
                                                    ---
                                                    Выход:
                                                    Creating default
                                                    0 1 
                                                    0 1 2 0 1 4 
                                                    Creating default
                                                    0 1 4 
                                                    
                                                    Посчитайте, пожалуйста, сколько строк «Creating default» было выведено!

                                                    Да, представьте себе, в таких языках как C++, JavaScript или PHP подход, использованный в примере из статьи — не просто допустим, он идеоматичен! Более того, в C++, во многих случаях, ваш любимый трюк с передачей None и созданием объекта внутри не просто не нужен — он попросту невозможен (копать в районе ссылок).

                                                    Так какие языки, чёрт побери, вы имеете в виду, говоря «инициализируй их None/nullptr/и т.д, а не модифицируй переменную»? Если в большинстве распространённых языков это не нужно?

                                                    P.S. Кстати спасибо вам за то, что разобрался нужно ли эти приседания делать в JavaScript (я с ним очень давно не работал, тогда ES6 был далеко не во всех браузерах, так что как там работают аргументы по умолчанию я, честно, просто не знал… оказалось — нормально… ну то есть не как в Python).
                                                      0
                                                      Я понимаю, что можно извернуться и вернуть входной список, но в общем случае если вы не передали входной аргумент в функцию, то и доступа к его содержимому у вас нет. Передавать нулевое значение, когда вас не интересует вывод — это не трюк, а нормальная практика. Передавать мутабельный аргумент (неконстантный указатель/ссылку) не в качестве выходного параметра смысла нет. Создавать переменную через new в дефолтном аргументе — грязный хак и утечка памяти, не пишите так, пожалуйста.
                                                        0
                                                        Передавать нулевое значение, когда вас не интересует вывод — это не трюк, а нормальная практика.
                                                        В данном случае речь идёт не о случае, когда вас не интересует выход, а о случае, когда «вы не хотите платить» за то, что вам не нужно. То есть по умолчанию функция создаёт и фомирует список (хотя в C++, скорее массив будет), но если у вас массив уже есть — то может и в него дописать. Чего тут необычного?

                                                        Создавать переменную через new в дефолтном аргументе — грязный хак и утечка памяти, не пишите так, пожалуйста.
                                                        С какого перепугу? Агрумент как аргумент. Да, конечно, нужно, чтобы вызывающая функция его удалила. Ну если вы уж очень хотите кошерности, то нужно, конечно, что-то типа такого написать:
                                                        #include <iostream>
                                                        #include <memory>
                                                        #include <vector>
                                                        
                                                        template <typename Container>
                                                        void print_container(std::unique_ptr<Container>&&
                                                            container) {
                                                          for (const auto& element : *container) {
                                                            std::cout << element << " ";
                                                          }
                                                          std::cout << std::endl;
                                                        }
                                                        
                                                        std::unique_ptr<std::vector<int>>&& f(
                                                            int x,
                                                            std::unique_ptr<std::vector<int>>&& v =
                                                                std::make_unique<std::vector<int>>()) {
                                                          for (int i = 0; i < x; ++i) {
                                                            v->push_back(i * i);
                                                          }
                                                          return std::forward<
                                                            std::unique_ptr<std::vector<int>>>(v);
                                                        }
                                                        
                                                        int main() {
                                                          print_container(f(2));
                                                          print_container(f(3,
                                                              std::make_unique<std::vector<int>>(
                                                                  std::vector{0, 1, 2})));
                                                          print_container(f(3));
                                                        }
                                                        Но принципиально это ничего не меняет, просто суть теряется за бесконечным дублированием имён…

                                                        Про утечку памяти я написал в самом первом примере — и да, как раз если мы собеседуем кандидата на C++ программиста и не уверены, сколько времени он работал с Python, а сколько с C++ — то тут уже про это нужно пару вопросов задать.

                                                        Но я не видел ещё никого, кто бы на интервью все эти std::make_unique и std::forward изобразил и нигде не запутался — обычно всё-таки в этом месте «срезают углы» и ограничиваются просто new. Иначе ну просто очень много писанины. C++ — он такой, да. Многословный.

                                                        P.S. Кстати напишите уж вот эту последниюю версию — с unique_ptr — без создания объекта в качестве дефолтного параметра. Очень уж интересно, как вы это, собственно, собрались делать.
                                                          0
                                                          Вы опять уходите в какие-то дебри. Вам нужна функция которая возвращает вектор квадратов? Так верните вектор квадратов (т.е. сигнатура будет vector f(int x)). Вам нужно дописать в вектор квадраты? Ок, используйте void f(int x, vector* v). На таком примере гораздо интереснее поговорить об SRP, KISS и т.д., чем об особенностях дефолтных аргументов в разных языках.
                                                            0
                                                            Вам нужна функция которая возвращает вектор квадратов? Так верните вектор квадратов (т.е. сигнатура будет vector f(int x)). Вам нужно дописать в вектор квадраты? Ок, используйте void f(int x, vector* v).
                                                            Но что нам мешает реализовать сразу два функционала в одной функции? Собственно дефолтное значение для того и существует, чтобы две функции не писать — больше никакого особого смысла у него нет.

                                                            Можно обсуждать — стоит ли вообще пользоваться дефолтными значениями (есть компании, где считается, что их использование «усложняет понимание кода»… и отчасти они правы), но если мы вообще их допускаем — то я не вижу причины, по которой нельзя написать функцию так или так.

                                                            Обоснование «а вдруг кто-то дословно переведёт этот код с C++ на Python и вот там у него возникнут проблемы» — я, всё-таки, считаю притянутым за уши.

                                                            На таком примере гораздо интереснее поговорить об SRP, KISS и т.д., чем об особенностях дефолтных аргументов в разных языках.
                                                            Проблема в том, что умение рассуждать от SRP, KISS и прочих всяких акронимах и умение писать безопасный код на разных языках… это разные умения. Совсем разные.
                                              0
                                              Вот именно. Только это не проблема задачи, а проблема синьора, которого не смутила столь странная функция. Он даже не заподозрил подвоха увидев мутабельный объект в дефолтах. Он даже не вспомнил про этот сайдэффект.

                                              Вы, похоже, только что меня убедили. Эта задача — довольно эффективный способ определения синьора-самозванца. Истинный синьор-питонист не потратил бы на эту задачу и минуты. Она уже решена в его голове.
                                            0
                                            Ваш пример ниже — просто грязный хак. Мы опять возвращаемся к моему первому комментарию — если у вас на проекте такие хаки — это норма, задавайте подобные вопросы сколько угодно.

                                            Да блин, какого черта? Я ж написал, что в проде так не делают.
                                            А ещё уже несколько раз писал в этой баталии, что синьор должентакие вещи понимать даже в плохом коде. Что за синьор, который не может объяснить джуну как делать не следует и почему?!

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

                                              Неуместен он был бы, если бы все сеньоры на него без проблем отвечали.

                                              Что за синьор, который не может объяснить джуну как делать не следует и почему?!
                                              Ну вот собственно ключевой момент:
                                              1. Вы, вроде, тоже считаете, что сеньор должен на такой вопрос отвечать.
                                              2. Из опыта, нам известно, что отвечают на него, в общем-то, далеко не все люди, называющие себя сеньорами.
                                              3. Так почему, собственно, он вдруг стал неуместным?
                                                0
                                                Сударь, вы и весь этот спор в целом меня убедили.
                                                Секунды своего драгоценного времени, которые потратит на эту задачу настоящий синьор стоят того, чтобы избавить HR от многочасовых разговоров с самозванцами на темы сложных задач «из практики».
                                                  0
                                                  Не секунды, про коэффициент π не забывайте. Так что, на собеседовании, минуты 2-3, а может, даже, и целых пять… но это вполне приемлемое время, да.

                                                  Вот если всё интервью с синьором из подобных задач состоит — то это уже клиника, так быть не должно.
                                                0
                                                Что за синьор, который не может объяснить джуну как делать не следует и почему?!

                                                Ну так если у вас стоит цель проверить, умеет ли ваш синьор в ревью, так дайте ему, блин, сделать ревью небольшого куска реалистично выглядящего джуниорского кода. Моя претензия к самой постановке задачи в духе «вот кусок непонятного кода, что он выведет». Даже если вы возьмете тот же кусок кода и спросите «что с ним не так?» — уже будет лучше. Если вы добавите пару тестов, которые проходят и спросите, что не так с покрытием тестов, то будет еще лучше и т.д. Я понимаю, что это потребует чуть больше 5 секунд подготовки к интервью, но оно того стоит, имхо.
                                                  0
                                                  Как ловко вы затролили в этом треде меня и коллегу khim.
                                                  Что ж, баталия вышла знатная.
                                                  Жаль я сразу не заглянул в ваш профиль. ХЗ как вы это сделали, но к бабке не ходи, этому ресурсу полезны такие люди как вы.
                                                    0
                                                    Не пытался никого троллить, но сожалею, если это так выглядит для вас.
                                      +2

                                      Я с ходу назову штук пять идиотских (wtf) решений в питоне, которые стреляют в ногу. Гоняя программиста на собеседовании по таким вещам можно понять только то, что он по этим граблям ходил, т.е. у него есть опыт работы с языком.


                                      Однако, это ничего не говорит о том, какой программист. Прямо от слова "совсем". Потому что эта грабля могла появиться даже в микроскопическом приложении на 100 строк, и это максимум, который делал кандидат.


                                      Короче, такие вопросы (с ожиданием правильного ответа, а не как повод "поговорить") — это глупость.


                                      Как повод поговорить — очень даже. Но, подразумевается, что в этом случае а) кандидат может сам запустить питон, потыкать результаты, подумать, и после этого можно его спросить что он про это думает.

                                        0
                                        Да ладно вам. Не нужно наступать на некоторые грабли, чтобы догадаться о возможных последствиях увидев их у стеночки клевцами вверх. Если у тебя большой опыт, то не исключено, что о некоторых граблях ты догадался и без шишек, а ещё это был так давно, что уже и трудно сказать когда именно ты научился реагировать на мутабельные дефолты и прочие примеры «неожиданного» в питончике. Вы ещё назовите ситуацию с присвоением — WTF.
                                          0

                                          Совершенно не очевидно, что дефолтное значение переменной в сигнатуре функции вычисляется в момент декларации функции. Ещё более не очевидно, что переменная не инициализируется ещё раз. Прям очень не очевидно, это именно wtf языка. Ровно как было в python2, когда except (Exception1, Exception2) ловил любое из двух exception'ов, а except [Exception1, Exception2] ловил список из двух exception'ов (как объект с типом list).

                                      0
                                      Что значит «докапываться»? Если бы вы потеряли возведение в квадрат, как alexvangog сделал — я думаю никто особо не парился бы. Хотя внимательность — штука полезная, но все понимают: собеседование, волнение, всё такое. Но если вы не понимаете, что в третий раз «на входе» — у вас непустой список, то вы, мягко говоря, не очень хорошо умеете в Python.
                                        0
                                        Это только списков касается?
                                          0
                                          Нет, конечно. С чего бы? Может быть dictionary. Или set. Или вообще ваш собственный тип. Принципы-то не меняются.
                                            0
                                            Это касается любого «мутабельного» объекта. Да, в Питоне есть такое понятие. Есть объекты, которые нельзя менять (строки, кортежи, числа) и есть объекты. которые менять можно (списки, словари, множества). Можно самому реализовать мутабельный и иммутабельный тип и они будут полезны в тех или иных случаях.
                                            Зачем это все так — относительно долгая история. Это следствие ряда механизмов внутренней оптимизации интерпретатора.
                                            К примеру в C переменная похожа на ящичек, куда что-то можно положить, а потом положить туда что-то другое. В Питоне переменная — это ярлычок, который привязывается к объекту. Присвоением можно привязать к этому же объекту ещё один ярлычок (другое имя, другую переменную) или ярлычок (имя, переменную) перевязать с одного объекта на другой.
                                              0
                                              К примеру в C переменная похожа на ящичек, куда что-то можно положить, а потом положить туда что-то другое.
                                              В C есть указатели и нормальные типы. В Python все переменные — указатели, а все объекты — где-то в куче.

                                              В C++, как бы, указатели тоже есть. Но вот такая программа:
                                              #include <list>
                                              #include <iostream>
                                              
                                              template <typename Container>
                                              void print_container(const Container* container) {
                                                for (const auto& element : *container) {
                                                  std::cout << element << " ";
                                                }
                                                std::cout << std::endl;
                                              }
                                              
                                              auto f(int x, std::list<int>* l=new std::list<int>()) {
                                                for (int i = 0; i < x; ++i) {
                                                  l->push_back(i * i);
                                                }
                                                return l;
                                              }
                                              
                                              int main() {
                                                print_container(f(2));
                                                print_container(f(3, new std::list{0, 1, 2}));
                                                print_container(f(3));
                                              }
                                              
                                              Таких эффектов, как в Python — не вызывает. И да — она вызывает другие (утечку памяти). Это — другая особенность C++.
                                                0
                                                Все верно. Поэтому я и написал, что «это следствие ряда механизмов внутренней оптимизации интерпретатора».
                                                Ещё один механизм — это механизм определения функций и их аргументов.
                                          +3
                                          Мы не знаем заранее чего хотел добиться интервьюер задавая такой вопрос. Можно, конечно, предположить, что он глуп и сделал это от незнания до чего докопаться.
                                          Однако с такой логикой легко и самому оказаться в дураках.
                                          Давайте предположим, что интервьюер довольно умён, какую бы он цель вероятно преследовал в этом случае?

                                          Дело в том, что ситуация с этим списком в дефолтах очень баянистая. В каждой первой книжке а-ля Python с нуля или для чайников это рассматривается. Наверно только джуниор (в этом языке) не знал бы такого (в общем-то весьма важного) «нюанса», связанного с передачей параметров в функции.
                                          Может быть интервьюер хотел одним вопросом понять уровень знания языка. В данном случае, как выяснилось, вопрос попал в точку, то есть на границу знаний автора. Лично я, к примеру, расстроился бы, если бы меня спросили такое на собеседование в синьоры. Вот это действительно было бы пальцем в небо. А тут вопрос сработал на все 100%.

                                          Автору оригинальной статьи эта история показалась, видимо, настолько поучительной, что он не стал изучать вопрос глубже, тогда он обнаружил бы всю баянистость его. Зато автор решил поведать всему миру о таком странном поведении языка.
                                            0
                                            Лично я, к примеру, расстроился бы, если бы меня спросили такое на собеседование в синьоры.
                                            А почему, собственно? Я бы удивился и огорчился, если бы это было единственный вопрос, который бы задали сеньору. Но если «в качестве разминки», просто чтобы проверить, что сеньор ещё не забыл как код пишут… почему и нет?

                                            P.S. Но тот факт, что «Senior Python Developer» был поражён этим вопросом в пятку… настолько, что решил написать об этом заметку в блог… вот это меня немного удивило.
                                              0
                                              А почему, собственно?

                                              Хотя бы потому, что ответы на более сложные и профильные вопросы покажут забыл или не забыл, зато экономия времени на «разминке» позволит контрагентам лучше узнать друг друга: интервьюер поймёт больше о соискателе, его интересах, планах; соискатель больше узнает о компании. Эдак можно было начать с арифметики в качестве разминки. Зачем?
                                              «Senior Python Developer» был поражён

                                              Ну да, странненько как-то. Разве что автор просто наполняет блог материалами для разного круга подписчиков, и в ход идут какие-то старые воспоминания или фантазии… не знаю.
                                                0
                                                зато экономия времени на «разминке» позволит контрагентам лучше узнать друг друга
                                                Вы уверены?

                                                Эдак можно было начать с арифметики в качестве разминки. Зачем?
                                                Затем, что на практике сеньоров, забывших арифметику — я не встречал. Всё-таки в начальной школе её крепко вбивают. А вот Senior Software Developer'ов, не написавших за последние лет 10 ни строчки кода, не умеющих это делать (когда-то возможно и умевших… но прямо сейчас — нет) и, работающих, по факту, менеджерами — я видел достаточно для того, чтобы мне хотелось их отсеять как можно быстрее и лишнего времени на них не тратить (они могут оказаться неплохими PM'ами — но это другая позиция, мы же тут вроде про Senior Software Developer'ов говорим). А дальше уже можно в ту или иную сторону эту задачу развернуть, было бы желание.
                                                  0

                                                  А мне кажется что давать такие задачки на собеседовании, человеку который метит на позицию Senior — плохая трата времени, как своего, так и кандидата. Если этот человек Senior — то это же не разработчик на Питоне в вакууме. Нам нужен Senior Python Developer.


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


                                                  Что же касается вышеприведённой задачи, то в контексте обсуждения написания API, я бы спросил у кандидата следующее:


                                                  Как бы вы инициализировали значение по-умолчанию аргумента функции, если это значение — изменяемый объект?

                                                  И попросил бы обосновать свой ответ. Спросил бы о том, как бы кандидат задокументировал это значение. Использовал бы type annotations?
                                                  Как по вашему, помогло бы это нам лучше узнать кандидата, по сравнению с "что делает этот код?"

                                                    0
                                                    Например дал бы предварительное задание касающееся предметной области.
                                                    Можете пролистать просто статьи за последние несколько месяцев — и обнаружите просто толпы возмущённых синьоров, которые возмущаются тем, что на их время кто-то без оплаты позарился.

                                                    Обсудит архитектуру, язык, технологии, фреймворки, безопасность, алгоритмы и т.д.
                                                    Это ж какого размера вы тестовое задание собрались давать? Чтобы там всё «выстрелило» — оно должно быть на неделю работы… а столько мало какой сеньор ради тестового задания сотворит.

                                                    Как по вашему, помогло бы это нам лучше узнать кандидата, по сравнению с «что делает этот код?»
                                                    Это, конечно, дало бы некоторую информацию о кандидате. Но ответа на вопрос: может ли он разобраться в существующем коде — по прежнему бы не было.

                                                    А на практике — именно это является основной деятельностью разработчика.
                                                      0
                                                      Чтобы там всё «выстрелило» — оно должно быть на неделю работы…

                                                      Совершенно верно! Если я, как программист заинтересован в работодателе, то неделя потраченная на тестовое задание вполне того стоит.


                                                      Если нужно проверить, насколько программист разбирается в легаси коде, бусть часть задания будет из серии "Вам пришлось работать с легаси-кодом программы -цатилетней давности, написанной, скажем, на COBOL. Разберитесь в том что он делает и представьте идентичный API на Питоне".

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

                                                        «Вам пришлось работать с легаси-кодом программы -цатилетней давности, написанной, скажем, на COBOL. Разберитесь в том что он делает и представьте идентичный API на Питоне».
                                                        Вы точно будут «гореть желанием» пойти к работодателю с подобными требованиями?

                                                        Моё лично решение будет — пойти поискать более вменяемые предложения.
                                                          0

                                                          Из опыта: было дано задание на которое ушло 3 часа. Но это была обязательная часть (минимальный CRUD API на Django / DRF).
                                                          Также предлагалось раз в 7 больше задач с пометкой "мы были бы рады увидеть ваше решение, если вы знаете об этом больше нас". И эти задачи проникали во все концепции DevOps актуальные для компании — от вопросов "напиши Web UI для приложения" до "как бы вы организовали инфраструктуру AWS для нагрузки в 100, 1000, 10000 запросов / сек. ?"
                                                          Конечно можно было бы прийти на собеседование лишь с обязательной частью. Но тогда и не скромные запросы зарплаты вряд ли бы прокатили.


                                                          Вы точно будут «гореть желанием» пойти к работодателю с подобными требованиями?

                                                          Немного превувеличил :)

                                            0
                                            > То что человек знает конкретный нюанс языка? Так таких нюансов кучи, теперь на каждом из них будем заморачиваться?

                                            То, что это типовые грабли при достаточно типовом использовании, на которые очень легко наступить. Поэтому их знание показывает уровень опыта — или такого априорного знания, которое вместо опыта.

                                            Я реально на это нарвался, но чуть в другой форме:

                                            class Foo:
                                            
                                                bar = []
                                            
                                                ...
                                                    bar.append(baz)
                                            


                                            Потом в процессе прочистки кода заменил на инициализацию self.bar в конструкторе и забыл. Но старую версию успели форкнуть в релиз.
                                            Суть бага: как только прошёл хотя бы один трансфер, каждый новый удваивает список устаревших записей без его очистки, пока всё приложение не падает (список рос до сотен миллионов записей).
                                            Пришлось срочно выкатывать версию x.0.1 на следующий день после x.0.
                                            +2
                                            После таких вопросов, ощущение остается что тебя обокрали либо облапошили. Поскольку любой человек попадая в подобную ситуацию, будет ее избегать и уходить от нее разными способами вплоть до «исключения данной ситуации из своего рациона». Как если бы вас поджидали в подворотне, то потом вы туда точно ходить не будете. Так вот, теперь вас специально завели в эту подворотню и смотрят на ваши действия. Вполне очевиден провал, просто психологически и не важно знали вы это раньше или нет. Видимо для того чтобы пройти тест обязательны знания «кунг-фу».
                                              0
                                              Мне кажется всё зависит от предполагаемого уровня Питона. Вас же не удивляет подобная программа:
                                              >>> class Foo:
                                              ...   a = []
                                              ...   def __init__(self):
                                              ...     self.b = []
                                              ... 
                                              >>> foo = Foo()
                                              >>> foo.a.append(1)
                                              >>> foo.b.append(1)
                                              >>> bar = Foo()
                                              >>> bar.a.append(2)
                                              >>> bar.b.append(2)
                                              >>> bar.a
                                              [1, 2]
                                              >>> bar.b
                                              [2]
                                              
                                              , так почему то, что обсуждается в статье удивляет?
                                                –1
                                                Удивляет, потому что подобный код некрасив и путает программиста с областью видимости переменных.
                                                Очевидно, что если мы что-то хотим получить снаружи класса это необходимо явно получать через __init__? и все внутренние переменные так же определять в __init__.
                                                Есть ли реальные и правильные случаи когда используется подобное написание класса?
                                                  0
                                                  Конечно есть. Например когда инстансы должны регистрироваться внутри класса. Это полезно, скажем, когда мы наделали экземпляров, а потом хотим найти их все где-то в одном месте. Где? В классе неплохое место для этого. Другое дело, что называть атрибуты надо более понятно — это да.

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

                                                  Отдельный вопрос касается психологического давления. Человек может быть действительно крутым профессионалом, но иметь проблемы, связанные с уверенностью в себе. В отлаженном рабочем процессе это может не представлять проблемы, а в стрессовых ситуациях вполне может «завалить» соискателя на элементарщине.
                                                  Уметь разгрузить ситуацию, снизить напряжение и вывести собеседуемого в комфортный режим — это искусство интервьюера. Плохой быковатый «помощник» из программистов, которого может позвать HR, чтобы «позадавать технические вопросы» вполне может запороть отличного спеца на вакансию из-за даже неосознанного желания самоутвердиться.
                                                    0
                                                    инстансы должны регистрироваться внутри класса
                                                    а это нормально? Если необходимо вести учет инстансов и обеспечивать их взаимодействие, логичнее было бы вынести эту функцию в отдельный класс и передавать его в __init__? Удобнее написать чуть более длинный, но более понятный код. IMHO.

                                                    О порядке прохождения собеседований — уже много копий сломано. Все пытаются схантить super-star программиста умеющего рефакторить чужой код под минометным обстрелом. И раз подобная практика распространена — значит она приносит свои плоды, иначе бы ее не использовали, или бы все конторы загнулись из-за нехватки программистов среднего звена.
                                                      0
                                                      Все пытаются схантить super-star программиста умеющего рефакторить чужой код под минометным обстрелом.
                                                      А также схантить токаря, умеющего выточить деталь и сварщика, умеющего сварить две детали нетривиальной формы.

                                                      Не знаю, откуда взялся миномётный обстрел (за всю карьеру ни разу в окопах ничего не рефаторил… там другими делами обычно заняты), но вообще рефакторинг — это то, чем занимается программист бо́льшую часть времени. Где-то это может быть 50%, где-то и 90%, но так, чтобы, в основном, человек писал новый код — это, как раз, не уровень синьора. Джуна (или себя?) ещё могут пожалеть первое время и сложных задач на рефакторинг не давать. А синьору — от них никуда не деться.
                                                        0
                                                        Есть такие штуки, как MVP, техдолг, agile и прочие.
                                                        В совокупности получается, что в простых случаях и для MVP вполне достаточно сделать максимально просто и быстро.
                                                        К примеру роль синглтоновой коллекции инстансов вполне может играть класс, если нет и не предвидится необходимости сделать два таких «синглтона».
                                                        Если же такая необходимость вдруг появится, то всегда можно дописать необходимую функциональность, а не предусматривать заранее то, что никогда не пригодится.
                                                    0
                                                    Меня больше смущает то, что после «победы в кунг-фу поединке», мне придется работать с «преступниками». Эти вопросы интересны как факультатив, но как обязательные к выполнению — это напряжёно. Опять же находить и устранять проблемы связанные с подобными нюансами возможно, даже не имея правильного ответа. Поскольку, когда-то наткнувшись на такое, уже по результату работы программы, отыщется подводный камень. Поэтому правильно оценить человека таким вопросом будет трудно — это очередной тест на зубрешку. Те кто их задает, должны организовывать «разминочно-тренировочные площадки» для таких случаев. И подручные инструменты на то и нужны, чтобы программу не на бумаге писать.
                                                  0
                                                  Вывод первых двух строк достаточно очевиден...

                                                  Если очевиден, то это просто вопрос на внимательность. А если не очевиден, то — излишне усложнен, как мне кажется
                                                    0
                                                    Ни то, ни другое. В зависимости от внутреннего устройства языка на вывод третьей строки влияет либо вторая, либо первая. Python, всё-таки, стремится к логичности — потому первая. Но можно себе представить язык, который «сильно быстро делали», где влияла бы и вторая.
                                                    –1
                                                    даладна! опять? а самое смешное то, что подобное пишут сеньеры и это оказывается на проде после ревью.
                                                    жизнь боль
                                                      0

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

                                                      +1

                                                      Я так понимаю, на собеседовании смотрят, загорится ли у человека в глазах жажда убийства при виде этого кода. Если нет – он не подходит.

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

                                                          Ну, это тот случай, когда стОит писать комментарии.

                                                            0
                                                            В комментариях будет написано почему вы решили получить разрешение на нарушение Style Guide, но там не будет написано как это работает. Ваш К.О.

                                                            Так что знать как это работает — всё-таки нужно.
                                                        +1
                                                        Какая-то мутная не очевидная хрень. Зачем так писать? Не вижу реальных примеров чтобы была такая вот необходимость. Другой разработчик не должен шарады разгадывать. Если на такой код нет комментария — застрелить автора и точка.

                                                        P.S.: Не питонист, случайно забрел — заинтересовало.
                                                          0
                                                          Вот вполне рабочий и характерный для Питона пример. Есть в каждом учебнике этого языка.
                                                          Да, пример чисто академический и такое рекурсиями не решают, но сути того, что сеньор-питонист обязан понимать такие вещи это не меняет.
                                                          def factorial(x, _cache={1: 1}):
                                                               result = _cache.get(x, None)
                                                               if result is None:
                                                                   result = _cache[x] = x * factorinal(x - 1)
                                                          
                                                               return result
                                                          
                                                            0
                                                            а скажите, вот так:
                                                            factorial(N)[N] = N

                                                            тоже же можно странное поведение получить?

                                                            UPD: а не, у вас число на выходе. Тогда возьмем для примера функцию топикстартера:
                                                            f(N).append(-100)
                                                            , он как раз ссылку наружу возвращает
                                                              0
                                                              Конечно. В тот самый дефолтный объект добавится -100.
                                                              А ещё кого-то может обескуражить такое
                                                              a = [1,2,3]
                                                              b = a
                                                              b.append(4)
                                                              print(a)  # [1, 2, 3, 4]
                                                              
                                                                0
                                                                Ну, тут то как раз все нормально. Понимать что, в данном случае, переменная лишь ссылка, а мы обращаемся к одному и тому же объекту должен программист на любом языке. В отличие от не очевидного примера статьи.
                                                                  0
                                                                  На самом деле всё-таки не на всех. В Basic или PHP4 не так. Но эти языки, почему-то всё-таки малораспространены.
                                                              0
                                                              Для тех, кто рекурсию объясняет на примере факториала, уготован специальный котел в аду.
                                                                0
                                                                Ну замените факториал на Фибоначи. Код-то особо не изменится, а вот результат — таки да: вместо экспоненциального алгоритма полиномиальных — это всегда хорошо. Так-то можно и линейный сделать… но это — другая история.
                                                              0
                                                              … еще удивляют высказывания типа «этим можно себе выстрелить».
                                                              Да первый же тест типа

                                                              f(1) == [0]
                                                              f(10) == [i*i for i in range(10)]

                                                              покажет, что у вас в коде странное (и дальше вы это пофиксите)

                                                              PS: но я тоже не питонист. Может конечно там что-то тайное сокрыто, чего нам непонять…
                                                                +1
                                                                Непонятно что? Что на «очевидную» функцию в три строки не всегда пишут тесты? Что в каких-то других языках (C++, в частности) подобных «подвохов» нет и что человек, не очень хорошо знающий Python может такую штуку «без задней мысли» написать? А «комбинаторный программист» с помощью StackOverflow может и не такое написать?

                                                                Что именно вам непонятно-то?
                                                                  0
                                                                  непонятна такая жаркая реакция на совершенно проходную задачку.
                                                                  Причем, хоть знаешь, хоть не знаешь «правильного» ответа — я на такие задачки стараюсь даже не начинать решать, а смотреть собеседнику в глаза и говорить спокойным голосом: «это пример того, как писать не надо и этот код мой ревью не пройдет. Можно конечно, если вы так сильно настаиваете попробовать подумать, но в реальной жизни над этим будет думать сам автор сочинения»
                                                                    0
                                                                    Ну вы же понимаете, что в результате вы всего лишь получаете [небольшой] минус в итоговой оценке и продолжение дискуссии?

                                                                    Причём вашу «подачу» развернуть — раз плюнуть. «Отлично, мы подняли логи и выяснили, что автор этого сочинения уволился пять лет назад и сегодня является начальником крупного офиса, ну, скажем, в Facebook. Ваши дальнейшие действия? Предложите ему зарплату в несколько миллионов, чтобы он дюжину строк кода поправил?»
                                                              0
                                                              Можно так:
                                                              def f(x, l=None):
                                                                  l = l or []
                                                                  for i in range(x):
                                                                      l.append(i * i)
                                                                  return l
                                                              
                                                                0
                                                                Это уже второй вопрос — для начала хорошо бы объяснить, чем тот вариант, что в статье, плох и как он будет работать…

                                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                              Самое читаемое