Хабрахабр

Знакомство с Python для камрадов, переросших «язык A vs. язык B» и другие предрассудки

К сожалению, качество этого "введения" кхм… не будем о грустном. Для всех хабравчан, у которых возникло ощущение дежавю: Написать этот пост меня побудили статья "Введение в Python" и комментарии к ней. Удивительно, что не вспомнили Ruby! Но ещё грустнее было наблюдать склоки в комментариях, из разряда "C++ быстрее Python", "Rust ещё быстрее C++", "Python не нужен" и т.д.

Как сказал Бьярн Страуструп,

«Есть всего два типа языков программирования: те, на которые люди всё время ругаются, и те, которые никто не использует».

Добро пожаловать под кат всем, кто хотел бы познакомиться с Python, не опускаясь при этом до грязных ругательств!

Два молодых человека сидели на большом валуне и что-то рьяно обсуждали, активно жестикулируя руками. Утро в горах Восточного Кавказа ознаменовалось воплями. Видно этот куст рос там неспроста, — он сразу утихомирил драчунов и внёс перемирие в их неугасающий спор. Через минуту они начали толкать друг друга, а потом сцепились и свалились с валуна в (как оказалось) куст крапивы. Delphi! Как вы, наверное догадались, одним из спорщиков был я, другим — мой лучший друг (привет, Quaker_t!), ну а предметом нашей светской беседы — Visual Basic vs.

Иногда мы превращаем любимые языки программирования в культ и готовы отстаивать его до последнего! Узнаёте себя? B" из предмета споров перерастает в "Мне комфортнее работать с А, но при необходимости я научусь работать с B, C, D, E и вообще, с чем угодно". Но годы идут и наступает момент, когда "A vs. Вот только когда мы сталкиваемся с новыми языками программирования, старые привычки и культура могут нас долго не отпускать.

Как у любой технологии, у него есть свои сильные и слабые стороны. Я хотел бы познакомить вас с Питоном и помочь перенести ваш опыт в новое русло. К любому инструменту прилагается инструкция и любым инструментом надо научиться пользоваться правильно. Python, как и C++, Rust, Ruby, JS, и все остальные — это инструмент.

Давайте знакомиться! "Автор, не пудри мозги, ты собирался нас с Питоном знакомить?".

Python — зрелый язык программирования с богатой экосистемой и традициями. Python — динамический, высокоуровневый язык программирования общего назначения. Python — заряженный язык, в его стандартной библиотеке есть решения на многие случаи жизни. Хоть язык и увидел свет в 1991-м году, его современный облик начал формироваться в начале 2000-х. Python — популярный язык программирования: Dropbox, Reddit, Instagram, Disqus, YouTube, Netflix, чёрт побери, даже Eve Online и многие другие активно используют Python.

С вашего позволения, изложу собственную версию. В чём причина такой популярности?

Динамическая типизация. Python — простой язык программирования. Функции высшего порядка. Сборщик мусора. для получения срезов). Простой синтаксис для работы со словарями, множествами, кортежами и списками (в т.ч. Но эта простота — как верхушка айсберга. Питон отлично подходит для новичков: даёт возможность начать с процедурного программирования, потихоньку перейти к ООП и почуствовать вкус программирования функционального. Ныряешь ещё дальше — и попадаешь в свод чётких правил по оформлению кода — Style Guide for Python Code. Стоит нырнуть в глубину, как натыкаешься на философию Питона — Zen Of Python. В этот удивительный этап изучения языка, начинаешь понимать, почему хорошие программы на Питоне пишутся именно так, а не иначе. Погружаясь, программист постепенно вникает в понятие "Python way" или "Pythonic". Питон не преуспел в скорости выполнения. Почему язык эволюционировал именно в этом направлении, а не в другом. "Пишите код для людей, а не для машины" — это основа из основ Питона. Но он преуспел в важнейшем аспекте нашей работы — читабельности.

А писать красивый код — чем не приятное занятие? Хороший код на Питоне выглядит красиво.

Язык зиждется на этих постулатах и наше общение будет намного приятнее, если и вы будете с ними знакомы. Совет 0: Перед тем как читать дальше, пожалуйста, загляните в уголок Дзена Питона.

Какой умник додумался до отступов?

Первым шоком для тех, кто никогда не видел код на Питоне, является обозначение тела инструкций отступами:

def main(): ins = input('Please say something') for w in ins.split(' '): if w == 'hello': print('world!')

"Тело инструкции отступами? Вспоминаю вечера в общаге Питерского Политеха, когда мой сосед, VlK, с горящими глазами рассказывал, что ещё нового он откопал в Питоне. Действительно, для человека прошедшего от Visual Basic (if ... Серьёзно?" — была моя реакция. "Ты же форматируешь код отступами?", спросил VlK. end if) до C# (фигурные скобки), сквозь C, C++ и Java, подобный подход казался, мягко говоря, странным. Точнее, за меня это делала спираченная Visual Studio. Конечно же я форматировал его. Я никогда не задумывался о форматировании и отступах — они появлялись в коде сами по себе и казались чем-то обыденным и привычным. Она справлялась с этим чертовски хорошо. "Тогда зачем тебе фигурные скобки, если тело инструкций в любом случае будет сдвинуто вправо?". Но крыть было нечем — код был всегда отформатирован отступами.

Оглядываясь назад, я могут точно сказать, что́ именно помогало быстро усваивать новый материал. В тот вечер я засел за Python. Под влиянием того же VlK, незадолго до вышеописанных событий, я перешёл с Windows на Ubuntu и Emacs в качестве редактора (на дворе 2007й год, до PyCharm, Atom, VS Code и прочих — ещё много лет). Это был редактор кода. Совсем чуточку 🙂 Традиционно, клавиша <tab> в Emacs не добавляет символов табуляции, а служит для выравнивания строки по правилам данного режима. "Ну вот, сейчас будет пиарить Emacs..." — скажете вы. Нажал <tab> — и строка кода сдвигается в следующее подходящее положение:

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

Совет 1: При знакомстве с Python используйте редактор, который возьмёт на себя заботу об отступах.

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

Да ну вашу динамическую типизацию

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

Стоит уточнить, что типизация в Питоне строгая и сложить число со строкой не получится:

1 + '1'
>>> TypeError: unsupported operand type(s) for +: 'int' and 'str'

Питон также проверяет сигнатуру функции при вызове и выбросит исключение, если сигнатура вызова не верна:

def sum(x, y): return x + y sum(10, 20, 30)
>>> TypeError: sum() takes 2 positional arguments but 3 were given

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

def sum(x, y): return x + y sum(10, '10')
>>> TypeError: can only concatenate str (not "int") to str

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

# main.py:
def sum(x: int, y: int) -> int: return x + y sum(10, '10') $ mypy main.py
tmp.py:5: error: Argument 2 to "sum" has incompatible type "str"; expected "int"

Едиственное условие — аннотации должны быть валидными значениями с точки зрения языка. Питон не придаёт никакого значения аннотациям, хотя и сохраняет их в аттрибуте __annotations__. 0 (что было более десяти лет назад!), именно усилиями сообщества, аннотации стали использовать для типизированной маркировки переменных и аргументов. С момента их появления в версии 3.

Ещё один пример, посложнее.

# Для тех кто очень в теме, напоминаю: это пример :) from typing import TypeVar, Iterable Num = TypeVar('Num', int, float) def sum(items: Iterable[Num]) -> Num: accum = 0 for item in items: accum += item return accum sum([1, 2, 3)
>>> 6

Особенно если этот код писался без аннотаций и вам приходится тратить много времени на выяснение типов переменных. Совет 2: На практике больше всего динамическая типизация вызывает проблемы при чтении и отладке кода. Вам не обязательно указывать и документировать типы всего и вся, но время, потраченное на детальное описание публичных интерфейсов и наиболее критических участков кода, воздастся сторицей!

Кря! Утиная типизация

Порой знатоки Питона напускают на себя таинственный вид и говорят об "Утиной типизации".
Утиная типизация (Duck typing) — это применение "утиного теста" в программировании:

Если объект крякает как утка, летает как утка и ходит как утка, то скорее всего это утка.

Рассмотрим пример:

class RpgCharacter: def __init__(self, weapon) self.weapon = weapon def battle(self): self.weapon.attack()

Класс RpgCharacter получает объект weapon в конструкторе и позже, в методе battle() вызывает weapon.attack(). Тут — классическое внедрение зависимости (dependency injection). Это может быть меч, BFG 9000, или кит с цветочным горшком, готовые приземлиться неприятелю на голову в любой момент. Но RpgCharacter не зависит от конкретной имплементации weapon. Важно, чтобы у объекта был метод attack(), всё остальное Питон не интересует.

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

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

Что было бы, используй мы условную Java?

interface IWeapon { void attack();
} public class Sword implements IWeapon
} public class RpgCharacter { IWeapon weapon; public RpgCharacter(IWeapon weapon) { this.weapon = weapon; } public void battle() { weapon.attack(); }
}

Цена — невозможность использовать объект, имеющий метод attack(), но при этом не реализующий интерфейс IWeapon явным образом. А была бы классическая статическая типизация, с проверкой соответствия типам на стадии компиляции.

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

Процедурный подход и __специальные_методы__()

Питон — объектно-ориентированный язык и в корне иерархии наследования стоит класс object:

isinstance('abc', object)
>>> True isinstance(10, object)
>>> True

ToString(), в Питоне будет вызов функции str(obj). Но там где в Java и C# используется obj. Создатель языка, Гвидо Ван Россум (Guido van Rossum), объяснил это следующим образом: Или например вместо myList.length, в Питоне будет len(my_list).

Это сразу говорит мне о том, что результатом будет целое число, а аргументом — какой-то контейнер. Когда я читаю код в котором говорится len(x), то знаю, что запрашивается длина чего-то. [Источник]. И наоборот, читая x.len(), мне необходимо знать, что x — это какой-то контейнер, имплементирующий определённый интерфейс или наследующий от класса, в котором имеется метод len().

Тем не менее внутри себя функции len(), str() и некоторые другие будут вызывать определённые методы объекта:

class User: def __init__(self, name, last_name): self.name = name self.last_name = last_name def __str__(self): return f"Honourable {self.name} {self.last_name}" u = User('Alex', 'Black')
label = str(u)
print(label)
>>> Honourable Alex Black

in ..., оператором контекста with, оператором индекса [] и т.д.
Например, протокол итератора состоит из двух методов: __iter__() и __next__(): Специальные методы также используются операторами языка, как математическими и булевыми, так и операторами цикла for ...

# Никаких Iterable, IEnumerable, std::iterator и т.д.
class InfinitePositiveIntegers: def __init__(self): self.counter = 0 def __iter__(self): """Возвращает объект по которому будет проводиться итерация. Вызывается встроенной фунцкией iter(). """ return self def __next__(self): """Возвращает элементы итерации. Вызывается встроенной фунцкией next(). """ self.counter += 1 return self.counter for i in InfinitePositiveIntegers(): print(i)
>>> 1
>>> 2
>>> ...
# чтобы остановить, нажмите Ctrl + C

Но почему они выглядят так вырвиглазно? Хорошо, допустим специальные методы. Т.е. Гвидо объяснил это тем, что имей они обычные имена без подчёркиваний, программисты, сами того не хотя, рано или поздно переопределяли бы их. Как показало время — защита эффективная 🙂 __метод__() это своебразная защита от дурака.

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

Где инкапсуляция? Где мой private?! Где моя сказочка?!!

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

import os class MyFile: # Поле считается приватным _os_handle = None def __init__(self, path: str): self._open(path) # Метод считается приватным def _open(self, path): # os.open() - *низкоуровневая* функция для открытия файлов. # На практике используется встроенная функция open(). # Нам же os.open() отлично подойдёт для примера. self._os_handle = os.open(path, os.O_RDWR | os.O_CREAT) # А этот метод считается публичным def close(self): if self._os_handle is not None: os.close(self._os_handle) f = MyFile('/tmp/file.txt')
print(f._os_handle) # с доступом к "приватному" полю нет никаких проблем!
f.close()

Почему?

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

В конце концов мы все здесь взрослые люди.

— Karl Fast [Источник].

А как же избежать коллизии имён при наследовании?

Сделано это для избежания коллизий имён при наследовании. В Питоне есть специальный механизм искажения (mangling) имени аттрибутов, начинающихся с двойного подчёркивания и не заканчивающихся на двойное подчёркивание (__my_attr)! Но для внутреннего доступа ничего не меняется: Для вызова вне тела методов класса, Питон добавляет префикс _ИмяКласса__аттрибут.

class C: def __init__(self): self.__x = 10 def get_x(self): return self.__x c = C()
c.__x
>>> 'C' object has no attribute '__x' print(c.get_x())
>>> 10 print(c._C__x)
>>> 10

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

class BaseFile: def __init__(self, path): self.path = path class LocalMixin: def read_from_local(self): with open(self.path) as f: return f.read() class CachedMixin: class CacheMissError(Exception): pass def __init__(self): # Tepeрь, даже если в соседнем классе в цепочке наследования # будет аттрибут __cache, или метод __from_cache(), # коллизии, а точнее переопределения не произойдёт! self.__cache = {} def __from_cache(self): return self.__cache[self.path] def read_from_cache(self): try: return self.__from_cache() except KeyError as e: raise self.CacheMissError() from e def store_to_cache(self, data): self.__cache[self.path] = data class File(CachedMixin, LocalMixin, BaseFile): def __init__(self, path): CachedMixin.__init__(self) BaseFile.__init__(self, path) def read(self): try: return self.read_from_cache() except CachedMixin.CacheMissError: data = self.read_from_local() self.store_to_cache(data) return data

Если вам интересно взглянуть на имплементацию этого механизма в CPython, прошу в Python/compile.c

Например, в изначально написанном классе Coordinates, Наконец, благодаря наличию свойств (properties) в языке, теряется смысл писать геттеры и сеттеры в стиле Java: getX(), setX().

class Coordinates: def __init__(self, x, y): self.x = x self.y = y c = Coordinates(10, 10)
print(c.x, c.y)
>>> (10, 10)

Правильным подходом будет заменить его на property, тем самым сохраняя контракт с внешним миром. понадобилось управлять доступом к аттрибуту x.

class Coordinates: _x = 0 def __init__(self, x, y): self.x = x self.y = y @property def x(self): return self._x @x.setter def x(self, val): if val > 10: self._x = val else: raise ValueError('x should be greater than 10') c = Coordinates(20, 10)
c.x = 5
>>> ValueError: x should be greater than 10

Не обижайтесь на авторов библиотек, если "всё перестало работать" по той причите, что вы активно пользовались приватными полями их классов. Совет 5: Как и многое в Питоне, понятие о приватных полях и методах класса опирается на устоявшуюся конвенцию. В конце концов, мы все здесь взрослые люди 🙂.

Немного об исключениях

Кроме привычного перехвата и обработки а-ля C++ / Java, вам придётся столкнуться с использованием исключений в контексте В культуре Питона своеобразный подход к исключениям.

"Проще попросить прощения, чем спрашивать разрешение" (Easier to ask for permission, than forgiveness, EAFP).

Вместо этого оберни логику в try..except. Перефразируя — не пиши лишнего if, если в большинстве случаев исполнение пойдёт по данной ветке.

На входе функции — словарь (dictionary) типа ключ-значение: Пример: представим обработчик POST-запросов, создающей пользователя в условной базе данных.

def create_user_handler(data: Dict[str, str]): try: database.user.persist( username=data['username'], password=data['password'] ) except KeyError: print('There was a missing field in data passed for user creation')

Мы ожидаем, что скорее всего они там будут. Мы не стали загрязнять код проверками "содержится ли username или password в data". Мы не просим "разрешения" пользоваться этими полями, но "просим прощения" когда очередной кулхацкер запостит форму с отсутствующими данными.

Только не доводите это до абсурда!

if здесь будет куда уместнее: Например, вам хочется проверить, присутствуют ли фамилия пользователя в данных и при отсутствии установить её в пустое значение.

def create_user_handler(data): if 'last_name' not in data: data['last_name'] = '' try: database.user.persist( username=data['username'], password=data['password'], last_name=data['last_name'] ) except KeyError: print('There was a missing field in data passed for user creation')

У современного Питона есть замечательная консткрукция raise from, позволяющая сохранить контекст цепочки исключений. Errors should never pass silently. — не замалчивайте исключения! Например:

class MyProductError(Exception): def __init__(self): super().__init__('There has been a terrible product error') def calculate(x): try: return 10 / x except ZeroDivisionError as e: raise MyProductError() from e

С raise from X, причина (т.е. Без raise from e цепочка исключений обрывается на MyProductError, и мы не сможем узнать, что именно было причиной этой ошибки. X) выбрасываемого исключения сохраняется в аттрибуте __cause__:

try: calculate(0)
except MyProductError as e: print(e.__cause__) >>> division by zero

Но есть маленький нюанс в случае с итерацией: StopIteration

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

class PositiveIntegers: def __init__(self, limit): self.counter = 0 self.limit = limit def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == self.limit: # никаких hasNext() или moveNext(), # только исключения, только хардкор raise StopIteration() return self.counter for i in PositiveIntegers(5): print(i)
> 1
> 2
> 3
> 4

Не пренебрегайте ими! Совет 6: Мы платим за обработку исключения, лишь в исключительных ситуациях.

There should be one-- and preferably only one --obvious way to do it.

— используйте if и словари. switch или pattern matching? — для этого есть while и for. do-циклы? Думаю вы и сами догадались. goto? Самое удивительное, что нет никаких технических ограничений на их реализацию, просто "у нас так не принято". Это же относится и к некоторым техникам и шаблонам проектирования, которые кажутся сами собой разумеющимися в других языках.

Вместо него используется возможность передавать и явным образом запрашивать именные аргументы функции. Например, в Питоне не часто встретишь паттерн "Builder". Вместо

human = HumanBuilder.withName("Alex").withLastName("Black").ofAge(20).withHobbies(['tennis', 'programming']).build()

будет

human = Human( name="Alex" last_namne="Black" age=20 hobbies=['tennis', 'programming']
)

Помню, как коллега, пришедший из мира Kotlin, показывал мне код следующего толка (взято из официальной документации по Котлину): В стандартной библиотеке не используются цепочки методов для работы с коллекциями.

val shortGreetings = people .filter { it.name.length < 10 } .map { "Hello, ${it.name}!" }

Переписав этот код один в один получится: В Питоне map(), filter() и многие другие — функции, а не методы коллекций.

short_greetings = map(lambda h: f"Hello, {h.name}", filter(lambda h: len(h.name) < 10, people))

Поэтому для длинных связок вроде .takewhile().filter().map().reduce() лучше использовать т.н. По-моему выглядит ужасно. Кстати, этот же пример на Котлине, приводится в виде соответствующего list comprehension. включения (comprehensions), или старые добрые циклы. А на Питоне это выглядит так:

short_greetings = [ f"Hello {h.name}" for h in people if len(h.name) < 10
]

Для тех же, кто скучает по цепочкам

Есть библиотеки, такие как Pipe или py_linq!

Например в web-фреймворке Django, цепочки используются для построение объекта-запроса к БД: Цепочки методов используются там, где они эффективнee стандартных средств.

query = User.objects \ .filter(last_visited__gte='2019-05-01') \ .order_by('username') \ .values('username', 'last_visited') \ [:5]

Совет 7: Перед тем, как сделать что-то очень знакомое из прошлого опыта, но не знакомое в Питоне, спросите себя, какое бы решение принял опытный питонист?

Питон медленный

Да.

Да, если речь идёт о скорости исполнения по сравнению с статически типизированными и компилируемыми языками.

Но вы, похоже, желаете развёрнутого ответа?

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

Jake VanderPlas в своём блоге разбирает, что происходит у CPython под капотом при сложении двух переменных, содержащих целочисленные значения:

a = 1
b = 2
c = a + b

Даже если не углубляться в дебри CPython, можно сказать, что для хранения переменных a, b и c, интерпретатору придётся создать три объекта в куче (heap), в которых будут храниться тип и (указатели на) значения; повторно выяснять тип и значения при операции сложения, чтобы вызвать что-то вроде binary_add<int, int>(a->val, b->val); записать результат в c.
Это чудовищно неэффективно по сравнению с аналогичной программой на C.

Global Interpreter Lock (GIL). Другая беда CPython — это т.н. GIL упрощает разработку кода, работающего в многопоточной среде: вам не надо думать о синхронизации доступа к переменным или о взаимных блокировках (deadlocks). Этот механизм, по сути — булевое значение, огороженное мьютексом, используется для синхронизации выполнения байткода. За это приходится платить тем, что лишь один поток получает доступ и выполняет байткод в данный момент времени:

Если вам интересно, какие попытки предпринимаются для искорениеня GIL

Рекомендую прочесть статью Anthony Shaw "Has the Python GIL been slain?".

Каковы выходы из ситуации?

  1. Питон отлично взаимодействует с нативными библиотеками. В простейшем варианте (CFFI) нужно описать источник и сигнатуру функции в Питоне и вызывать её из динамической библиотеки. Для полноценной же работы с интерпретатором и окружением Питон предоставляет API для написания расширений (extensions) на C/C++. А порывшись в Гугле, можно найти реализацию расширений на Rust, Go и даже Kotlin Native!
  2. Использовать альтернативную реализацию Питона, например:
    • PyPy, со встроенным JIT-компилятором. Прирост скорости будет меньше, чем при использовании нативного расширения, но может в конкретном случае большего и не будет нужно?
    • Cython — транспайлер и компилятор надмножества языка Python в код на C.
    • IronPython — имплементация, работающая поверх .NET framework.

Если же вы работаете над приложением, в котором IO (сеть, БД, файловая система) является узким местом, то к тому моменту, когда скорость Питона перестанет вас устраивать, вы точно будете знать, как решить эту проблему 🙂 Совет 8: Если вам априори важна скорость выполнения, эффективнее будет использовать Питон как связку между нативными компонентами и не пытаться впихнуть невпихуемое.

Основные инструменты

Если у вас под рукой Linux или MacOS, то в 95% случаев Питон будет установлен из коробки. Как начинаются первые шаги в Питоне? 7. Если вы живёте на острие прогресса, то скорее всего это версия 3.х, а не отживающая свой век версия 2. Вот несколько вариантов: использовать Docker, Windows Subsystem for Linux, Cygwin, наконец, официальный инсталлятор Питона для Винды. Для товарищей на Windows всё чуточку сложнее.

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

Превосходно! Вы уже написали "Hello world" и он работает? Через пару дней вы займётесь machine learning-ом и вам понадобится какая-нибудь библиотека из каталога Python Package Index (PyPI).

виртуальные окружения (virtual environments). Чтобы избежать конфликтов версий при установке пакетов (packages), в Питоне используются т.н. Там же будут лежать шелл-скрипты для управления этой средой. Они позволяют частично изолировать среду путём создания директории, в которой будут находиться установленные пакеты. При активированной виртуальной среде pip будет устанавливать пакеты именно в неё. Установщик пакетов pip также идёт в комплекте. А объединяет всё это такие утилиты, как pipenv или poetry — аналоги npm, bundler, cargo и т.п.

Всё остальное — это удобные, красивые, высокоуровневые обёртки. Совет 0xA: Ваши главные помощники для управления зависимостями — это pip и virtualenv. Ведь всё, что нужно Питону и вам — это правильный sys.path — список директорий, по которым пойдёт поиск модулей при их импорте.

Что же дальше?

Тогда не поленитесь взглянуть и на туториал по вышеописанным инструментам. Вы уже прочли официальный туториал? И как в небезызвестной копипасте:

Завтра ищешь в интернете книжку Dive into python...

Ведь редкий день проходит на Хабре без появления статьи о применении Питона там где, казалось, ему совсем не место 🙂 Уверен, что у вас скопилась гора идей и чешутся руки взяться за новый проект на Питоне.

Дерзайте, камрады!

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»