Comments 11
Этот же набор вопросов можно (частично) использовать для обоснования того, что питон был плохо спроектирован.
Ну как плохо... Изначально он предполагался не для того, чтобы делать на нем что-то большое и сложное, а для того, чтобы делать что-то маленькое и очень быстро. И для этого он был спроектирован лучше некуда.
С Javascript то же самое.
А какие именно вопросы? Я, максимум, мог бы только про третий вопрос так сказать.
ответил ниже, промахнулся
Увидел, спасибо. Но за исключением третьего пункта, не понял, как остальное говорит про плохое проектирование языка. Про GIL ещё спорно конечно, но всё же он решал определенные проблемы
Это субъективно, разумеется. По всем пунктам можно сделать критику критики почему так надо было реализовать.
Например (5) squares_of_evens = [i * i for i in range(10) if i % 2 == 0]
Это то, за что ругали Perl. Это нечитаемая конструкция для тех, кто не работает постоянно с такими конструкциями. Вопрос появился имсенно потому что это нетривиальная и лишняя конструкция. У современного программиста есть более важные задачи, чем изучать такие особенности. Разрешает такие конструкции как# Только открытые issues open_issues = [issue for issue in issues if issue['state'] == 'open']
(7) - метод '__init__', который должен был быть конструктором и не прятаться во всякие @dataclass. Есть несколько способов создать члены класса, а должен быть только один.
(8) yield - в многопоточных программах его проще не использовать чем использовать. Да и в async тоже. "Умение использовать генераторы — признак разработчика, который задумывается об эффективности и масштабируемости своего кода" - ага, на пару потоков размасштабировать и потом должго искать источник проблем.
(9) декораторы. Интересно, насколько они замедляют программу. И насколько усложняют её понимание. "Понимание более сложных концепций языка" - это было актуально пару лет назад и действительно упрощало программирование, сейчас в связи с появлением ИИ актуальность приобретает приход в программирование непрограммистов и им нужно именно понятность языка без неочевидной логики, которую вносят декораторы.
(10) GIL - это то что я ненавижу при использовании MicroPython. Мало того, что питон сам по себе тормоз, так ещё и искусственно ограничен. Есть процессор с 20-ю ядрами, но использовать нельзя.
Спасибо за развёрнутый ответ.
(5) comprehensions, на мой взгляд, очень удобны в использовании и лаконичны, даже в вашем примере, читается естественно
[ проблема для проблемы из списка проблем если состояние проблемы = открыто]
образно выражаясь :) субъективщина конечно. и конечно же, можно наворотить нечитаемого с таким синтаксисом (ваш пример, по моему мнению, вполне читаем). но много какой синтаксис, и не только в питоне, можно использовать для создание нечитаемого кода
(7) init не то что бы прячется, он есть, и ничего не мешает его реализовать в своём датаклассе, просто зачем тогда использовать датакласс
(8) с помощью yield в асинхронщине можно сделать батчевую/порционную выгрузку откуда то и завернуть это в итоге в красивый async for. или сделать контекстный менеджер для async with или просто with. тут точно не понимаю какая здесь архитектурная проблема
(9) декаратор это же фактически синтаксический сахар, который по итогу просто вызывает функцию, делая что то ещё. на сколько вызов функции замедлит программу? безусловно, можно сделать очень тяжелый декоратор, как и функцию.
(10) на сколько знаю, гил был нужен для удобства разработки бибиотек и потокобезопасности, но не эксперт
3,5,7,8,9,10
Некоторые пункты спорные, но если бы их не было было бы лучше.
Хешируемость (Hashability):
tupleявляется хешируемым объектом, поэтому его можно использовать в качестве ключа в словаре (dict) или как элемент множества (set).listне является хешируемым.
Доказывать на собеседовании, что tuple хешируемый - главная ошибка.
Просто запусти вот это и все станет понятно:
a = tuple([[1,2,3],1])
d = {a:5}Хешируемость и изменяемость — это совершенно разные понятия никак не связанные друг с другом.
У tuple (как и у большинства контейнеров в python), хеш берется не от самого контейнера, а от элементов внутри него.
Хешируемость определяется банальными магическими методами eq и hash. Ты можешь даже list сделать хешируемым))
Изменяемость же определятся логикой работы этих объектов в CPython.
p.s. Доп вопрос на собес: если ты создашь свой класс, например:
class A:
passА потом создашь его экземпляр и добавишь как ключ в словарь, то все сработает:
a = A()
d = {a:1}Но сразу после этого вы можете добавить полей в этот класс:
a.lol = 10и при этом его хеш не сломается:
print(d[a]) -> 1Как же так? - велком в коменты
Поправьте меня, если не прав.
Мне кажется, что class хэшируемый и наследуется от object (неявно). Аттрибут lol как бы не является частью класса, как например ключ в словаре, а ссылается на ячейку памяти
Тут же всё зависит от реализации hash дандер метода, который может (и действительно) не зависеть от наличия\отсутствия аттрибутов класса или экземпляра
Нашел что по дефолту хэш в классах считается как id(a) // 16
class A:
pass
a = A()
hash(a) # 144368393563
id(a) // 16 # 144368393563И т.к. при изменении аттрибутов, id не меняется, хэш остаётся тот же
10 вопросов на собеседовании Junior Python-разработчика. Часть 1