Pull to refresh

Comments 47

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

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

Такие вопросы мне всё-таки больше нравятся, чем "сколько окон в Москве?" :)
Ещё однажды просили посчитать сколько в день расходуется деревянных палочек для мороженого во всех макдональдсах столицы. Понимаю, что интересует подход и обоснование методологии, но тут точно можно подобрать задачи на "подумать" из предметной области.

Наиболее интересен вопрос про скобки, наверное :) и ответ — так писать нельзя и если увижу пойду проверять(читать документацию).
Даже в sql с его ограниченным набором регулярно можно увидеть op1 and op2 and… and op3 or op4… и призадуматься а того ли хотел автор запроса...

Студентам не помешает.
И пусть их "собеседуют" такими вопросами — они это уже будут знать.
Но, главное, повышается шанс на ускорение отладки.
Так что автору публикации респект.

UFO just landed and posted this here

Ну, одно дело — просто сказать "Не делай так", а другое — объяснить, почему. Ну и потом далее по списку:


Сначала ты не знаешь, что нельзя делать то-то.
Потом знаешь, что нельзя делать то-то.
Потом ты понимаешь, что иногда таки можно делать то-то.
Ну, а потом ты понимаешь, что помимо того-то существует еще шестьдесять шесть способов добиться желаемого, и все из них практически равноправны.
Когда тебя спрашивают "как мне добиться желаемого", ты быстро перебираешь в уме эти шестьдесять шесть способов, прикидываешь то общее, что в них есть, вздыхаешь и говоришь: "вообще-то, главное — гармония."
На вопрос обиженных учеников: "а как ее добиться?", ты говоришь: "никогда не делайте то-то".

Про объекты, из того что пришло в голову, можно к примеру проверять что элементы некоторого множества получены в одном блоке кода (да странная идея но мало ли) при этом только для питона(? а может нет) целочисленные объекты в определённом диапазоне будут вести себя по другому, и исходя из примера про print даже в разработчиках языка(точнее интерпретатора, и ещё бы проаерить как оно будет в компилируемой версии, нет единого мнения о том как оно должно быть...

Отличный детектор токсичных собеседований.
Т.е. в вашей компании придётся разбираться с таким кодом? Я вам перезвоню.
PS: единственное, что тут надо знать: что всё это подозрительная муть, которую надо переписать на всякий случай.

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

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

Дык молодёжь тоже не лыком шита! Опыта мало, но соображалка свежая и работает быстро, юношеский максимализм и беспечность, детская наивность. Жизнь не трепала пятничными релизами с восстановлением базы на все выходные и неаккуратными миграциями с потерей клиентских данных, которая обнаруживается только спустя пару недель…
Вот и спрашивает такая кроха: что такое хорошо, а что такое плоха…
Надо же не отмахиваться, а четко внятно объяснить что к чему или уж более менее адекватно послать в нужное место документации, хотя бы.
Ответ вида: "незнаю почему, но так делать нельзя, еще дет так говаривал..." считаю непрофессиональным. А это значит, что синьор девелопер обязан ориентироваться во всех этих тонкостях, а при поиске такого на собеседовании вполне нормально спросить пар утаких вопросов.


Важно только понимать, что невозможно всё знать и помнить, главное уметь думать, искать и разбираться. Так что ответ или отсутствие ответа кандидата по каждому из таких вопросов еще ни о чем не говорит, нужно смотреть в комплексе.

Ну первый вопрос не выглядит каверзным. Лично я на него не ответил, но лишь потому что я никогда не использовал Питон хоть сколько-нибудь серьезно. Но если бы он входил в мой стек — несомненно, знал бы, поскольку это (насколько я теперь уже вижу) вполне логичная, документированная и используемая фича.

А вот второй пример очень похож на баг языка. Оптимизация, которая меняет поведение — это вы серьезно?
UFO just landed and posted this here
Не оправдание. Всё равно странный дизайн языка.
Непонятно: 1 — это все-таки объект или примитив?
Если примитив, то почему этот is в принципе работает?
Если объект, то какого черта используются такие ломающие оптимизации?

Это объект. В питоне всё — объект. Однако есть мутабельные и иммутабельные объекты. В данном случаее если сделать "логичнее", то получится "абсурднее". Ну, в смысле, можно сделать отдельную фазу работы транслятора, в которой вес одинаковые константы будут приводиться к одной, но это придётся делать только чтобы устранить "странное" поведение при нецелевом использовании оператора.


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

Никто никогда не проверяет значения через is, потому что is проверяет идентичность ссылок, а не значений. Он делает то же самое, что и id(x) == id(y). Через is делают быструю и надежную проверку на None, False, True и т. д.

Проверка is True не будет правильно работать, если проверяемый класс только прикидывается True через перегруженный __bool__. Такое точно есть в логических операциях в SQLAlchemy

Конечно, bool(x) совсем не то же самое что x is True. Последний можно использовать для строгой проверки того, что x — в точности True, а не произвольный True-like объект с реализованным __bool__. Такая строгая проверка на идентичность None/True/False часто используется в сериализаторах, а is None встречается в обычном коде вообще везде.

С учетом того, что сейчас программист пишет обычно на нескольких языках(c, java, sql, javascipt), полагаться на порядок операторов, это бомба. Я, например, пишу всегда скобки, и не засоряю мозг какой порядок операторов в конкретном языке.
Параметр а превратился в метод класса, такой же, как при определении метода внутри класса через def a(self).


Тут, как мне видится, можно написать еще лучше, заменив человеческое «превратился» на описание того, что происходит. Насколько я помню, «При доступе к атрибуту экземпляра Python создаст объект типа types.MethodType с полями __func__ и __self__, если этот атрибут был найден в классе и является чем-то вызываемым». Ну и сразу будет понятно, почему для «b» превращений не произошло — его в классе не находили, его нашли в экземпляре.
Насколько я помню, «При доступе к атрибуту экземпляра Python создаст объект типа types.MethodType с полями func и self, если этот атрибут был найден в классе и является чем-то вызываемым».

Превращение функций в методы (bound methods, они же экземпляры MethodType) происходит еще на этапе создания объекта.

О, это ценно. А не помнишь, это где-то в явном виде написано? Я быстро чекнул в доках и там «при создании»:

When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object.

Хм, ты был прав. Документация пишет, что


When a non-data attribute of an instance is referenced, the instance’s class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object.
https://docs.python.org/3/tutorial/classes.html

class A:
    def f1(self):
        pass

def f2():
    pass

a = A()
A.f1 = f2
print(a.f1) # <bound method f2 of <__main__.A object at 0x7f0fd200d610>>

Я почему-то был уверен, что это не так ;)

Согласно ему, значение переменной из замыкания (это переменная i) вычисляется в тот момент, когда вызывается внутренняя функция (наши list_lamba_f).


Тут, как мне видится, можно написать еще лучше, заменив абстрактное «вычисляется» на техническую реализацию. Насколько я помню, «Замыкание сохраняется как ссылка на namespace, из которого значение будет получаться по ключу в момент использования (а не в момент замыкания). Все созданные lambda будут замыкать один и тот же namespace (Замыкание сохраняет ссылку на существующий namespace, откуда замыкается какое-то имя. Замыкание не создает копию namespace и ничего из namespace не копирует). Так как после прокручивания range в этом namespace останется последнее значение i=4, то имено оно будет использовано кодом позднее»
UFO just landed and posted this here
Паттерн видится как одно из решений «проблемы». Где «проблема» в том, что при создании нескольких замыканий они получают один и тот же scope, поиск в котором по имени идентификатора будет происходить позже, когда лямбды будут вызывать. Коварный вопрос проблему подчеркивает одним из возможных способов)
Не нужно таким людей пугать. Тем более на собеседованиях.
Нужно использовать хороший статический анализатор, который не допустит ни одной ситуации из списка: github.com/wemake-services/wemake-python-styleguide
Красиво! :) Первую задачу сразу не сообразил, но с подсказки вспомнил про цепи. Остальное — в новинку. Спасибо!
А есть какое-то обьяснение почему пул малых интов в отрицательную сторону именно до -5?
В чём магичность -5 для питона?

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

Где-то можно почитать на основе чего выбран диапазон до -5 среди негативных чисел?
С 256 довольно очевидно.
А с -5 что-то ни чего толкового не гуглиться.

PS: Нашёл несколько статей — я взял и померял количесвто ссылок на своём проекте. Ок. Возможно в среднем на типовом проекте где-то так и есть, хотя ожидал какого-то более научного обьяснения :)

Я вечером откопал старый топик на mail.python.ru. Раньше диапазон этих "синглтонов" был [-1, 99], затем он был расширен до [-5, 100], но с введением bytes его еще раз расширили до [-5, 256] чтобы покрыть байт.


https://bugs.python.org/issue1436243
https://hg.python.org/cpython/rev/4d1a15fc4748
https://github.com/deadsnakes/python2.4/search?q=NSMALLNEGINTS
https://github.com/deadsnakes/python2.5/search?q=NSMALLNEGINTS

Этот вопрос однажды встретился мне на собеседовании. Он про классы и методы.


Всегда очень с подозрением отношусь к компаниям, которые на интервью спрашивают вопросы вроде: «Вот вам и безобразный и заведомо не работающий код, в котором больше одной ошибки. Какую из них вернет интерпретатор?» — А какая собственно разница? Я готов еще понять, если бы под этим вызовом была бы еще обработка TypeError и NameError с разной логикой, хотя, это еще безобразнее. Сразу вопрос к потенциальному нанимателю: «а какую задачу мне придется выполнять, если надо будет разбирать такой код?». Вы хотите понять, понимаю ли я логику построения и разворачивания цепочки вызовов? Ну так придумайте релевантную практике задачу, где это значимо. Не можете? значит в вашем проекте просто нету таких задач, и этот навык не релевантен вашей позиции, зачем его проверять?

В той компании собеседование было на большой монолит с приличной историей. Кода было всякого не мало.
Такие задачи, мне кажется, дают, чтобы проверить "соображалку" и понимание языка. А релевантные примеры обрезаются до минимума, чтобы кандидату не пришлось вникать в условную "бизнес логику", а сосредоточиться на поведении языка.
А какие задачи вам запомнились на собеседованиях? Что хорошо характеризовало работодателя? Что было приятно решать?

Нормальные вопросы для собеседования. Но лучше сначала показывать майндфак, а потом предлагать объяснить/пофиксить. За незнание заранее минус не ставить.


Аналогичный пример для числа > 256 сработает ожидаемо

А вот PyPy выдаст True вместо False.


Про LOAD_CONST я бы добавил пояснение, что за массив констант такой. Его можно глянуть в test.__code__.co_consts.


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


>>> f = lambda self: self + 1
>>> m = f.__get__(6)
>>> m
<bound method <lambda> of 6>
>>> m()
7

То есть, чтобы исправить пример нужно написать


self.b = (lambda self: None).__get__(self)

тогда функция b привяжется к self и станет методом, в точности как a.

Ой помню, как я когда-то еще плохо знал питон и наткнулся на 4й пример. Когда разобрался — думал, что нашел баг в интерпретаторе. Спросил у лида, кому такое репортить, а он мне переписал примерно вот так:
I = 0
def create_multipliers():
    for _ in range(5):
        yield lambda x: x * I

multipliers_generator = create_multipliers()
next(multipliers_generator)(2)
I = 1
next(multipliers_generator)(2)
I = 2
next(multipliers_generator)(2)
...

И только тогда до меня дошло)
Меня в свое время очень впечатлил следующий каверзный пример из книги Л. Ромальо:
>>> a = {True : "1", 1 : "one"}
>>> print(a)    #?


Вывод print(a)
>>> print(a)
{True: 'one'}



Объяснение
В python3 тип bool реализован как подкласс инта, поэтому хеши True и 1 (а также False и 0) cовпадают. Для словаря, который использует хеш-функцию как индекс это одинаковые объекты.

Можно пойти еще дальше и вспомнить, как считаются хешы для float:
>>> a = {True : "1", 1 : "one", 1.0 : "double one"}
>>> print(a)
{True: 'double one'}



Такой вот коварный питон :)

Вот интересно почему true идёт как 1 а не как -1 в доп коде (255/65535....)

Со вторым примером все еще интереснее:
$ python
Python 3.8.3 (default, Jul  2 2020, 16:21:59) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 123
>>> b = 123
>>> a == b
True
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a == b  # True
True
>>> a is b  # False
False
>>> 
$ cat 1.py 
a = 65537
b = 65537
print(a == b) 
print(a is b) 
a = 251
b = 251
print(a == b) 
print(a is b) 
$ python 1.py 
True
True
True
True

Видимо, второй пример работает так как описано только при запуске в интерактивном режиме в интерпретаторе, а при запуске файла — происходит какая-то компилляция в байткод и в ходе нее что-то оптимизируется, так что две переменных с числом 65537 тоже оказываются одним объектом. Что совсем интересно — в байткоде полученом дизассемблированием pyc лежит вот это:
  1           0 LOAD_CONST               0 (65537)
              2 STORE_NAME               0 (a)

  2           4 LOAD_CONST               0 (65537)
              6 STORE_NAME               1 (b)

  3           8 LOAD_NAME                2 (print)
             10 LOAD_NAME                0 (a)
             12 LOAD_NAME                1 (b)
             14 COMPARE_OP               2 (==)
             16 CALL_FUNCTION            1
             18 POP_TOP

  4          20 LOAD_NAME                2 (print)
             22 LOAD_NAME                0 (a)
             24 LOAD_NAME                1 (b)
             26 COMPARE_OP               8 (is)
             28 CALL_FUNCTION            1
             30 POP_TOP

  5          32 LOAD_CONST               1 (251)
             34 STORE_NAME               0 (a)

  6          36 LOAD_CONST               1 (251)
             38 STORE_NAME               1 (b)

  7          40 LOAD_NAME                2 (print)
             42 LOAD_NAME                0 (a)
             44 LOAD_NAME                1 (b)
             46 COMPARE_OP               2 (==)
             48 CALL_FUNCTION            1
             50 POP_TOP

  8          52 LOAD_NAME                2 (print)
             54 LOAD_NAME                0 (a)
             56 LOAD_NAME                1 (b)
             58 COMPARE_OP               8 (is)
             60 CALL_FUNCTION            1
             62 POP_TOP
             64 LOAD_CONST               2 (None)
             66 RETURN_VALUE
Третий пример вполне рабочий, если его немножко изменить (делать на практике так не надо):

```
class C:
a = lambda self: self.b()

def __init__(self):
self.b = (lambda self: None).__get__(self)

c = C()
c.a()
```
Sign up to leave a comment.