LINUX.ORG.RU

Объектная модель питона

 , ,


2

4

- Сколько нужно архитекторов, чтобы создать язык программирования?
- Сто. Один будет писать язык, а 99 - говорить, что могут сделать лучше.

Так скажем, я решил вспомнить обсуждение по теме треда: Generics в Python или помогите победить mypy

Да, наркоманский питон захватывает мир, и с этим нужно что-то делать. Нет, я не намерен делать питон 4 - я вижу свет в конце тоннеля в рамках третьей версии. Но мне нужна ваша помощь: какие фундаментальные фичи, по-вашему, в питоне вообще не нужны, а какие - должны быть переработаны?

Прежде всего, я хотел бы вспомнить про RPython ( https://rpython.readthedocs.io/en/latest/rpython.html ).
Смысл особенностей языка прост - поддержка вывода типов. В частности, из языка убраны динамические определения классов и функций, динамическая типизация переменных, глобальные переменные стали константами, функции-генераторы транслируются в классы-итераторы и потеряли большую часть своих фич. У RPython есть большой минус - эти его ограничения сильно раздувают код, затрудняют писание и читание.
Итак, мои соображения:

1. Множественное наследование. Его нет даже на уровне C-функций в реализации питона и API расширений. «Но как же интерфейсы?» - возразите вы. Интерфейсы в C++ и Java нужны в роли объявления протоколов вызова методов объекта с целью последующей статической проверки этих протоколов при компиляции, а также для формирования таблиц методов, которые можно использовать независимо от объекта во время выполнения. Эти роли полностью потеряны в питоне, потому нет никакого оправдания их существованию. Мне нравится то, как сделаны интерфейсы в Go - это очень похоже на питоновые ABC: https://www.python.org/dev/peps/pep-3119

2. Генераторы - зло. Это прямо-таки запущенный случай GoTo, когда выполнение не просто бесконтрольно прыгает по коду - оно прыгает по стэкам. Особенно лютая дичь происходит, когда генераторы пересекаются с менеджерами контекста (привет PEP 567). В треде, скорее всего, опишу веселости реализации генераторов в PyPy/RPython. В питоне есть общая тенденция запутывать приложение в тесный клубок связанных изменяемых состояний, что не дает возможности параллелить и оптимизировать выполнение программы, а генераторы - вишенка на этом торте.

3. Изменение классов для существующих экземпляров объектов. Не, я понимаю, что класс в питоне объявляется во время выполнения. Но, блин, зачем в него совать изменяемые переменные? Зачем в старые объекты добавлять новые методы? Причем, попрошу обратить внимание на то, как нужно нагибаться раком для того, чтобы добавить аналогичные методы в сам объект, а не в класс - для этого нужны types.MethodType, function.__get__, functools.partial, и так далее. Методы в питоне вообще понадобились по простой причине - гвидо не придумал других способов сделать короткие имена функций (чтобы не было gtk_button_set_focus_on_click), поскольку не ясно, как выбирать из кучи похожих функций с коротким именем нужную под этот конкретный объект. Так в питоне появились len, iter, next, isinstance, slice, dict, dir, str, repr, hash, type - сейчас это обертки над соответствующими методами классов с подчеркиваниями в имени, а когда-то встроенные простые типы не являлись классами и работали только через эти функции. Так-то, я не вижу особой разницы между записью method(object) и object.method - особенно если method является статичной функцией, которой, в общем-то, все равно, какой первый аргумент (self) принимать.

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

★★★★

Ответ на: комментарий от anonymous

Map и Set как бы вообще про другое. На массивах и объектах так напрямую сделать нельзя.

Нормальных списков так и не подвезли

Потому что они нахрен никому не упёрлись. В ES2020 зато будет typed array.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

это конструкция из ES6

for (let val in a), а что let не из ES6? for/in из ES3, который уже давно протух и не используется. Т.е. в ЖС как раз сделали то, о чём ты тут мечтаешь.

for...in - очень старый, а for...of - новый.

А о чем я тут мечтаю? К слову, я сейчас рабствую в основном на JS, так что я как бы немного в курсе, что там сделали в JS.

byko3y ★★★★
() автор топика
Ответ на: комментарий от anonymous

Да уж, всего-то набили гору шишек и через 20 лет сделали наконец костыль для «массивов».

К слову, сейчас советуют вообще не использовать for...in, а только for (let key of Object.keys(obj)) {...}.

byko3y ★★★★
() автор топика
Ответ на: комментарий от no-such-file

ES6 это такой приемлимый скриптоязык на фоне полного говна, которым он был до того. Питон 15 лет назад был уже приемлимым, никакой революции не требовалось, только доработка реализаций. Потом конечно наворотили. Вот эта разработка через PEP с максимальным удовлетворением «просьб трудящихся» по-моему полная фигня. Тяжелый случай комитетского дизайна. Гвидо так себе диктатор оказался, потом вообще психанул и сбежал.

anonymous
()
Ответ на: комментарий от no-such-file

Через 10. forEach появился ещё в es5 в 2009

Исключительно для массивов. А JS в основном оперирует таки объектами.

byko3y ★★★★
() автор топика
Ответ на: комментарий от no-such-file

В ES2020 зато будет typed array.

Это и есть нормальный список. Ну хорошо, что не в 2050.

anonymous
()
Ответ на: комментарий от anonymous

Ну и в js потом докинули специализированные коллекцие: List, Map. Видать не так прикольно всё делать через Object.

Ну вот же, значит я не один, такой поехавший, вижу, что для разных хранилищ нужны разные интерфейсы, и они не должны пересекаться.

byko3y ★★★★
() автор топика
Ответ на: комментарий от anonymous

Питон 15 лет назад был уже приемлимым

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

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

удобство и понятности операций

И что конкретно тут неудобного?

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от anonymous

ES6 это такой приемлимый скриптоязык на фоне полного говна, которым он был до того.

+

Питон 15 лет назад был уже приемлимым, никакой революции не требовалось, только доработка реализаций.

Для скриптов до 10 тыс строк? Да. Но бизнесу нужны сотни тысяч! И тут старый питон не канает.

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

Гвидо с самого начала ублажал умственно отсталых. Так что не «оказался», а «всегда таким был».

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

Исключительно для массивов

Object.keys() отдаёт как бы массив. Это, внимание, то чем нормальные языки отличаются от питона — там можно объединять разные абстракции получая новую сложность.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

15 лет назад питон был ничем не хуже собратьев по пыхоплеяде. Вот пхп4 с перлом тебя в продакшне не смущают? А что из себя представляли интерпретаторы js помнишь? Без слез на все это не взглянешь сейчас.

anonymous
()
Ответ на: комментарий от no-such-file

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

Постерегись, ты вплотную подошел к тайнам IT индустрии. Тайна эта зовется... синдром утенка. То первое, что осилил утенок, он всю жизнь считает это своими мамой и папой. Отсюда ты можешь познать причину популярности таких кусков дерьма, как Cfront/C++, Java, PHP, Git, MS Office, и других.

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

сложение умеет складывать только ассоциативные массивы перезаписью

Там все массивы ассоциативные. И не перезаписью, а наоборот, берётся то, чего в левом массиве нет.

php > print_r([1,2,3] + [5,6,7,8]);
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 8
)

array_merge рассматривает числоподобные ключи особо, объединяя такие ключи в контексте неассоциативного массива (имитируя обычный массив). Когда в массиве есть и строковые ключи и числовые это конечно усложняет ситуацию для понимания (хотя логика сохраняется, её можно проследить и ничего не ломается). Но в том то и суть, которую я тебе пытаюсь донести. На практике такое не используется, массивы делаются либо только с числовыми ключами, либо с ключами из строк и поэтому никаких практических проблем это не вызывает. С академической точки зрения это трэш и содомия, да. Но почему-то в академическом питоне проблемы с выразительностью (тривиальные конструкции делают не то, что хотелось бы), а в трэшовом пыхе и жс нет.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от byko3y

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

anonymous
()
Ответ на: комментарий от byko3y

Ну вот же, значит я не один, такой поехавший, вижу, что для разных хранилищ нужны разные интерфейсы, и они не должны пересекаться.

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

anonymous
()
Ответ на: комментарий от byko3y

Забавно в этом то, что люди, узнавшие про синдром утенка, начинают видеть его вообще везде. Казалось бы, причём здесь синдром утёнка.

anonymous
()
Ответ на: комментарий от no-such-file

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

Да, в пыхе тривиальных конструкций для списков просто нету. Я бы не стал это рассматривать как абсолютный плюс.

byko3y ★★★★
() автор топика
Ответ на: комментарий от anonymous

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

Для справки, питон стал заменой именно лиспа.

Если говорить о языках для обучения, то абсолютно нет разницы. Одним из первых моих языков стал Си, и я сомневаюсь, что кто-то из вас назовет его простым. В какой-то степени я осознал свою невхерственность, когда наблюдал, как челы рядом не могут ничерта понять, как они получают ошибки, но не могут их исправить, а я легко писал программы на сихе, и даже проги повышенной сложности щелкал как орешки. В этом плане Java, Python, PHP дают отрицательный отбор, когда бездарные кодер пробуют писать код, и получается «я у мамы программист». Он, может быть, и мог бы в конце-концов освоить норм язык, но ему уже за 25, в мозгу щелкает переключатель - и теперь он необучаем.

Это реальность, в которой мы с тобой вынуждены жить, реальность засилия бездарных кодеров и доминирования инструментов их уровня, которые нужны индустрии для впаривания говна и раздувания пузырей. Лисп там не нужен, он сложный, в нем слишком много возможностей, макака не сможет его прочитать. У меня вот сидит макака, которая не может JS прочитать, а ты гришь «лисп» - с дуба рухнул, что ли? Он увидит язык, и подумает, что это такая шутка, ну типа смайликов много.

byko3y ★★★★
() автор топика
Ответ на: комментарий от anonymous

Забавно в этом то, что люди, узнавшие про синдром утенка, начинают видеть его вообще везде. Казалось бы, причём здесь синдром утёнка.

«Если у вас нет паранойи, то это не значит, что за вами не следят».

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

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

anonymous
()
Ответ на: комментарий от byko3y

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

Язык тут ни при чём. Это зависит от способа мышления, а он, ты удивишься, у разных людей отличается. Есть люди, которые осмысляют сущности разбирая их на винтики и когда нужно что-то решить, они из винтиков пробуют собирать разные решения. Есть другие люди, которые осмысляют что-то по предоставленному извне шаблону и когда нужно решить задачу они либо знают готовое решение (шаблон, как решать) и выдают его, либо не знают и впадают в ступор. Это заложено с рождения и практически невозможно изменить (можно только натренироваться «понимать ход мыслей» людей с другим типом мышления и в какой-то мере самому так думать ставя себя на место другого).

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от byko3y

В предыдущей серии:

синтаксис питона не поддерживает работу с изменяемыми данными
списки [] питона? Это оптимизация кортежей, костыль, потому что операции с пересозданием кортежей на большом числе значений становятся очень медленными.
я сам не могу придумать лаконичных и совместимых способов добавить четкое разграничение изменяемых и неизменяемых объектов в рамках имеющегося языка. Ваши предложения?

Сегодня будет про полиморфизм, больше в диспетчеризацию, то бишь, ad-hoc полиморфизм во время выполенния.
https://ru.wikipedia.org/wiki/Полиморфизм_(информатика)
«Полиморфизм в языках программирования и теории типов — способность функции обрабатывать данные разных типов»

Статья: https://en.wikipedia.org/wiki/Multiple_dispatch#Use_in_practice
Исследователи заявляют, что в исследованных системах 13–32% функций используют диспетчеризацию по одному аргументу (a.k.a. виртуальный метод), и 2.7–6.5% функций — по двум и более. Но я вам скажу ужасающую новость: когда вы просто используете арифметические операции над разными типами, или даже банально присваивание, то вы применяете статическую диспетчеризацию по одному и более аргументу. Полиморфизм намного ближе, чем кажется, и на самом деле все популярные языки очень активно употребляют полиморфные операции.

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

Питон же фундаментально полиморфичен, потому что большинство его конструкций применимы (в том числе с генерацией исключения) к произвольным типам. Язык сам по себе изначально не задавал никаких правил о реализации этого полиморфизма: у вас есть просто код «a = b + c», а вы дальше сами разбирайтесь, как вы будете это выполнять. Как же Гвидо решил это выполнять?

 — явная диспетчеризация во время выполнения: «if (Py_TYPE(obj) == &PyLong_Type) {long val = PyLong_AsLong...}» на Си или аналогичное на питоне через type();
 — приведение полиморфного аргумента к единому типу: «PyArg_ParseTuple()» на Си или конструкторы типов на самом питоне, вроде «this.number = int(param1); this.text = str(param2)» — в данном случае явная реализация диспетчеризации оказывается уже в конструкторе, а сам вызов конструктура является неявной диспетчеризацией. В стандартной библиотеке много подобных конструкций, правда, часто они вызваны банальной необходимостью сделать копию объект известного типа (напоминаю из прошлого эпизода, что синтаксис питона не поддерживает операций с изменяемыми данными);
 — методы/функции/поля объекта и его классов. Это уже не похоже на простые таблицы виртуальных методов в каноничных классовых ООП языках — это алгоритм поиска подходящего атрибута по сложному и не всегда ясному алгоритму.

Итак, возьмем банальную операцию сложения. Что делет питон для того, чтобы сложить два объекта разных типов? Я прочитал весь код реализации механизма сложения в CPython, но, честно говоря, до сих пор не пойму, что курил ее автор, и зачем было так это делать. Грубо говоря, опишу это так: будет вызван метод сложения одного из аргументов или встроенная быстрая функция сложения чисел. Пардон, а где здесь полиморфизм по двум аргументам? А нету его: здесь подраузмевается, что оба метода у аргументов будут одинаковыми и/или эти методы будут выдавать ошибку, если обнаружат несовместимый метод сложения/тип у другого аргумента.

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

Вернемся к нашим баранам: Гвидо не придумал, как сделать множественный полиморфизм. В языке, фундамент которого представляет собой полиформизм всех аргументов. Есть и утешительный вывод: он здесь не одинок, и куча создателей ООП языков соснули со множественным полиморфизмом. К сожалению, чисто классовая модель решительно провальна в этом плане, потому что дает исключительно одиночный полиморфизм — через виртуальный метод класса.

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

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

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

К слову, начиная с 3.4 в питоне появилась так называемая «клиника аргументов» ( Argument - Monty Python ), которая призвана автоматически генерировать код обработки аргументов для функции с чисто сишными аргументами. Так сказать «не прошло и двадцать лет».... а не, уже прошло: 3.4 выпущен в 2014, а первый релиз питона был в 1991.

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

Но это все эти потуги в питоне представляют собой полумеры. Питону нужна распределенная диспетчеризация, примерно как trait в расте (не важно, во время компиляции, исполнения, интерпретации, или чего бы то ни было еще). То есть, если я складываю объект А с объектом Б, то вызывается не метод объекта, и не встроенный в интерпретатор костыль, а функция, которая объявлена как функция сложения с аргументами типа А и Б. Проблемы возникают разве что с самим определением «тип». Поскольку в питоне очень распространена утиная типизация, а наследованию мы объявили войну, то класс объекта не может служить его типом в данном случае. Скорее, нужно что-то вроде интерфейса, протокола, растовых типажей (trait). Например, объект имеет __iter__ - он становится автоматически обладателем типажа «итератор». Правда, если так широко загребать объекты под типажи, то очень быстро возникает ситуация, когда конфликтующие типажи представляются абсолютно одинаковыми атрибутами. С другой стороны, не хотелось бы скатиться в крестовые обобщения с многоуровневыми объявлениями типов, как то в ренжах, где объявления шаблонов раз в пять больше, чем сам код реализации алгоритма. Потому есть смысл вдобавок к автоматическому присвоению типажей сделать явное объявление типажей, вроде «вот эти мои атрибуты принадлежат такому-то типажу, а никакому не другому».

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

byko3y ★★★★
() автор топика
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от byko3y

Пока что в общих чертах идея мне нравится тем, что она должна будет минимально изменить конечный код (или даже вообще оставить как есть)

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

anonymous
()
Ответ на: комментарий от anonymous

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

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

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

Лучше подумай какие особенности жаваскрипта позволили его так ускорить, и почему с питоном не прокатило

А я бы предложил еще и посмотреть на причины, по которым LuaJIT была написана в одно рыло, и только в 2018 году команда V8 смогла догать LuaJIT по скорости числодробилок на самом языке.

Проблема с питоном (и в меньшей степени с JS) заключается в том, что питон представляет собой тысячи интерпретаторов, которые при комбинировании составляют астрономическое кол-во вариантов выполнения, для оптимизации которых нужно астрономическое число ресурсов. В JS бардка был чуть меньше, потому что сам язык был проще, но все равно тот же for..in очень долго пришлось оптимизировать - настолько это нелогичный и замороченный оператор.

Что я хочу донести: такая ситуация не является неотъемлимым свойством питона, а является исключительно свойством его кривой эталонной реализации, которая выполнена без архитектурного планирования; эти проблемы решаемы, хоть они и не могут быть решены с сохранением 100% совместимости, потому что отдельные либы иногда используют кривые особенности реализации. Я не верю в перспективу реализации подобных механизмов в виде питона 4, потому что не все еще на третий перекатились, а предлагаемые мной изменения на самом деле глубже, чем разница между 2 и 3, потому что затрагивают фундаментальную модель исполнения программы и портирование не может быть выполнено банальным автоматическими транслятором. Потому я думаю сделать некую дополнительную реализацию объектной системы, вроде фреймворка, чтобы можно было по кускам переползать на новую модель. Ну то есть: ссылку на новый объект ты можешь прочитать как родной, потому что имеющийся механизм входных аргументов всеяден и как бы предоставляет неявную прокладку; записать в новый объект извне можешь, но через промежуточный слой, который в этом сценарии уже ближе к новому фреймворку.

Короче, жаваскрипт это правильный питон (как бы у хейтеров не полыхало).

У жаваскрипта те же проблемы: непредсказуемость поведения после значительной перестройки кода, вплоть до «исправил баг - всплыло десять новых»; очень бедные механизмы параллелизации, которая не предусмотрена самим языком, а именно, язык опирается на сложные общие изменяемые состояния, из-за чего даже Tcl смотрится перспективнее; JS по прежнему имеет большой потенциал для написания неоптимизируемого кода, и наоборот, Си-подобный asm.js хорошо оптимизируется, правда, плохо читается.

Отсюда возник проект Dart, в котором все убогие особенности JS были устранены, но внезапно возникла проблема с совместимостью между Dart и JS, которая довольно хреновенькая. Потому в вебе Dart-а не будет, а вот в отдельной новой платформе он вполне взлетит, то есть, там, где нет проблем совместимости со старыми библиотеками, потому что старых библиотек не существует на новой платформе. Так-то порог вхожденяи в дарт низок, и не он является основной проблемой - это вам не ReasonML.

По этой причине нет абсолютно никаких шансов для нового языка взлететь на старой платформе. А вот для какого-то нового фреймворка - вполне очень даже есть.

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

Где достаешь упорин форте?

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

А ты точно программист? Tcl - это язык высокого уровня. Что-то вроде lisp, но без скобок и с другим подходом к синтаксису. А то, про что ты говоришь - его БИБЛИОТЕКА, которая называется Tk. Которая адаптирована для Python в виде Tkinter… В общем - лучше иногда есть, чем говорить

silver-bullet-bfg ★★
()
Ответ на: Где достаешь упорин форте? от silver-bullet-bfg

Tcl - это язык высокого уровня. Что-то вроде lisp, но без скобок и с другим подходом к синтаксису

Спасибо за объяснение, но тикль совершенно не похож на лисп.

А то, про что ты говоришь - его БИБЛИОТЕКА, которая называется Tk. Которая адаптирована для Python в виде Tkinter

Дело в том, что проклятый питон и Tk/Tcl имеют один общий главный поток, в котором крутится главный цикл гуя. Если человек хочет, чтобы интерфейс был отзывчив, то один из самых популярных, но кривых способов сделать это - создать поток питона, который будет виснуть в отдельном потоке ОС, пока тикль продолжит делать параллельной обработки. Если ты посмотришь на Modules/_tkinter.c: _tkinter_tkapp_mainloop_impl, то увидишь, что главный цикл отпускает GIL только на время обработки/ожидания одного события, после чего обратно его берет. То есть, параллельный питоновый поток будет работать только тогда, когда _tkinter_tkapp_mainloop_impl=>Tcl_DoOneEvent ожидает события. И наоборот, когда работает и висит параллельный поток, может обработаться только одно событие, после чего LEAVE_TCL зависнет в take_gil.

Эта проблема не актуальна для hello world приложений, но становится тем более острой, чем больше становится приложения, чем дольше оно начинает провисать, чем больше появляется в интерфейсе отзывчивых фич, которым нужны постоянно работающие функции в главном потоке для отрисовки. По этой причине, если ты хочешь обрести реальную отзывчивость с питоном, то ты должен учить тикль, чтобы писать на нем истинную многопоточность без GIL.

byko3y ★★★★
() автор топика
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от byko3y

В связке tcl/tk/твой-код пистон явно лишний. Или всё пишется на тикле в простейшем случае, или дергаешь из тикля свои либы на си. Пистон здесь будет только все усложнять. В любом случае понадобятся оба интерпретатора, это уже выглядит скверно само по себе. К тому же tk удобнее всего использовать из родного тикля, и документацию смотреть будешь тиклевую, так что всяко придется его подучить.

anonymous
()
Ответ на: комментарий от anonymous

Как раз очень похож, только вместо списков строки.

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

От того, что в языке есть eval, он еще не становится лиспом. К тому же, превращение процедуры в строку в Tcl делается через info args/body, что есть запрос о состоянии интерпретатора, и потом непонятно что с возвращаемыми строками делать. Пропускать через регулярное выражение? Дело в том, что структура процедур в тикле не соответствует фундаментальному типу - строке. Как и в лиспе и во многих других языках, процедуры в тикле представляют собой дерево, где на одном уровне команды представляют собой списки из команд или такой же список вызова команды с аргументом, а каждый аргумент может быть отдельным списком. Списки и деревья для тикля есть ( https://tools.ietf.org/doc/tcllib/html/struct_tree.html#section3 ), но они уже используют внутренние нестроковые хранилища, а для состыковывания со строками используется сериализация.

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от anonymous

В связке tcl/tk/твой-код пистон явно лишний. Или всё пишется на тикле в простейшем случае, или дергаешь из тикля свои либы на си. Пистон здесь будет только все усложнять.

Да, я согласен, что это слишком сложно. Но беда тикля заключается в отсутствии простых способов работать со структурированными данными. Как бы вроде получается, что язык простой, но, с другой стороны, довольно бесполезный, и питон со своими структурированными объектами легко вытесняет его с ниши. В итоге индустрия пришла к тому, что tcl/tk лишние, а если нужно потоки - они пишутся на C/C++.

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

В прошлой серии:

Си глубоко полиморфичен, то есть, позволяет однообразно обрабатывать разные типы данных
«Вариться в супе» не является методом собаки
целая куча функций из стандартной библиотеки питона представляют из себя отдельный интерпретатор

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

Прежде всего, давайте начнем с того, зачем нужны классы, и как реализованы многие классы в стандартной библиотеке питона. А именно, они скомпилированы статически, в виде расширений на Си. Очевидно, что они ждут вполне конкретный ограниченный набор входных параметров, для проверки которых применяют собственные велосипеды, и хранят внутренние данные в фиксированном формате, который не может внезапно измениться с массива на строку. Огромное кол-во проблем в программах на чистом питоне возникла из-за того, что присваивание питоне абсолютно слепо, оно просто берет любую ссылку объект на вход, и присваивает эту ссылку левому выражению. Например:

>>> a = [1, 2, 3]
...
>>> a = '15'
...
>>> for i in map(lambda x: x*2, a):
>>>    print(i)
11
55

Ожидается вывод

2
4
6
Удачи вам искать подобную ошибку в код на сотни тысяч строк.

Конечно, мы не можем заставлять программистов использовать только один тип в одной переменной, поскольку запихивание кучи типов в одну переменную стало уже сложившейся традицией. Даже банальный None — это уже другой тип, NoneType. Однако же, есть законченные куски кода — модули и объекты, которые в реализации на Си более-менее блюдут чистоту типов, но при написании на питоне внезапно эту чистоту теряют. Давайте рассматрим на простейшем примере теоретическую новую модель:

>>> class A():
>>>   def __init__(self, value):
>>>     self.val = value
>>>
>>> a = A('2')
>>> a.val = []
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign str (not "list") to str
>>> a.myattr = []
>>> a.myattr = 2
Здесь создается класс A с полиморфным типом атрибута val, тип которого, однако, выводится из конструктора. Естественно, кто-то извне может захотеть добавить свой собственный атрибут в объект — этот атрибут будет уже находиться на уровне экземпляра объекта, и дальше уже создающий и использующий экземпляр код будет разбиратсья, нужно ли проверять тип или нет.

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

>>> class A():
>>>   def __init__(self, value):
>>>     self.val = value
>>>
>>> def func():
>>>   a = A(None)
>>>   a.val = 2
>>>   print(a.__dict__)
>>>
>>> func()
{'val': 2}
Здесь вызывающий код создает как бы A<None or Int>, в результате чего функция успешно выполняется. Естественно, при строго построчной интерпретации проверить тип не представляется возможным, потому код создания и инициализации объекта завернут в функцию.

Я плавно перехожу к проблеме отсутствие времени компиляции: интерпретатор предполагает, что вы в любой момент имеете возможность перейти любого состояния в почти любое — через двойную сплошную, овраг, сбив пасущуюся на лугу корову перетащите поезд с железной дороги на речку, и поплывете по речке всё с теми же звуками «чух-чух», которые были у класса «поезд». Глобальная изменяемость состояния - это важная особенность, которая пронизывает весь язык, из-за нее, к слову, питон отвратительно натягивается на функциональщину, потому что в своем фундаменте является языком глобального изменяемого состояния, в противовес чистым функциям без состояния. При всем при этом, однако, вы не можете банально заменить стандартный тип «list», который используется, в том числе, в list comprehension через коды операций BUILD_LIST и LIST_APPEND в CPython — вы можете найти логичное оправдание такой негибкости на фоне гибкости? Как по мне, так это больше напоминает (опять-таки) модель «груда беспорядочных фич», «лишь бы как-то заработало».

Еще замечу, что кто-то (я) может захотеть сделать приведение типа аргумента при присвоении, вроде «a.val = int(newval)». Собсна, стандартная библиотека часто делает приведение, но больше в аргументах и уже после выполения самого присвоения. Для перезаписи операций приваивания атрибутов в классах есть такие служебные механизмы, как __setattr__ и __setattribute__, а начиная c 2.2 добавились еще и дескрипторы с их __set__ ( https://www.python.org/dev/peps/pep-0252/ ). Но давайте не забывать: мы не любим делать классы просто так, и предпочли бы оперировать локальными переменными функции, нежели создавать класс контекста и экземпляр этого класса просто для того, чтобы поработать с парой значений - в худших традициях C++/Java/C#. В принципе, можно было бы сделать через дескрипторы переопределение оператора присвоения с минимальными изменениями: достаточно начать уважать в простых переменных методы __set__, __get__, __delete__, таким образом позволяя сделать проверку типа аргумента присвоения во время выполнения, например:

>>> a = StrictDict({'first': 1 })
>>> a = { 'dummy': 666 }
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: StrictDict: key 'first' is missing in assignment source
К слову, это один из вариантов решения двусмысленности операций со списками — мы можем явно создать copy-on-write массив, который будет принимать новые значения, но не перезаписывать себя ссылкой на новый объект-массив, а делать copy-on-write ссылку на имеющиеся данные:
>>> a = COWList([1, 2, 3])
>>> b = a
>>> a.append(4)
>>> b.append(5)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 5]

Интересно, что в питоне на уровне Си уже есть довольно обширный слой статичных атрибутов, которые внутри называются «слоты». Это не те слоты, которые __slots__ у класса. Это сишная ссылка в структуре класса на методы, доступ к которым также можно получить через питон, и даже можно определить в новом классе на питоне методы с этим именем, и эти методы попадут в слот класса. В основном такой механизм применяется к методам, но есть также и данные: тот же __slots__ класса, который сам является слотом PyHeapTypeObject->ht_slots, или __dict__, который хранится в самом экземпляре объекта по смещению PyTypeObject->tp_dictoffset. Как вы видите, без статических структур реализовывать интерпретатор было не прикольно.

Но и явные описания типов и слотов повсеместно в коде нужны только там, где у кода кишки торчат наружу, как то было у Си, где каждый модуль тусовался сам по себе, а потом линкер соединял их вместе, мол «это двенадцатиперстная, ее нужно будет подключать к тонким кишкам; а вот толстая кишка — она будет выводиться в попу, не вздумай выводить ее в ротешник», и все типы этих подключений должны были быть описаны с обоих сторон подключения. Однако, если вы полностью контролируете алгоритмы, то вам не нужны типы. Взять ту же знаменитую строчку великих людей «Кукарек<кококо> кукарек = new Кукарек<кококо>()», которая в современной жаве приняла намного более приятную форму «var кукарек = new Кукарек<кококо>()». Также, из опыта людей по написанию фронтендов на ReasonML выяснилось, что практически единственное место, где нужны явные типы - это интерфейсы к JS либам. Отсутствие времени компиляции в питоне на фоне того, что полный код программы обычно известен до выполнения, в итоге отнимает очень много сил у разраба, который потом в этом минном поле неопределенности пытается проверять ошибки в программе при помощи тестов, и все равно пропускает гору простейших ошибок, потому что по мере роста кол-ва кода все варианты удерживать под контролем становится всё сложнее и сложнее.

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

Аффтар, пишы исчо!.. очень интересно пишешь и дельные вещи :)

Sahas ★★★★☆
()
Ответ на: комментарий от no-such-file

Это зависит от способа мышления, а он, ты удивишься, у разных людей отличается. Есть люди, которые осмысляют сущности разбирая их на винтики и когда нужно что-то решить, они из винтиков пробуют собирать разные решения. Есть другие люди, которые осмысляют что-то по предоставленному извне шаблону и когда нужно решить задачу они либо знают готовое решение (шаблон, как решать) и выдают его, либо не знают и впадают в ступор. Это заложено с рождения и практически невозможно изменить (можно только натренироваться «понимать ход мыслей» людей с другим типом мышления и в какой-то мере самому так думать ставя себя на место другого

вот эта вот тирада напоминает «Programmer’s Stone» линк про паковщиков и картостроителей. у него ещё сиквел был, Reciprociality про M2, «глупость это паразиты мозга».

так он там совершенно противоположные выводы делает: изменить можно, научить другому типу мышления можно. картостроители это постоянно делают – «карта это не территория, это достаточно полезное её отображение». а паковщики да, думают квадратно-гнездовыми «пакетами знаний», паттернами, лол. и если шаблона нет – ступор. а если карты нет или она негодная у картостроителя – он её выкидывает и составляет другую карту.

то есть: научить таки можно, если ты картостроитель, хотя бы в зачаточном виде. и практически невозможно если «хочешь быть передовым – сей квадратно-гнездовым» паковщик. им это просто не нужно.

где-то там и кроется разгадка: чтобы кого-то кому-то научить (например, самому себя новому типу мышления) нужно испытывать в этом потребность, кризис-катарсис-просветление это вот. а если и так всё ок, и потребности такой не возникает.

но думается что научить другому способу мышления таки можно (если конечно нужно).

anonymous
()
Ответ на: комментарий от byko3y

tl;dr в питоне костыли по историческим причинам.

чего только не делают, закатывая солнце вручную – лишь бы мультиметоды из лиспа и родовые функции не реализовывать.

а вот чего думаешь по поводу языка Nim, например? мультиметоды там реализованы. есть dispatch trees и более правильный полиморфизм вдобавок к одиночной диспетчеризации.

как тебе этот Nim в качестве более «правильного питона» ?

anonymous
()
Ответ на: комментарий от byko3y

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

баш не похож, потому что там нет нормальных списков. а вот к примеру rc из Plan9, inferno sh, es и xs (потомки rc) где есть списки и лямбды – уже похожи.

anonymous
()
Ответ на: комментарий от byko3y

Таким образом, несмотря на формальную гомоиконность и наличие eval, гомоиконности по факту нет, и язык не способен работать с кодом как с данными, хоть и может выполнить как код подаваемую извне строку.

red или rebol в этом смысле лучше – есть полноценные списки, которые не строки. правда потом они вычисляются руками, ну типа как fexprs.

anonymous
()
Ответ на: комментарий от byko3y

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

ну вот в Nim например полноценные AST деревья и макросы с метапрограммированием на AST есть.

anonymous
()
Ответ на: комментарий от byko3y

Огромное кол-во проблем в программах на чистом питоне возникла из-за того, что присваивание питоне абсолютно слепо

ты мне лучше скажи, кто в питоне виноват в GIL? где он там возникает?

anonymous
()
Ответ на: комментарий от byko3y

Для того, чтобы это понять, не стоит читать объёмные статьи на тему «как так получилось?», «кто виноват?» и «что делать?».

Достаточно(self): просто(self): написать(self): пару(self): строк(self): на(self): этом(self): языке(self):.

EXL ★★★★★
()
Ответ на: комментарий от EXL

откуда возникают быдлокодеры

Достаточно(self): просто(self): написать(self): пару(self): строк(self): на(self): этом(self): языке(self):.

и сразу становится скуууууучнооо.... из Reciprociality про M0:

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

По сравнению с эндорфином имеется важное отличие. Избыток допамина выделяется после периода скуки, а не в начале его. В то время как человек не может производить больше эндорфинов без испытания некоторого стресса, избыток допамина можно производить просто не делая ничего интересного! Таким образом, человек, находящийся в состоянии скуки достаточно долгое время, чтобы установился новый баланс химического состояния мозга, попадется на крючок собственного допамина и будет способен поддерживать свою собственную привычку просто вводя себя в скуку! Хотя привыкание может ослаблять некоторые истощающие (debilitating) последствия нахождения в состоянии отключения, он даже не начнет компенсировать утраченные способности.

Приводящим в движение органом, таким образом, является петля положительной обратной связи, посредством которой человек, попавший на крючок собственного допамина (Secretion — Секреция, гормональная система), будет поддерживать состояние скуки для стимуляции производства допамина (Constraint — Принуждение). Как и в случае других вызывающих привыкание (addictive) ситуаций, чем выше доза, тем больше желание, так что петля положительной обратной связи всегда будет приводить к стремлению увеличивать состояние скуки и уровень допамина в рамках возможного.

Переносчик (Vector)

anonymous
()
Ответ на: комментарий от anonymous

Это зависит от способа мышления, а он, ты удивишься, у разных людей отличается. Есть люди, которые осмысляют сущности разбирая их на винтики и когда нужно что-то решить, они из винтиков пробуют собирать разные решения. Есть другие люди, которые осмысляют что-то по предоставленному извне шаблону и когда нужно решить задачу они либо знают готовое решение (шаблон, как решать) и выдают его, либо не знают и впадают в ступор. Это заложено с рождения и практически невозможно изменить (можно только натренироваться «понимать ход мыслей» людей с другим типом мышления и в какой-то мере самому так думать ставя себя на место другого

где-то там и кроется разгадка: чтобы кого-то кому-то научить (например, самому себя новому типу мышления) нужно испытывать в этом потребность, кризис-катарсис-просветление это вот. а если и так всё ок, и потребности такой не возникает.

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

если и так всё ок, и потребности такой не возникает

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

В мире есть два фундаментальных типа людей: те, кто создают, и те, кто повторяют

да, два фундаментальных типа людей. те, которые делятся на два типа, и которые в два типа не вмещаются :))

при этом создателю тяжело бездумно повторять, а повторяющему тяжело создавать

а кому сейчас легко? и кроме того, почему им не должно быть тяжело и сложно с непривычки – а потом полегче, как насобачатся? слишком легко быть и не должно , иначе сформируется привыкание, лень – и вирус глупости :)

anonymous
()

Множественное наследование

ну его, ненужно

Генераторы

нужно, в скриптухе удобно

Изменение классов для существующих экземпляров объектов.

Костыльненько, да, но не вижу криминала.

PS

А вот принудительной (с попыткой автоматического привидения типа) статической типизации ему не хватает, да. Не так как с ЯП у которых статистическая типизация и есть опциональный var для динамики, а чтобы var из коробки по умолчанию, пока не объявишь тип сам.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.