Comments 65
Спасибо за интересную статью, заинтересовался Go.
А я открыл для себя ООП без классов в Lua. Тоже очень красивая по-своему система.
А я открыл для себя ООП без классов в Lua. Тоже очень красивая по-своему система.
И отличным дополнением к данной теме будет эта хабрастатья: Является ли Go языком ООП?
если бы бабушка... писала на Go, как бы изменился мир…
А вообще интересен разбор и анализ межпроцессового обмена данных в Go (не межпотоковый, го-рутинный, а именно межпроцессовый).
А вообще интересен разбор и анализ межпроцессового обмена данных в Go (не межпотоковый, го-рутинный, а именно межпроцессовый).
На самом деле это тема не совсем Go-specific, различных вариантов много:
начиная от golang.org/pkg/net/rpc/ и github.com/OneOfOne/netchan
и заканчивая github.com/gdamore/mangos на любой вкус, как говорится.
начиная от golang.org/pkg/net/rpc/ и github.com/OneOfOne/netchan
и заканчивая github.com/gdamore/mangos на любой вкус, как говорится.
Имхо, заканчивая github.com/gocircuit/circuit
и вот еще в тему: github.com/docker/libchan
Ну как бы любой интерфейс, который вам предоставляет ОС. В случае с Linux это может быть простейший локальный сокет. С Windows я думаю все по сложнее, но тем не менее реализуемо.
Go не любит late binding, но любит messaging, а концепция «свойств и поведения» объектов реализована великолепно, и это дает повод называть Go великолепным ОО-языком.
Что за глупости? Что значит «не любит late binding, но любит messaging»? Интерфейсы и вызов методов конкретных типов через интерфейсы и есть late binding.
Интерфейсы и вызов методов конкретных типов через интерфейсы и есть late binding.
И да, и нет.
Late-binding — это больше о внутренней реализации, и в Go (по крайней мере, в gc-компиляторах: 6g, 8g) используется смешанный вариант — что-то среднее между early-binding и late-binding: таблицы методов для interface- и conrete-типов генерируются на этапе компиляции, а финальная таблица создается на лету, при этом кешируется после первого просчета.
Так что тут в Go mixed-binding ;)
Вот тут подробно расписано: research.swtch.com/interfaces
Касательно messaging — насколько я знаю, Алану Кею были близки идеи CSP, и реализация в Go, наверняка близка, к тому, что он хотел бы увидеть в OO-языках, но я не углублялся, чтобы ненужные холивары не разводить :)
Golang это недо ООП язык.
у ООП есть чёткое определение: инкапсуляция, полиморфизм, наследование.
Golang реализует только часть.
у ООП есть чёткое определение: инкапсуляция, полиморфизм, наследование.
Golang реализует только часть.
Пример какой-то не полный. Зачем Person включает Sources и зачем ему FullName, не понятно. Но в целом очень хорошо, спасибо.
Вообще например такие фичи как embedding и использование в нем не только непосредственно объектов, но и указателей/ссылок на объекты, это одна из тех удивительно простых вещей, про которые думаешь когда впервые узнаешь: ну почему это еще в Си не было сделано, это же так просто и красиво!
func (s TwitterSource) Find(word string)
Все таки receiver лучше через указатель передавать, а то копироваться будет.
func (s *TwitterSource) Find(word string)
Пусть копируется )
Это дешево, и для этого примера не релевантно.
Это дешево, и для этого примера не релевантно.
Товарищи минусующие, я, конечно, понимаю, что у вас есть свое представление о дешевизне копирования и я даже готов послушать ваши аргументы, но эта тема поднималась в Go-комьюнити с 2009-го года не раз и ответ на вопрос «использовать поинтер или значение в качестве ресивера» даже вынесено в FAQ — рекомендую познакомиться: golang.org/doc/faq#methods_on_values_or_pointers.
Для тех кому вправду интересно: pointer в качестве ресивера используется если значение наверняка нужно менять или если тип ресивера — большая структура. Для базовых и простых типов, которые не нужно менять — рекомендуется использовать значение. Там есть еще парочка моментов, которые стоит учитывать, но в целом как-то так.
В примере статьи значение менять не нужно и тип очень простой. Минусуйте дальше :)
Для тех кому вправду интересно: pointer в качестве ресивера используется если значение наверняка нужно менять или если тип ресивера — большая структура. Для базовых и простых типов, которые не нужно менять — рекомендуется использовать значение. Там есть еще парочка моментов, которые стоит учитывать, но в целом как-то так.
В примере статьи значение менять не нужно и тип очень простой. Минусуйте дальше :)
Правильный краткий ответ на вопрос: «нет, не является, но это не важно» :)
А язык хороший.
А язык хороший.
Хм, прикольная игрушка на фотке. Сразу же нашел в Украинском магазине и заказал.
Подскажите магазин(prooflink)
Да, gopher у них удачный очень вышел. Мне больше намного нравится, чем Google-овский — на конференции dotGo в Париже раздавали. Оно такое чувство, будто его камазом сбили и переехали ) А этот крутой. Я его, кстати, подарил девочке из Будапешта, которая движок Prezi писала — она на Lviv IT Arena делала презентацию, как Go в Prezi используется, и это была одна из самых мимимишных презентаций, которые я видел )))
Я, кстати, как-то этой игрушке фотосет знатный устроил :) Вот такие карточки получились:
Некоторые ругают Go, что нельзя
а надо
func (s string) Find(word string) ([]Message, error) {...}
а надо
type Plaintext string
func (s Plaintext) Find(word string) ([]Message, error) {...}
Я думаю, это правильное решение. Допустим, разрешили built-in типам добавлять свои методы. Что будет, если чей-то package, включенный в мой код переопределяет метод String() для int? Или два разных package-а, реализуют одинаковый метод для built-in типа. Как такие вещи разруливать?
Кроме того, новое поведение у типа — подразумевает, что этот тип представляет некую новую сущность. Ну там «источник данных», а не просто «строка». Тоесть это разные сущности, и правильнее дать им разные имена.
Кроме того, новое поведение у типа — подразумевает, что этот тип представляет некую новую сущность. Ну там «источник данных», а не просто «строка». Тоесть это разные сущности, и правильнее дать им разные имена.
А вы не подскажете, как внутри
вызвать имплементацию Find у string, если такая имеется?
что-то не нашел в интернетах.
func (s Plaintext) Find(word string) ([]Message, error) {...}
вызвать имплементацию Find у string, если такая имеется?
что-то не нашел в интернетах.
«имплементацию Find у string» — это поиск подстроки, я так понимаю? Операций на строках достаточно много и они вынесены в стандартную библиотеку, в package strings: golang.org/pkg/strings
Find в данном случае вам нужно или Contains (содержит или нет) или Index (позиция, с которой содержит).
Find в данном случае вам нужно или Contains (содержит или нет) или Index (позиция, с которой содержит).
не важно, что такое Find. Важно, что мы хотим вызвать одноименный метод.
Что нужно написать вместо super.MethodABCD(), чтобы вызвать MethodABCD() для T?
Если мы вызовем просто MethodABCD(), мы рекурсивно закрутимся.
type AliasOfT T
func (s T) MethodABCD() { .../* надо вызвать это */ ...}
func (s AliasOfT) MethodABCD() { super.MethodABCD() }
Что нужно написать вместо super.MethodABCD(), чтобы вызвать MethodABCD() для T?
Если мы вызовем просто MethodABCD(), мы рекурсивно закрутимся.
Приведите к нужному типу и вызывайте: T(s).MethodABCD()
Но вообще немного weird дизайн. Я такого использования не встречал, это калька с class-oriented языков.
Но вообще немного weird дизайн. Я такого использования не встречал, это калька с class-oriented языков.
func (s AliasOfT) MethodABCD() { T(s).MethodABCD() }
В Go используется правило регистра первой буквы имени — если название начинается заглавной буквы — это public-доступ, если со строчной — private. Вначале может показаться посягательством на свободу слова неудобством, но с первых строк на Go, понимаешь, насколько это было удобное решение.
… и если ты хочешь сделать публичный метод приватным или приватный публичным — нужно пройтись по всему проекту и сменить регистр. Оооок, очень удобно. И правильно, нечего без IDE программировать на серьёзных языках.
Ну да, рефакторить код лучше не ручками :) GoRename конкретно этот use-case делает очень простым.
Изобрели проблему и героически её побороли. Оооок.
Если вы чаще, чем раз в месяц меняете туда сюда видимость методов, то, наверное да, Go вам не подходит. А если еще и «я так не привык» для вас затмевает все остальные преимущества, которые дает Go (и которые даже не затрагивались в этой статье) — то тем более )
Очевидно, сам Go здесь ни при чём — это разработчики на Go придумали такую, кхм, странную концепцию. А Двораком не пользуетесь?
Извините, что игнорирую ваш сарказм, но расскажу один момент.
Когда я только познакомился с Go, самым странным для меня было то, что компилятор выдавал ошибку на неиспользованный импорт или неиспользованную переменную. И, более того, изменить это поведение нельзя было — ну там, какой-то опцией командной строки например.
WTF, подумал я — что за ограничение свободы слова и посягательство на базовые права человека? Да какое право они имеют мне диктовать, оставлять или нет неиспользованные импорты? Хочу и оставляю, я свободный человек! Да и вообще, это же офигеть, как неудобно. Что это они вообще придумали, идиоты. Я то знаю как нужно — я же привык как в других языках.
Прошел год, когда я словил себя на мысли, что не представляю, во что бы превратились списки импортов в моих программах, не будь это ошибкой компиляции. Наверное в то же, что и в типичных C++ программах в больших проектах. Более того, сейчас я понимаю, насколько это решение было правильным, хоть и казалось спорным по-началу.
Роб Пайк, кстати, рассказывал подробно по всем дизайнерским решениям языка — что за этим стояло, сколько месяцев или лет они обсуждали все возможные решения и почему пришли именно к такому. У меня есть огромное доверие к Кену и Робу — думаю, их опыт в программировании и работы с разными программистами и командами больше моего в огромное количество раз, и когда мне вначале, еще не испробованная фича кажется «неправильной» — скорее всего, я просто сноб и боюсь изменить свои убеждения. Но хорошо, что я не боюсь :) Те вещи, за которые я бы раньше на C++ или Python даже не взялся, сейчас я пишу за пару дней. Чего и вам желаю. :)
Когда я только познакомился с Go, самым странным для меня было то, что компилятор выдавал ошибку на неиспользованный импорт или неиспользованную переменную. И, более того, изменить это поведение нельзя было — ну там, какой-то опцией командной строки например.
WTF, подумал я — что за ограничение свободы слова и посягательство на базовые права человека? Да какое право они имеют мне диктовать, оставлять или нет неиспользованные импорты? Хочу и оставляю, я свободный человек! Да и вообще, это же офигеть, как неудобно. Что это они вообще придумали, идиоты. Я то знаю как нужно — я же привык как в других языках.
Прошел год, когда я словил себя на мысли, что не представляю, во что бы превратились списки импортов в моих программах, не будь это ошибкой компиляции. Наверное в то же, что и в типичных C++ программах в больших проектах. Более того, сейчас я понимаю, насколько это решение было правильным, хоть и казалось спорным по-началу.
Роб Пайк, кстати, рассказывал подробно по всем дизайнерским решениям языка — что за этим стояло, сколько месяцев или лет они обсуждали все возможные решения и почему пришли именно к такому. У меня есть огромное доверие к Кену и Робу — думаю, их опыт в программировании и работы с разными программистами и командами больше моего в огромное количество раз, и когда мне вначале, еще не испробованная фича кажется «неправильной» — скорее всего, я просто сноб и боюсь изменить свои убеждения. Но хорошо, что я не боюсь :) Те вещи, за которые я бы раньше на C++ или Python даже не взялся, сейчас я пишу за пару дней. Чего и вам желаю. :)
github.com/bradfitz/goimports крайне облегчает жизнь
Ну, тогда еще не было goimports :)
Кстати, я им немного попользовался и вернулся обратно на go fmt (при сохранении всмісле) — во-первых, как-то уже привык добавлять импорты сам перед тем, как использовать какие-то вещи из них — не добавив, банально автодополнение не сработает, а удалять через :GoDrop легче простого, не сходя с нужной строки. А во-вторых, там есть баг, до которого у меня руки не дошли разобраться — но imports в некоторых случаях, неверно угадывает путь и вставляет неправльные import-ы.
Кстати, я им немного попользовался и вернулся обратно на go fmt (при сохранении всмісле) — во-первых, как-то уже привык добавлять импорты сам перед тем, как использовать какие-то вещи из них — не добавив, банально автодополнение не сработает, а удалять через :GoDrop легче простого, не сходя с нужной строки. А во-вторых, там есть баг, до которого у меня руки не дошли разобраться — но imports в некоторых случаях, неверно угадывает путь и вставляет неправльные import-ы.
Концепция кстати вполне интуитивная. Немного контринтуитивно, что вот так например нельзя.
func foo()
{...
}
Это другая тема, это про точки с запятой. Опять же, это все в спецификации, которая читается целиком за час.
Формально, в Go используются точки с запятой, но они не обязательны — лексер их сам расставляет, руководствуясь простыми правилами. В данном примере лексер ставит точку с запятой после ')' — поэтому и ошибка. Этот пример, кстати, там же, в спеке языка :)
golang.org/doc/effective%5Fgo.html#semicolons
Формально, в Go используются точки с запятой, но они не обязательны — лексер их сам расставляет, руководствуясь простыми правилами. В данном примере лексер ставит точку с запятой после ')' — поэтому и ошибка. Этот пример, кстати, там же, в спеке языка :)
golang.org/doc/effective%5Fgo.html#semicolons
Спасибо, читал:)) Все же
-встроенные типы особенней самодельных
-встроенный sort() может быть полиморфным, а самодельный нет
-на вопрос «When generics?» Rob Pike отвечает «We are done.» и представляет go generate
это все прекрасно аргументировано и с этим можно нормально работать и Pike — commander и голова. Но расстановка семиколонок и навязанный стандарт gofmt — для меня слишком proprietary and opinionated. Go сейчас мой любимый язык, более того Plan9 acme by Rob Pike — мой рабочий редактор, однако диктат отцов основателей местами чрезмерный что ли.
-встроенные типы особенней самодельных
-встроенный sort() может быть полиморфным, а самодельный нет
-на вопрос «When generics?» Rob Pike отвечает «We are done.» и представляет go generate
это все прекрасно аргументировано и с этим можно нормально работать и Pike — commander и голова. Но расстановка семиколонок и навязанный стандарт gofmt — для меня слишком proprietary and opinionated. Go сейчас мой любимый язык, более того Plan9 acme by Rob Pike — мой рабочий редактор, однако диктат отцов основателей местами чрезмерный что ли.
Согласен, что встроенные типы особым статусом наделены, но неспроста же.
Насчет generics и «commander и голова» — ну, это же не «Пайк единолично не хочет generics» :) Хочет же, хоть и неохотно ) Но не видит пока-что способа действительно красиво это сделать.
Я лично пока-что не понимаю, какой есть use-case, когда необходим generics. Те примеры, которые я видел решаются интерфейсами, а иногда через reflection (что не слишком красиво, согласен). Я продолжаю искать в реальных задачах способ наткнутся на ситуацию, когда упрусь в необходимость generics — но пока что этого не случалось, и адекватных ответов по этой теме я не видел. Люди, как правило, просто хотят решать задачу тем же путем, которым они привыкли в прошлых языках.
Acme гляну, интересно )
Насчет generics и «commander и голова» — ну, это же не «Пайк единолично не хочет generics» :) Хочет же, хоть и неохотно ) Но не видит пока-что способа действительно красиво это сделать.
Я лично пока-что не понимаю, какой есть use-case, когда необходим generics. Те примеры, которые я видел решаются интерфейсами, а иногда через reflection (что не слишком красиво, согласен). Я продолжаю искать в реальных задачах способ наткнутся на ситуацию, когда упрусь в необходимость generics — но пока что этого не случалось, и адекватных ответов по этой теме я не видел. Люди, как правило, просто хотят решать задачу тем же путем, которым они привыкли в прошлых языках.
Acme гляну, интересно )
Reflection'ы же медленные, и с кешированием код получается несколько громоздким.
Согласен. Но все равно хочу увидеть задачу, в которой Go by design без дженерикс не справляется.
Вот, к примеру, хорошая реализация B-Tree, которая зачастую в других языках решается с помощью generics. По-моему, очень красиво и элегантно:
godoc.org/bitbucket.org/santucco/btree
Вот, к примеру, хорошая реализация B-Tree, которая зачастую в других языках решается с помощью generics. По-моему, очень красиво и элегантно:
godoc.org/bitbucket.org/santucco/btree
Я лично пока-что не понимаю, какой есть use-case, когда необходим generics.
func Sort(func compare(a,b T) bool, []T) []T
func Filter(func fine(a T) bool, []T) []T
func Map(func apply(a T) Ta, []T) []Ta
func Fold(func combine(a, b T) Tc, []T) Tc
Стандартный набор функциональщины (сортировка, фильтр, мапинг, свертка) на слайсе в первую очередь
Воот — вот об этом я и говорил:
Посмотрите, как реализован stdlib-овский sort, или приведенный выше B-Tree.
Это просто другой подход к проблеме. Можно спорить о том, какие минусы и какие плюсы этого подхода, и даже спорить — а считать ли этот подходит тоже как вид generics, но уж точно на «Go тут фейлится» не подходит.
Люди, как правило, просто хотят решать задачу тем же путем, которым они привыкли в прошлых языках.
Посмотрите, как реализован stdlib-овский sort, или приведенный выше B-Tree.
Это просто другой подход к проблеме. Можно спорить о том, какие минусы и какие плюсы этого подхода, и даже спорить — а считать ли этот подходит тоже как вид generics, но уж точно на «Go тут фейлится» не подходит.
Ну и не убедительные примеры
- stdlib sort использует reflection, что медленно и велосипед
- godoc.org/bitbucket.org/santucco/btree построен на encoding/binary, то есть не typesafe, а скорее untyped
stdlib sort использует reflection, что медленно и велосипед
В каком месте и для чего sort использует reflection? Желательно прямо строчку кода укажите :)
godoc.org/bitbucket.org/santucco/btree построен на encoding/binary, то есть не typesafe, а скорее untyped
нет, encoding/binary используется исключительно для чтения/записи в с storage (io.ReadWriter) — это задача сериализации/десереализации данных. Сам же b-tree алгоритм построен исключительно на использовании интерфейсов.
Поймите, generics — это написание алгоритмов в type-независимой манере. Go дает возможность это делать с помощью интерфейсов — ваш тип должен реализовать методы, необходимые для работы алгоритма — и наслаждайтесь. В С++ эту проблему решили иначе — темплейтами, в Java — еще иначе — и везде за счет потерь в скорости, читаемости и/или эффективности. И люди, привыкшие к тем или иным решениям, просто чувствуют себя без них неудобно в новом языке — отсюда и эти «Пайк, спаси, введи темплейты». Но это неправильно.
Или вот еще — когда для каких-то особых случаев людям советуют использовать reflect или interface{} с кастингом — они начинают рассказывать про «это медленно» и вообще, мы хоть это за zero costs получить. Блин, конечно медленно — а в в других языках, generics, думаете бесплатно получается?
В конце-концов да, тем кто прямо без темплейтов жить не может — (уже почти) есть go generate и были какие-то ужасные (как по мне) решения вроде gonerics.
Вобщем я все еще в поиске реальных use-кейсов, где Go без имплементации C++/Java-style generics не справляется красиво.
А насчет Хиндли-Милнер — не знаю, и мне сложно проанализировать, поскольку с языками его использующего не работал и не сильно понимаю ценность.
Хиндли-Милнер нужен, чтобы
C примерами кстати убедили, невнимательно исходники читал, виноват))
//имея
func Btree(node T) Btree T {...}
//написать
var bt Btree int
//так же легко как
var sl [] int
//
//и typesafe
type T; //type parameter
func twotime([]T) []T {...}
//или
func twotime(bt Btree T) bt Btree T {...}
//вместо untyped
func twotime(sl []interface{}) []interface{} {...}
C примерами кстати убедили, невнимательно исходники читал, виноват))
Вот тут Russ Cox рассказывает про Hindley-Milner, почему это не есть так гуд, как это кажется в теории:
To take one example, Hindley-Milner type inference is very well understood and I think generally accepted in the programming language community as a good feature. But new ML programmers inevitably hit the situation where changing one part of a program causes a mysterious type error in a seemingly unrelated part. The solution is that in practice one writes type signatures for most functions. Hindley-Milner type inference is beautiful research, but it’s not as beautiful in practice. In Go, the only type inference is that if you say var x = e, the type of x comes from e. This rule is simple and predictable and doesn’t suffer from the kinds of “spooky action at a distance” of more complex type inference algorithms. I miss having more complex inference sometimes, but Go’s simple rule handles at least 90% of the cases you care about, with none of the unpredictability or mystery.
Но расстановка семиколонок и навязанный стандарт gofmt — для меня слишком proprietary and opinionated.
А по-моему,
gofmt
— одна из самых замечательных вещей в Go. Никаких тебе больше code style guides. Никаких споров о tabs vs spaces и о размерах отступов. Не надо больше думать, переносить фигурную скобку на следующую строчку или нет.Весь код единообразен и глаз быстро привыкает именно к такому форматированию, что крайне положительно сказывается на легкости чтения.
В Python похожая реализация, private поля начинаются с двух подчеркиваний. Дело привычки.
Что? В какой момент в питоне появились приватные поля? Не вводите людей в заблуждение. Во-первых, в большинстве проектов «приватные» поля принято начинать с одного нижнего подчеркивая, а не с двух. Во-вторых, два нижних подчеркивания стараются использовать только для служебных «магических» методов вроде __len__(). И в-третьих, и те и другие все равно можно изменять, просто с двумя подчеркиваниями добавляется имя класса (что кстати ломает нормальное использование в дочерних классах).
Вы не правы. 1) С одного подчеркивания обычно называются «внутренние» поля, которые не нужны клиенту, но могут быть нужны наследникам. По идеологии тут ближайшее — protected поле, хотя это только соглашение имен, защиты на уровне языка нет.
2) Использование двух подчеркиваний ведет к автоматическому переименованию поля к виду _<classname>__<fieldname>, таким образом исключается возможность обратиться к полю родителя, не используя интроспекции (так как self.__field у дочернего класса приведет к обращению к self._child__field).
Извне пользоваться прямым именем, начинающимся с __ также запрещено.
Таким образом, полем в явном виде можно пользоваться только внутри класса. Такое поведение делает такие поля приватными.
3) Магические методы имеют имя __<имя>__, приватные не имеют двух подчеркиваний в конце.
см. PEP8 (__double_leading_underscore)
2) Использование двух подчеркиваний ведет к автоматическому переименованию поля к виду _<classname>__<fieldname>, таким образом исключается возможность обратиться к полю родителя, не используя интроспекции (так как self.__field у дочернего класса приведет к обращению к self._child__field).
Извне пользоваться прямым именем, начинающимся с __ также запрещено.
Таким образом, полем в явном виде можно пользоваться только внутри класса. Такое поведение делает такие поля приватными.
3) Магические методы имеют имя __<имя>__, приватные не имеют двух подчеркиваний в конце.
см. PEP8 (__double_leading_underscore)
По поводу пункта 2. Создайте класс Foo и определите поле self.__bar в __init__(). Создайте foo = Foo() а потом спокойно присвойте foo._Foo__bar любому значению. Все прекрасно работает без всякой интроспекции (кстати, что такое интроспекция в питоне? гм, getattr() с __dict__-ом чтоли?). Так что ничего они не приватные. Приватность — это невозможность изменить поле извне, в т.ч. в дочерних классах. И раз уж об этом зашла речь, окей, в том числе для этого придумали __slots__.
PEP8 это, конечно, здорово, но в реальном мире, в котором написаны к примеру matplotlib, twisted и tornado никто никогда в здравом уме такую свинью с двумя подчеркиваниями разработчикам не подкладывает. Можете привести пример известного, крупного проекта, где «приватные» поля начинаются как пепе?
PEP8 это, конечно, здорово, но в реальном мире, в котором написаны к примеру matplotlib, twisted и tornado никто никогда в здравом уме такую свинью с двумя подчеркиваниями разработчикам не подкладывает. Можете привести пример известного, крупного проекта, где «приватные» поля начинаются как пепе?
Хотя нет, даже возня со __slots__ не поможет. Нельзя добиться приватности в Питоне, не тот язык)
> foo._Foo__bar
Это не прямое обращение по имени, а скорее хак. Да, это не интроспекция, согласен.
Но не в этом суть, это просто механизм языка, который позволяет сделать переменную в рамках одного класса, которая не наследуется.
Если что, в Java тоже можно достать приватное поле. От этого они не менее приватные.
Ну вот специально поискал по OpenStack'у, нашел кучу примеров использования. У вас какое-то предубеждение к этому) Вполне нормальный механизм.
Это не прямое обращение по имени, а скорее хак. Да, это не интроспекция, согласен.
Но не в этом суть, это просто механизм языка, который позволяет сделать переменную в рамках одного класса, которая не наследуется.
Если что, в Java тоже можно достать приватное поле. От этого они не менее приватные.
Ну вот специально поискал по OpenStack'у, нашел кучу примеров использования. У вас какое-то предубеждение к этому) Вполне нормальный механизм.
между прочим, поискал еще по matplotlib, нашел как пример:
github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/backends/backend_svg.py#L97
Не повсеместно, но присутствует ;)
github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/backends/backend_svg.py#L97
Не повсеместно, но присутствует ;)
Sign up to leave a comment.
Golang и ООП