Что нужно запомнить программисту, переходящему на Python

  • Tutorial

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


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


  • Билл Любанович «Простой Python. Современный стиль программирования»
  • Дэн Бейдер «Чистый Python. Тонкости программирования для профи»
  • Бретт Слаткин «Секреты Python: 59 рекомендаций по написанию эффективного кода»

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


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


Я заметил, что на собеседованиях по питону достаточно часто задают вопросы про вещи не имеющие отношения к реальной разработке, типа того, что может быть ключом словаря (или про то что означает x = yield y), ну чуваки, в реальной жизни ключом может быть только число или строка, в тех уникальных случаях когда это не так, можно почитать документацию и разобраться, зачем спрашивать такое? Чтобы найти чего собеседуемый не знает? Так в итоге все запомнят ответ именно на этот вопрос и это перестанет работать.


Актуальными я считаю питон версий выше 3.5 (про второй питон давно пора забыть) т.к. именно такая версия в стабильном дебиане, а значит во всех остальных местах более свежие версии )


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


Типизация


Питон динамически типизированный язык т.е. он проверяет соответствие типов в процессе выполнения, например:


cat type.py

a=5
b='5'
print(a+b)

выполняем:


python3 type.py
... TypeError: unsupported operand type(s) for +: 'int' and 'str'

Однако, если ваш проект дозрел до необходимости статической типизации, то питон предоставляет и такую возможность путём использования статического анализатора mypy:


mypy type.py
type.py:3: error: Unsupported operand types for + ("int" and "str")

Правда так ловятся не все ошибки:


cat type2.py

def greeting(name):
    return 'Hello ' + name

greeting(5)

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


cat type3.py
def greeting(name: str) -> str:
    return 'Hello ' + name

greeting(5)

а теперь:


mypy type3.py
type3.py:4: error: Argument 1 to "greeting" has incompatible type "int"; expected "str"

Переменные и данные


Переменные в питоне не хранят данные, а лишь ссылаются на них, а данные бывают изменяемые (мутабельные) и неизменяемые (иммутабельные).
Это приводит к различному поведению в зависимости от типа данных в практически идентичных ситуациях, например такой код:


x = 1
y = x
x = 2
print(y)

приводит к тому, что переменные x и y ссылаются на различные данные, а такой:


x = [1, 2, 3]
y = x
x[0] = 7
print(y)

нет, x и y остаются ссылками на один и тот же список (хотя как заметили в комментариях пример не очень удачный, но лучше я пока не придумал), что кстати в питоне можно проверить оператором is (я уверен что создатель джавы навсегда лишился хорошего сна от стыда когда узнал про этот оператор в питоне).


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


>>> mystr = 'sss'
>>> newstr = mystr  # делаем ссылку на те же данные
>>> mystr[0] = 'a'
...
  TypeError: 'str' object does not support item assignment
>>> mystr = 'ssa'  # меняем исходную переменную
>>> newstr  # данные не изменились и доступны по второй ссылке
  'sss'

Кстати, о строках, из-за их иммутабельности конкатенация очень большого списка строк сложением или append'ом в цикле может быть не очень эффективной (зависит от рализации в конкретном компиляторе/версии), обычно для таких случаев рекомендуют использовать метод join, который ведёт себя немного неожиданно:


>>> str_list = ['ss', 'dd', 'gg']
>>> 'XXX'.join(str_list)
'ssXXXddXXXgg'
>>> str = 'hello'
>>> 'XXX'.join(str)
'hXXXeXXXlXXXlXXXo'

Во-первых, строка у которой вызывается метод становиться разделителем, а не началом новой строки как можно было бы подумать, а во-вторых, передавать нужно список (итерируемый объект), а не отдельную строку ибо таковая тоже является итерируемым объектом и будет сджойнена посимвольно.


Так как переменные это ссылки, то вполне нормальным является желание сделать копию объекта, чтобы не ломать исходный объект, однако тут есть подводный камень — функция copy копирует только один уровень, что явно не то, что ожидается от функции с таким именем, поэтому используете deepcopy.


Аналогичная проблема с копированием может возникать при умножении коллекции на скаляр, как разбиралось тут.


Область видимости


Тема области видимости скорее заслуживает отдельной статьи, но есть хороший ответ на SO.
Если кратко, то область видимости лексическая и есть шесть областей видимости — переменные в теле функции, в замыкании, в модуле, в теле класса, встроенные в питон фунции и переменные внутри списковых и иных включений.
Есть тонкость — переменная по умолчанию доступна для чтения в лексически вложенных пространствах имён, но модификация требует использования специальных ключевых слов nonlocal и global для модификации переменных на один уровень выше или глобальной видимости соответственно.


Например, такой код:


x = 7
print(id(x))

def func():
    print(id(x))
    return x

print(func())

Работает с одной глобальной переменной, а такой:


x = 7
print(id(x))

def func():
    x = 1
    print(id(x))
    return x

print(func())
print(x)

уже порождает локальную.
С моей точки зрения это не очень хорошо, по идее любое использование нелокальных переменных в функции это часть публичного интерфейса функции, её сигнатуры, а значит должно объявляться явно и видимо в начале функции. Также ключевые слова не очень информативны — global звучит как определение глобальной функции, а на самом деле означает use global.


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


if __name__ == '__main__':
    main()

такое условие сработает если файл будет вызван как скрипт, а не импортирован как модуль.


Аргументы функций


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


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


def add_element(mylist):
    mylist.append(3)

mylist = [1,2]
add_element(mylist)
print(mylist)

выполняем:


python3 arg_modify.py
[1, 2, 3]

однако нельзя затереть исходную ссылку в функции:


def try_del(mylist):
    mylist = []
    return mylist

mylist = [1,2]
try_del(mylist)
print(mylist)

исходная ссылка жива и работает:


python3 arg_kill.py
[1, 2]

Также для аргументов можно задавать значения по умолчанию, но с этим есть одна неочевидная вещь которую нужно запомнить — значения по умолчанию вычисляются один раз при определении функции, это не создаёт никаких проблем, если вы в качестве значения по умолчанию передаёте неизменяемые данные, а если передаются изменяемые данные или динамическое значение, то результат будем чуток неожиданным:


изменяемые данные:


cat arg_list.py

def func(arg = []):
    arg.append('x')
    return arg

print(func())
print(func())
print(func())

результат:


python3 arg_list.py
['x']
['x', 'x']
['x', 'x', 'x']

динамическое значение:


cat arg_now.py

from datetime import datetime

def func(arg = datetime.now()):
    return arg

print(func())
print(func())
print(func())

получаем:


python3 arg_now.py
2018-09-28 10:28:40.771879
2018-09-28 10:28:40.771879
2018-09-28 10:28:40.771879

ООП


ООП в питоне сделано весьма интересно (одни property чего стоят) и это большая тема, однако сапиенс знакомый с ООП вполне может нагуглить всё (или найти на хабре), что ему захочется, поэтому нет смысла повторяться, хотя стоит оговорить, что питон следует немного другой философии — считается, что программист умнее машины и не является вредителем (UPD: подробнее), поэтому в питоне по умолчанию нет привычных по другим языкам модификаторов доступа: private методы реализуются добавлением двойного подчёркивания (что в рантайме изменяет имя метода не позволяя случайно его использовать), а protected одним подчёркиванием (что не делает ничего, это просто соглашение об именовании).
Те кто скучает по привычному функционалу могут поискать попытки привнести в питон такие возможности, мне нагуглилась пара вариантов (lang, python-access), но я их не тестировал и не изучал.


Единственный минус стандартных классов — шаблонный код во всяких дандер методах, лично мне нравится библиотека attrs, она значительно более питоническая.
Стоит упомянуть, что так в питоне всё объекты, включая функции и классы, то классы можно создавать динамически (без использования eval) функцией type.
Также стоит почитать про метаклассы (на хабре) и дескрипторы (хабр).
Особенность, которую стоит запомнить — атрибуты класса и объекта это не одно и тоже, в случае неизменяемых атрибутов это не вызывает проблем так как атрибуты "затеняются" (shadowing) — создаются автоматически атрибуты объекта с таким же именем, а вот в случае изменяемых атрибутов можно получить не совсем то, что ожидалось:


cat class_attr.py
class MyClass:
    storage = [7,]
    def __init__(self, number):
        self.number = number

obj = MyClass(1)
obj2 = MyClass(2)

obj.number = 5
obj.storage.append(8)

print(obj2.storage, obj2.number)

получаем:


python3 class_attr.py
[7, 8] 2

как можно увидеть — изменяли obj, а storage изменился и в obj2 т.к. этот атрибут (в отличии от number) принадлежит не экземпляру, а классу.


Константы


Как и в случае с модификаторами доступа питон не пытается ограничить разработчика, поэтому задачать скалярную переменную защищённую от модификации стандартным способом нельзя, просто есть соглашение, что переменные с именем в верхнем регистре нужно считать константами.
С другой стороны в питоне есть неизменяемые структуры данных такие как tuple, поэтому если вы хотите сделать неизменяемой какую-то глобальную структуру вроде конфига и не хотите дополнительных зависимостей, то namedtuple, вполне хороший выбор, хотя он потребует немного больше усилий для описания типов, поэтому мне нравится альтернативная реализация неизменяемой структуры с dot-notation — Box (см. параметр frozen_box).
Ну а если вам хочется скалярных констант, то можно реализовать проверку доступа к ним на стадии "компиляции" т.е. проверки через mypy, пример и подробности.


.sort() vs sorted()


В питоне есть два способо сортировать список. Первый это метод .sort() который изменяет исходный список и ничего не возвращает (None) т.е. не получится сделать так:


my_list = my_list.sort()

Второй, это функция sorted() которая порождает новый список и умеет работать со всеми итерируемыми объектами. Кому хочется больше инфы стоит начать с SO.


Стандартная библиотека


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


Например, идущий в комплекте модуль для модульного тестирования unittest не имеет никакого отношения к питону и попахивает джавой, поэтому, как говорит автор питона: "Eveybody is using py.test ...". Хотя вполне интересный, пусть и не всегда подходящий модуль doctest идёт в стандартной поставке.


Идущий в поставке модуль urllib не имеет такого прекрасного интерфейса как стронний модуль requests.


Та же история с модулем для разбора параметров коммандной строки — идущий в комплекте argparse это демонстрация ООП головного мозга, а модуль docopt кажется просто шикарным решением — предельная самодокументируемость! Хотя, по слухам, несмотря на docopt и для click остаётся ниша.


С отладчиком также — как я понял идущий в комплекте pdb мало кто использует, альтернатив много, но похоже основная масса разработчиков используется ipdb, который, с моей точки зрения удобнее всего использовать через модуль-обёртку debug.
Она позволяет вместо import ipdb;ipdb.set_trace() просто написать import debug, также она добавляет модуль see для удобной инспекции объектов.


На замену стандартному модулю сериализации pickle делают dill, тут кстати стоит запомнить, что эти модули не подходят для обмена данными в внешними системами т.к. восстанавливать произвольные объекты полученные из неконтролируемого источника небезопасно, для таких случаев есть json (для REST) и gRPC (для RPC).


На замену стандартному модулю обработкти регулярных выражений re делают модуль regex со всякими дополнительными плюшками, вроде классов символов аля \p{Cyrillic}.
Кстати, что-то не попалось для питона весёлого отладчика для регексов похожего на перловый.


Вот другой пример — человек сделал свой модуль in-place, чтобы пофиксить кривизну и неполноту API стандартного модуля fileinput в части in place редактирования файлов.


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


Параллелизм и конкурентность


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


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


А если в ваших задачах много ожидания IO, то питон предоставляет массу вариантов на выбор, от тредов и gevent, до asyncio.
Все эти варианты выглядят вполне пригодными для использования (хотя треды значительно больше ресурсов требуют), но есть ощущение, что asyncio потихоньку выдавливает остальных, в том числе благодаря всяким плюшками типа uvloop.


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


Иные странности


В питоне a = a + b не всегда эквивалентно a += b:


a = [1]
a = a + (2,3)
TypeError: can only concatenate list (not "tuple") to list
a += (2,3)
a
[1, 2, 3]

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


Странности, которые не странности


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


Отдельная большая тема это округления (хотя это проблема общая практически для всех языков программирования), помимо того, что округление используется какое угодно кроме того, что все изучали в школьном курсе математики, так на это ещё накладываются проблемы преставления чисел с плавющей точкой, отсылаю к подробной статье.
Грубо говоря вместо привычного, по школьному курсу математики, округления по алгоритма half up используется алгоритм half to even, которые уменьшает вероятность искажений при статистическом анализе и поэтому рекомендуется стандартом IEEE 754.


Также я не мог понять почему -22//10=-3, а потом, другой добрый человек, указал, что это неизбежно следует из самого математического определения, по которому, остаток не может быть отрицательным, что и приводит к такому необычному поведению для отрицательных чисел.
ACHTUNG! Теперь это опять странность и я ничего не понимаю, см. сей тред.


Отладка регулярных выражений


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


sudo apt install cpanminus
cpanm Regexp::Debugger
perl -I ~/perl5/lib/perl5/ -E "use Regexp::Debugger; 'ababc' =~ /(a|b) b+ c/x"

Думаю даже человек незнакомый с перлом поймёт где тут надо вписать строку, а где регулярное выражение, x это флаг аналогичный питонному re.VERBOSE.
Нажимаем s и шагаем по регулярному выражению, подробное описание доступных команд в документации.


Документация


В питоне есть функция help, которая позволяет получить справку по любой загруженной функции (берётся из её docstring'а), параметром передаётся имя функции:


$ python3                                                                                                                                        
>>> help(help)

но это не всегда удобный способ и часто удобнее использовать утилиту pydoc:


pydoc3 urllib.parse.urlparse

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

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

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 33

    0
    nonlocal и global для модификации переменных на один уровень выше

    не на один уровень выше, а ближайший выше, исключая глобальную
      0
      ну это и имелось ввиду, я не знаю какая формулировка понятнее
      0
      такой код <> приводит к тому, что переменные x и y ссылаются на различные данные, а такой <> нет
      Может потому что в первом случае вы присваиваете значение переменной(==ссылке) повторно, а во втором э… нет? Ничего себе идентичные ситуации. Причём тут вообще мутабельность?
        0
        Да, надо придумать пример получше
        0
        >>> можно догадаться, что передача осуществляется по ссылке
        Почти, но не всегда.
        Мутабельные элементы, типа списка, да передаются по ссылке.
        А иммутабельные, например строка, передаются по значению.
        Такая вот гибридная система.
        А насчет многопоточности.
        В multiprocessing есть потоки, а есть процессы.
        У каждого свое применение.
        Для задач io лучше применять потоки, а для cpu вычислений лучше работают процессы.
          +2
          А иммутабельные, например строка, передаются по значению.

          Нет, строки тоже передаются по ссылке, это элементарно проверяется с помощью is или id() в CPython

            0
            Вот тут вы не правы. Да id функция показывает один id, но это не из-за того, что переменная передалась по ссылке.
            В python все есть объекты, даже простые числа, например 1 — это тоже объект. А переменные не хранят никаких значений, python связывает их с объектами, точнее хранят ссылку на объект.
            Поэтому когда вы передаете строку в функцию, передается сам объект.
            Например:
            mylist = [1, 2]
            def func(x):
            x.append(3)
            print(mylist) # [1, 2, 3]
            s = 'stroka_1'
            def func2(x):
            x = 'stroka_2'
            print(s) # stroka_1
            Может несколько сумбурно объяснил, но почитайте про объекты в python. На эту тему очень много публикаций.
              0
              это не из-за того, что переменная передалась по ссылке.

              Я не просто так упомянул CPython — в нём реализация id() для большинства объектов такова, что она возвращает именно что адрес в памяти, то есть по сути значение ссылки. Которая одинаковая, да.


              Поэтому когда вы передаете строку в функцию, передается сам объект.

              Когда я передаю строку в функцию, она принимает ссылку на объект. На тот же самый объект. Переменная — это по сути ссылка с именем. Разные переменные могут ссылаться на один и тот же объект.


              Когда вы пишете x = чтототам, вы меняете ссылку у переменной, но совершенно никак не трогаете сами объекты. Так же вы не трогаете все остальные переменные, которые ссылаются на этот объект — вы просто меняете значение этой одной переменной, и больше ничего.


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

                0
                Да, вызовы функций случайно упустил, каюсь.
                Вы конечно правы, и я с вами согласен что передаются ссылки на объекты. Вот только статья называется Что нужно запомнить программисту, переходящему на Python. Ключевое слово переходящему с других языков. Такая система с объектами, что переменные не хранят никаких значений кроме ссылок на объекты вот и вводит в путаность переходящих.
                Например, в C++ переменная является именованной областью памяти, хранящей значение. Поэтому передача переменной по ссылки в функцию подразумевает передачу ссылки именно на переменную, значение которой потом в функции можно изменить. В python же передается значение переменной, то есть непосредственно ссылка на объект. Из-за чего и возникает вопрос как переменные передаются в функцию, по значению или по ссылке.
                Наверно хорошо сказали тут:
                jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither
                  +1
                  Для того, чтобы не расщеплять сущности это назвали call by sharing и поскольку оно же используется в большинстве распространённых языков (Java, JavaScript и т.д.), то непонятно кого это может смущать…
                    0
                    Ну я с вами согласен, вот только языков программирования много, а не только Java и JavaScript. я, например, их не знаю вовсе, а в Python перешел с C++, где система несколько иная: call-by-value.
                    Я в первом своем комментарии неправильно выразился, надо было написать, что если сравнивать с языками по типу C/C++.
            +1
            А иммутабельные, например строка, передаются по значению.

            Иммутабельность тут ни при чем. Все передается по ссылке.
            >>> s = 'abc' * 1024 * 1024
            >>> id(s)
            2381293081856
            >>> def foo(s):
            ...     return id(s)
            ...
            >>> foo(s)
            2381293081856
            +1
            в реальной жизни ключом может быть только число или строка
            Кортеж координат как ключ тоже вполне востребованное решение, когда мы описываем объекты на игровой доске, например.
            А вообще вопрос про ключи словаря он на понимание хешируемости, там есть не совсем очевидные вещи, когда (1, 2) — допустимый ключ, а (1, [2]) — нет, хотя оба являются кортежем!
            a = (1, [2, 3], 4)
            print(type(a))   # <type 'tuple'>
            b = {a: 1}       # TypeError: unhashable type: 'list'
              +1
              однако нельзя затереть исходную ссылку в функции:

              def try_del(mylist):
                  mylist = []
                  return mylist
              
              mylist = [1,2]
              try_del(mylist)
              print(mylist)
              В Вашем примере создалась новая локальная переменная внутри функции, из функции она сама не вернется если нет присвоения при вызове функции (последний мой пример).

              Можно так, даже без возврата обнулить коллекцию по ссылке:
              def try_del(mylist):
                  mylist.clear()
              
              mylist = [1,2]
              try_del(mylist)
              print(mylist)  # []

              А если Вы хотите возвращаемое из функции значение, нужно обязательно присвоить переменной, «в никуда» оно не вернется.
              def try_del(mylist):
                  mylist = [4, 5]
                  return mylist
              
              mylist = [1,2]
              mylist = try_del(mylist)
              print(mylist)  # [4, 5]
                +1
                Именно " нельзя затереть исходную ссылку в функции" как раз и выполняется. Т.е. — передачей значения какой-то переменной — изнутри функции нельзя изменить адрес объекта, на который ссылается эта переменная, если конечно ее не указать в ф-и как global (выделал с помощью тире, чтобы путаницы не было).
                –2
                «Оставь надежду всяк сюда входящий» больше ничего не надо :)
                  +1
                  А вот мне кажется неправильным описывать поведение функции, в точности соответствующее шикарной документации, и дополнять эпитетами «неожиданно» и «внезапно».
                    0
                    Не знаю кто как, а я живу в мире где время очень лимитированный ресурс и индивиды не безупречны и поэтому хороший инструмент должен вести себя не только в соответствии с документацией, но и в соответствии с логикой, понятной специалисту в этой сфере, что минимизирует число ошибок.
                    +1
                    в какой-то момент перл исчерпал себя

                    Это как? Надоело писать доллары перед именами скалярных переменных?
                    А если серьёзно, чего не хватило в перле, из-за чего вы перешли на питон?

                    • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Это долго рассказывать (некоторые моменты я когда-то описывал тут), если кратко, то я понял, что нет смысла тратить время на инструменты, которые ты не считаешь лучшими, которые не кажутся тебе спроектированными максимально круто и максимально крутыми специалистами, питон мне кажется крутым (почти как хаскел), а перл нет, понятно это не значит, что в перле всё плохо, а в питоне всё идеально.
                        Ну и перл просто умер, это и скучно (поддержка легаси кода) и сужает пространство возможностей во всем смыслах — поди найди работу на перле с каким-нибудь машинным обучением, или захочешь релоцироваться в другую страну, а там ни одной вакансии на перле.
                        0
                        Спасибо — недавно перешел с C/C++ embedded в python и веб хайлоад)
                        Открыл некоторые вещи незнание которых было бомбой замедленного действия — например про ссылки, формально знал об этом, но так как в основном поведение было как у переменных — бдительность усыпило
                          0

                          Про деление с остатком/целой частью не совсем верно, по факту в питоне знак определяется по делителю, а не по делимому как в си например, то есть если вы будите делить например 7%-3 как в математике у вас будет 7=-3*2+1
                          А в питоне будет -2.
                          https://en.wikipedia.org/wiki/Modulo_operation
                          Здесь довольно хорошая картинка поясняющая суть.

                            0
                            Ничего не понял, знак определяется не делителем или делимым, а математическим определением по которому остаток только положительный, а из этого вытекает всё остальное.
                              0
                              Вы бы всёж-таки сходили по ссылке, а? Выше определение действительно удобнее для математиков — но его сложнее реализовать «в железе», потому современные процессоры считают, что остаток должен иметь тот же знак, что и делитель и быть меньше его по абсолютной величине — а отсюда уже вытекает всё остальное. А питон имеет аж два варианта — но «математического», о котором вы говорите — не имеет.
                                0
                                Вообще-то я ходил по этим ссылкам ещё в тот раз, по ссылке написано:
                                Given two positive numbers, a (the dividend) and n (the divisor), a modulo n (abbreviated as a mod n) is the remainder of the Euclidean division of a by n.

                                The range of numbers for an integer modulo of n is 0 to n − 1

                                In mathematics, the result of the modulo operation is the remainder of the Euclidean division.

                                Если этого недостаточно, то вот ещё:
                                Given two integers a and b, with b ≠ 0, there exist unique integers q and r such that

                                a = bq + r

                                and

                                0 ≤ r < |b|,

                                Т.е. по математическому определению, остаток должен быть положительным, и понятное дело должно выполняться равенство и питон как раз и реализует это поведение — остаток всегда положительный и равенство выполняется, что и должен делать высокоуровневый язык.
                                  +2
                                  Ну то есть это случай «смотрю в книгу — вижу фигу»? Вообще-то по ссылке написано не только какими способоами можно определять остаток, но и о том, что у Питона есть даже не один способ, а аж два — и оба не совпадают с математическим определением:
                                  $ python3
                                  Python 3.5.3 (default, Jan 19 2017, 14:11:04) 
                                  [GCC 6.3.0 20170118] on linux
                                  Type "help", "copyright", "credits" or "license" for more information.
                                  >>> import math
                                  >>> print(-14 % -3)
                                  -2
                                  >>> print(math.fmod(-14,-3))
                                  -2.0
                                  

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

                                  Мне казалось, что времена Аристотеля, который писал трактаты на тему количества ног у мухи, вместо того, чтобы взять — и их посчитать… таки прошли… но похоже, что нет.
                                    0
                                    Как говориться — посыпаю голову пеплом и извиняюсь, я теперь ничего не понимаю, набросал тест, запускать pytest-3 -v modulo.py, если я ничего не натупил вечером, то до математики питону как до Луны, а я так верил.
                            +1
                            В качестве пометки тем у кого пост в закладках, попалось неплохое видео от James Powell о метаклассах, декораторах, генераторах и менеджерах контекста, в принципе ничего нового я не узнал, но автор отлично объясняет.
                              0
                              Собрание всяких приколов на близкую тему, хотя и далёких от практики.
                              0
                              Попалась попытка сделать cheatcheet.
                                0
                                А вот интересный момент касательно строк, хотя на практике особой проблемы вроде нет.

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

                                Самое читаемое