Статьи

Python для людей Java

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

С точки зрения философии, Python — это почти полная противоположность Java. Он отказывается от статических типов и жесткой структуры в пользу рыхлой песочницы, внутри которой вы можете делать в основном все, что захотите. Возможно, Python о том, что вы можете сделать, в то время как Java о том, что вы можете сделать.

И все же оба языка по-прежнему черпают вдохновение, восходящее к C. Они оба являются императивными языками с блоками, циклами, функциями, назначениями и инфиксной математикой. Оба активно используют классы, объекты, наследование и полиморфизм. Оба имеют особые исключения. Оба обрабатывают управление памятью автоматически. Они даже компилируются в байт-код, который работает на виртуальной машине, хотя Python компилирует прозрачно для вас. Python даже взял несколько подсказок от Java — стандартные модули logging и unittest основаны на log4j и JUnit соответственно.

Учитывая это совпадение, я думаю , что разработчики Java должны чувствовать себя как дома с Python. И поэтому я прихожу к вам с легкой пропагандой Python. Если вы дадите мне шанс, я могу показать вам, чем Python отличается от Java, и почему я нахожу эти различия привлекательными. По крайней мере, вы можете найти некоторые интересные идеи, чтобы вернуться к экосистеме Java.

(Если вам нужен учебник по Python, у документации по Python есть хорошая . Кроме того, это с точки зрения Python 3! Python 2 все еще довольно распространен в дикой природе и имеет несколько синтаксических различий.)

Синтаксис

Давайте сначала уберем это с пути. Вот привет мир:

 print("Hello, world!") 

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

 from collections import Counter def count_words(path): words = Counter() with open(path) as f: for line in f: for word in line.strip().split(): words[word] += 1 for word, count in words.most_common(10): print(f"{word} x{count}") 

Python ограничен пробелами. Люди часто имеют твердые мнения по этому поводу. Я даже думал, что это было еретическим, когда я впервые увидел это. Сейчас, десятилетие или около того, это кажется настолько естественным, что мне трудно возвращаться к брекетам. Если вас это оттолкнет, я сомневаюсь, что смогу убедить вас в обратном, но я призываю вас хотя бы ненадолго забыть об этом; это на самом деле не вызывает серьезных проблем на практике и устраняет приличный шум. Кроме того, разработчики Python никогда не должны спорить о том, куда следует идти.

Помимо этой эстетической разницы, большая часть этого должна выглядеть знакомой. У нас есть несколько номеров, некоторые назначения и некоторые вызовы методов. Оператор import работает немного по-другому, но имеет тот же общий смысл «сделать эту вещь доступной». Цикл for Python очень похож на цикл for -each в Java, только с немного меньшей пунктуацией. Сама функция ограничена с помощью def вместо типа, но она работает так, как вы ожидаете: ее можно вызвать с аргументами, а затем вернуть значение (хотя это не так).

Только две вещи действительно необычны здесь. Один из них — блок with , очень похожий на « try -with-resources» в Java 7 — он гарантирует, что файл будет закрыт в конце блока, даже если в нем возникло исключение. Другой — синтаксис f"..." , довольно новая функция, которая позволяет интерполировать выражения непосредственно в строки.

Вот и все! Вы уже прочитали немного Python. По крайней мере, это не язык совершенно другой планеты.

Динамическая печать

Это, вероятно, очевидно из этого примера, но в коде Python не так много типов. Не в объявлениях переменных, не в аргументах и ​​не возвращаемых типах, не в макете объекта. Все может быть любого типа в любое время. Я еще не показал определение класса, так что вот тривиальное.

 class Point: def __init__(self, x, y): self.x = x self.y = y def magnitude(self): return (self.x ** 2 + self.y ** 2) ** 0.5 point = Point(3, 4) print(point.x) # 3 print(point.magnitude()) # 5.0 

Даже x и y не объявлены как атрибуты; они существуют только потому, что их создал конструктор. Ничто не заставляло меня переходить в целые числа. Я мог бы пройти поплавками, или, возможно, десятичными или дробями .

Если вы использовали только статические языки, это может звучать как хаос . Виды теплые, уютные и уютные. Они гарантируют … ну, возможно, не то, что код на самом деле работает (хотя некоторые могут не согласиться ), но что-то . Как вы можете положиться на код, если даже не знаете, что что-то имеет правильный тип?

Но подождите — у Java такой гарантии тоже нет! В конце концов, любой объект может быть null , верно? Это практически никогда не объект правильного типа.

Вы можете думать о динамической типизации как о полной сдаче null проблемы. Если нам все равно придется иметь дело с этим, мы могли бы также принять это и заставить его работать на нас — отложив все на время. Ошибки типа становятся нормальными логическими ошибками, и вы справляетесь с ними таким же образом.

(Для противоположного подхода, см. Rust , который не имеет null значения — или исключений. Я все же предпочел бы написать Python, но я ценю, что система типов Rust не всегда тихо обманывает меня.)

В моем методе magnitude не имеет значения, что self.x — это self.x число, число с плавающей точкой или любое self.x число. Нужно только поддерживать оператор ** и возвращать то, что поддерживает оператор + . (Python поддерживает перегрузку операторов, поэтому это может быть что угодно.) То же самое относится и к обычным вызовам методов: любой тип является приемлемым, если он работает на практике.

Это означает, что Python не нуждается в генериках; все уже работает в общем. Нет необходимости в интерфейсах; все уже полиморфно со всем. Нет downcasts, no upcasts, нет аварийных люков в системе типов. Не нужно сталкиваться с API, требующими List когда они могли бы работать так же хорошо с любым Iterable .

Ряд общих шаблонов становится намного проще. Вы можете создавать объекты-оболочки и прокси-серверы без необходимости изменения потребляющего кода. Вы можете использовать состав вместо наследования для расширения стороннего типа — без необходимости делать что-то особенное для сохранения полиморфизма. Гибкий API не требует дублирования каждого класса в качестве интерфейса; все уже действует как неявный интерфейс.

Философия динамического набора текста

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

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

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

В отличие от многих динамических языков, Python заблуждается на ранних этапах — в любом случае, во время выполнения. Например, чтение из переменной, которая еще не существует, вызовет исключение, как и чтение несуществующего ключа из dict (например, Map ). В JavaScript, Lua и аналогичных языках вы бы молча получили null значение в обоих случаях. (Даже Java возвращает null для отсутствующих ключей Map !) Если вы хотите вернуться к значению по умолчанию, у dicts есть методы для более явного выражения этого.

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

У вас меньше статических гарантий, но, по моему опыту, большинство ошибок типов сразу обнаруживаются … потому что первое, что я делаю после написания некоторого кода, это пытаюсь его запустить! Любые другие должны быть пойманы вашими тестами — которые вы должны писать на любом языке и которые Python делает относительно легко.

Гибридная парадигма

И Python, и Java являются императивными и объектно-ориентированными: они работают, выполняя инструкции, и моделируют все как объекты.

В последних выпусках Java, я полагаю, добавлял некоторые функциональные возможности, к большому ура. Python также имеет свою долю функциональных возможностей, но … подход несколько иной. Он предлагает несколько встроенных токенов, таких как map и reduce , но на самом деле он не предназначен для объединения множества маленьких функций.

Вместо этого Python смешивается с… чем-то другим. Я не знаю ни одного распространенного названия подходов, которые использует Python. Я полагаю, что это разделило идею «связывания функций» на две части: работа с последовательностями и повышение эффективности самих функций.

Последовательности

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

В самом начале я случайно опустил эту строку:

  for word, count in words.most_common(10): 

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

Все, что может быть повторено, также может быть распаковано. Распаковка поддерживает произвольное вложение, поэтому a, (b, c) = ... делает то, на что похоже. Для последовательностей неизвестной длины элемент *leftovers может появиться где угодно и впитать столько элементов, сколько необходимо. Возможно, вам действительно нравится LISP?

 values = [5, 7, 9] head, *tail = values print(head) # 5 print(tail) # (7, 9) 

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

 values = [3, 4, 5] values2 = [val * 2 for val in values if val != 4] print(values2) # [6, 10] 

Стандартная библиотека также содержит ряд интересных итераций, комбинаторов и рецептов в модуле itertools .

Наконец, в Python есть генераторы для создания ленивых последовательностей с императивным кодом. Функция, содержащая ключевое слово yield , при вызове не выполняется немедленно; вместо этого он возвращает объект генератора. Когда генератор повторяется, функция работает до тех пор, пока не встретит yield , после чего остановится ; полученное значение становится следующим повторяющимся значением.

 def odd_numbers(): n = 1 while True: yield n n += 2 for x in odd_numbers(): print(x) if x > 4: break # 1 # 3 # 5 

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

 # This is the pathlib.Path API from the standard library def iter_child_filenames(dirpath): for child in dirpath.iterdir(): if child.is_file(): yield child.name 

Чтобы выразить совершенно произвольный ленивый итератор в Java, вам нужно написать Iterator который вручную отслеживает его состояние. Для всех, кроме самых простых случаев, это может стать довольно волосатым. Python также имеет итерационный интерфейс, так что вы все еще можете использовать этот подход, но генераторы настолько просты в использовании, что большинство пользовательских итераций написаны с ними.

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

функции

На первый взгляд, функции Python довольно знакомы. Вы можете назвать их с аргументами. Стиль передачи точно такой же, как в Java — Python не имеет ни ссылок, ни неявного копирования. В Python даже есть «строки документации», похожие на комментарии Javadoc, но встроенные в синтаксис и читаемые во время выполнения.

 def foo(a, b, c): """Print out the arguments. Not a very useful function, really.""" print("I got", a, b, c) foo(1, 2, 3) # I got 1 2 3 

У Java есть переменные функции с синтаксисом args... ; Python имеет почти то же самое, используя *args . (Синтаксис *leftovers для распаковки был основан на синтаксисе функции.) Но у Python есть еще несколько хитростей. Любой аргумент может иметь значение по умолчанию, что делает его необязательным. Любой аргумент также может быть задан по имени — я делал это ранее с помощью Point(x=3, y=4) . Синтаксис *args может использоваться при вызове любой функции, чтобы передать последовательность, как если бы это были отдельные аргументы, и есть эквивалентный **kwargs который принимает или передает именованные аргументы как dict. Аргумент может быть сделан «только для ключевого слова», поэтому он должен передаваться по имени, что очень хорошо для необязательных bools.

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

Сейчас готовится одна из самых мощных функций Python. Во многом так же, как динамическая типизация позволяет прозрачно заменять объект оболочкой или прокси, *args и **kwargs позволяют прозрачно **kwargs любую функцию .

 def log_calls(old_function): def new_function(*args, **kwargs): print("i'm being called!", args, kwargs) return old_function(*args, **kwargs) return new_function @log_calls def foo(a, b, c=3): print(f"a = {a}, b = {b}, c = {c}") foo(1, b=2) # i'm being called! (1,) {'b': 2} # a = 1, b = 2, c = 3 

Это немного плотно, извини. Не беспокойтесь о том, как именно это работает; Суть в том, что foo заменяется new_function , которая передает все свои аргументы в foo . Ни foo ни вызывающему абоненту не нужно знать, что все по-другому.

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

Ява или Птихон?

Объекты и динамическое время выполнения

Динамическая среда выполнения — это среда выполнения — материал за кулисами, который питает основные части языка — с которым можно играть во время выполнения. Такие языки, как C или C ++, не имеют динамических сред выполнения; структура исходного кода «запекается» в скомпилированный вывод, и в дальнейшем нет разумного способа изменить его поведение. Java, с другой стороны, имеет динамическое время выполнения! Это даже идет с целым пакетом, посвященным размышлению.

У Python тоже есть отражение. Есть ряд простых функций, встроенных прямо для проверки или изменения атрибутов объектов на лету, что невероятно полезно для отладки и случайных махинаций.

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

В качестве крайнего примера взгляните на pytest , который делает очень умные вещи с помощью выражения assert Python. Обычно, запись assert x == 1 просто выдает AssertionError когда false, оставляя вас без контекста для того, что пошло не так и где. Вот почему встроенный в unittest модуль unittestunittest — например, JUnit и многие другие средства тестирования — предоставляет набор специфических служебных функций, таких как assertEquals . К сожалению, они делают тесты несколько более объемными и сложными для чтения. Но с pytest, assert x == 1 в порядке. Если это не удастся, pytest скажет вам, что такое x … или где два списка расходятся, или какие элементы отличаются между двумя наборами, или что вы сказали. Все это происходит автоматически, в зависимости от выполняемого сравнения и типов операндов.

Как работает pytest? Вы действительно не хотите знать. И вам не нужно знать, чтобы писать тесты с pytest — и делать это с удовольствием.

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

Объекты

Мой любимый простой пример — доступ к атрибутам. В Java класс Point может выбрать getX() и setX() вместо простого атрибута x . Причина заключается в том, что если вам когда-либо понадобится изменить способ чтения или записи x , вы можете сделать это, не нарушая интерфейс. В Python вам не нужно беспокоиться об этом заранее, потому что вы можете перехватить доступ к атрибутам при необходимости.

 class Point: def __init__(self, x, y): self._x = x self._y = y @property def x(self): return self._x # ... same for y ... point = Point(3, 4) print(point.x) # 3 

Забавный синтаксис @property — это декоратор, который выглядит как аннотация Java, но может более непосредственно изменять функцию или класс.

Чтение point.x теперь вызывает функцию и возвращает ее возвращаемое значение. Это полностью прозрачно для вызова кода — и неотличимо от любого другого атрибута чтения — но объект может вмешиваться и обрабатывать его так, как ему нравится. В отличие от Java, доступ к атрибутам является частью API класса и свободно настраивается. (Обратите внимание, что этот пример также делает x доступным только для чтения, потому что я не указал, как записывать в него! Синтаксис для property записи немного забавен, и то, как он работает, здесь не имеет значения. Но вы могли бы тривиально, скажем, обеспечить, чтобы в point.x могли быть назначены только нечетные числа.)

Подобные функции существуют в других статических языках, таких как C #, поэтому, возможно, это не так впечатляет. Действительно интересная часть Python — это то, что property вообще не является особенным. Это обычный встроенный тип, который можно написать менее чем на скрине чистого Python. Это работает, потому что класс Python может настроить свой собственный доступ к атрибутам, как в целом, так и для каждого атрибута. Оболочки, прокси и состав легко реализуются: вы можете перенаправлять все вызовы методов вместе с базовым объектом, не зная, какие методы у него есть.

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

Вы, наверное, уже заметили, что мой код не имеет public или private модификаторов, и, действительно, у Python нет таких концепций. По соглашению, единственное подчеркивание используется для обозначения «private-ish» — или, точнее, «не предназначено как часть стабильного публичного API». Но это не имеет смыслового значения, и сам Python не мешает никому проверять или изменять такой атрибут (или вызывать его, если это метод). Нет final или static или const , либо.

Это та же самая философия в работе: ядро ​​Python обычно не мешает вам что-либо делать. И когда вам это нужно, это очень полезно. Я исправлял ошибки в сторонних библиотеках, вызывая или переопределяя или даже полностью переопределяя частные методы во время запуска. Это избавляет меня от необходимости создавать целую локальную ветвь проекта, и как только ошибка исправлена, я просто удаляю свой патч-код.

Аналогичным образом вы можете легко написать тесты для кода, который зависит от внешнего состояния — скажем, текущего времени. Если рефакторинг нецелесообразен, вы можете заменить time.time() фиктивной функцией на время теста. Библиотечные функции — это просто атрибуты модулей (например, пакетов Java), а модули Python являются объектами, как и все остальное, поэтому их можно проверять и изменять одинаковыми способами.

Классы

Java-класс поддерживается объектом Class , но оба они не вполне взаимозаменяемы. Для класса Foo объектом класса является Foo.class . Я не думаю, что Foo может использоваться само по себе, потому что он называет тип , а Java делает некоторые тонкие различия между типами и значениями.

В Python класс — это объект , экземпляр type (который сам является объектом и, следовательно, экземпляром самого себя, о котором интересно думать). Таким образом, классы могут обрабатываться как любое другое значение: передаются как аргументы, сохраняются в более крупных структурах, осмотр или манипуляция. Способность делать диктовки, ключи которых являются классами, особенно полезна время от времени. И поскольку классы создаются просто путем их вызова — у Python нет new ключевого слова — во многих случаях их можно заменить простыми функциями. Некоторые общие модели, такие как фабрики, настолько просты, что почти исчезают.

 # Wait, is Vehicle a class or a factory function? Who cares! # It could even be changed from one to the other without breaking this code. car = Vehicle(wheels=4, doors=4) 

Несколько раз я помещал функции и даже обычный код на верхний уровень вне любого класса. Это разрешено, но последствия немного тонкие. В Python даже операторы class и def являются обычным кодом, который выполняется во время выполнения . Файл Python выполняется сверху вниз, и class и def не являются особенными в этом отношении. Это просто специальный синтаксис для создания определенных видов объектов: классов и функций.

Вот действительно классная часть. Поскольку классы являются объектами, а их тип — type , вы можете подклассировать type и изменять его работу. Затем вы можете создавать классы, которые являются экземплярами вашего подкласса.

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

 class Animal(Enum): cat = 0 dog = 1 mouse = 2 snake = 3 print(Animal.cat) # <Animal.cat: 0> print(Animal.cat.value) # 0 print(Animal(2)) # <Animal.mouse: 2> print(Animal['dog']) # <Animal.dog: 1> 

Оператор class создает объект, что означает, что он вызывает конструктор где-то, и этот конструктор может быть переопределен, чтобы изменить способ построения класса. Здесь Enum создает фиксированный набор экземпляров, а не атрибутов класса. Все это реализовано с простым кодом Python и обычным синтаксисом Python.

Целые библиотеки были построены на этих идеях. Вы ненавидите скуку ввода self.foo = foo для каждого атрибута в конструкторах? И затем определение равенства, хеширования, клонирования и представления, читаемого разработчиками, вручную? Java понадобится поддержка компилятора, которая может появиться в Project Amber . Python достаточно гибок, чтобы сообщество решило эту проблему с помощью библиотеки attrs .

 import attr @attr.s class Point: x = attr.ib() y = attr.ib() p = Point(3, 4) q = Point(x=3, y=4) p == q # True, which it wouldn't have been before! print(p) # Point(x=3, y=4) 

Или возьмите SQLAlchemy , многофункциональную библиотеку баз данных для Python. Он включает в себя ORM, вдохновленный Java Hibernate , но вместо того, чтобы объявлять схему таблицы в файле конфигурации или с помощью несколько многословных аннотаций, вы можете написать ее прямо и компактно как класс:

 class Order(Table): id = Column(Integer, primary_key=True) order_number = Column(Integer, index=True) status = Column(Enum('pending', 'complete'), default='pending') ... 

Это та же основная идея, что и в Enum , но SQLAlchemy также использует те же хуки, что и property поэтому вы можете изменять значения столбцов естественным образом.

 order.order_number = 5 session.commit() 

Наконец, сами классы могут быть созданы во время выполнения. Это немного больше ниши, но Thriftpy создает целый модуль, полный классов на основе файла определения Thrift . В Java вам понадобится генерация кода, которая добавляет совершенно новый шаг компиляции, который может выйти из синхронизации.

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

Завершение

Python имеет много тех же базовых концепций, что и Java, но принимает их в совершенно ином направлении и добавляет некоторые совершенно новые идеи. Где Java фокусируется на стабильности и надежности, Python фокусируется на выразительности и гибкости. Это совершенно другой способ думать об императивном программировании.

Я сомневаюсь, что Python заменит Java для вас в тех местах, где Java превосходит. Например, Python, вероятно, не выиграет соревнования по скорости (но см. PyPy , JITted Python). Java имеет встроенную поддержку потоков, тогда как сообщество Python в значительной степени избегает их. Очень большое сложное программное обеспечение с большим количеством пыльных углов может предпочесть проверку работоспособности , которую обеспечивает статическая типизация (но посмотрите mypy , средство проверки статического типа для Python).

Но, возможно, Python будет светить в тех местах, где нет Java. Множество программного обеспечения не должно быть особенно быстрым или параллельным, и тогда другие проблемы всплывают на поверхность. Мне очень быстро и легко начать проект на Python. Без отдельного шага компиляции цикл записи / выполнения намного быстрее. Код короче, что обычно означает, что его легче понять. Испытывать разные архитектурные подходы кажется дешевле. А иногда забавно просто попробовать глупые идеи, например реализовать goto с библиотекой .

Я надеюсь, что вы попробуете Python. Мне очень весело с этим, и я думаю, что вы тоже. Просто постарайтесь не рассматривать это как Java со всеми типами, скрытыми от вас.

В худшем случае всегда есть Pyjnius , который позволяет вам сделать это.

 from jnius import autoclass System = autoclass('java.lang.System') System.out.println('Hello, world!')