Содержание

Python | Введение

Язык программирования Python

Последнее обновление: 24.01.2018

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

Впервые язык Python был анонсирован в 1991 году голландским разработчиком Гвидо Ван Россумом. С тех пор данный язык проделал большой путь развития. В 2000 году была издана версия 2.0, а в 2008 году — версия 3.0. Несмотря на вроде такие большие промежутки между версиями постоянно выходят подверсии. Так, текущей актуальной версией на момент написания данного материала является 3.7. Более подробную информацию о всех релизах, версиях и изменения языка, а также собственно интерпретаторы и необходимые утилиты для работы и прочую полезную информацию можно найти на официальном сайте https://www.python.org/.

Основные особенности языка программирования Python:

  • Скриптовый язык. Код программ определяется в виде скриптов.

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

  • Интерпретация программ. Для работы со скриптами необходим интерпретатор, который запускает и выполняет скрипт.

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

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

  • Портативность и платформонезависимость. Не имеет значения, какая у нас операционная система — Windows, Mac OS, Linux, нам достаточно написать скрипт, который будет запускаться на всех этих ОС при наличии интерпретатора

  • Автоматическое управление памяти

  • Динамическая типизация

Python — очень простой язык программирования, он имеет лаконичный и в то же время довольно простой и понятный синтаксис. Соответственно его легко изучать, и собственно это одна из причин, по которой он является одним из самых популярных языков программирования именно для обучения. В частности, в 2014 году он был признан самым популярным языком программирования для обучения в США.

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

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

Установка Python

Для создания программ на Python нам потребуется интерпретатор. Для его установки перейдем на сайт https://www.python.org/ и на главной станице в секции Downloads найдем ссылку на загрузку последней версии языка (на данный момент это 3.7.2):

Перейдем по ссылке к странице с описанием последней версии языка. Ближе к низу на ней можно найти список дистрибутивов для разных операционных систем. Выберем нужный нам пакет и загрузим его. Например, в моем случае это ОС Windows 64-х разрядная, поэтому я выбираю ссылку на пакет Windows x86-64 executable installer. После загрузки дистрибутива установим его.

Соответственно для MacOS можно выбрать пункт macOS 64-bit installer.

На ОС Windows при запуске инсталлятора запускает окно мастера установки:

Здесь мы можем задать путь, по которому будет устанавливаться интерпретатор. Оставим его по умолчанию, то есть C:\Users\[имя_пользователя]\AppData\Local\Programs\Python\Python36\.

Кроме того, в самом низу отметим флажок «Add Python 3.6 to PATH», чтобы добавить путь к интерпретатору в переменные среды.

После установки в меню Пуск на ОС Windows мы сможем найти иконки для доступа к разным утилитам питона:

Здесь утилита Python 3.7 (64-bit) представляет интерпретатор, в котором мы можем запустить скрипт. В файловой системе сам файл интерпретатора можно найти по пути, по которому производилась установка. На Windows по умолчанию это путь C:\Users\[имя_пользователя]\AppData\Local\Programs\Python\Python37, а сам интерпретатор представляет файл python.exe. На ОС Linux установка производится по пути /usr/local/bin/python3.7.

Введение в функциональное программирование на Python / Хабр

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

Забейте. Функциональный код отличается одним свойством: отсутствием побочных эффектов. Он не полагается на данные вне текущей функции, и не меняет данные, находящиеся вне функции. Все остальные «свойства» можно вывести из этого.

Нефункциональная функция:

a = 0
def increment1():
    global a
    a += 1

Функциональная функция:

def increment2(a):
    return a + 1

Вместо проходов по списку используйте map и reduce

Map

Принимает функцию и набор данных. Создаёт новую коллекцию, выполняет функцию на каждой позиции данных и добавляет возвращаемое значение в новую коллекцию. Возвращает новую коллекцию.

Простой map, принимающий список имён и возвращающий список длин:

name_lengths = map(len, ['Маша', 'Петя', 'Вася'])

print name_lengths
# => [4, 4, 3]

Этот map возводит в квадрат каждый элемент:

squares = map(lambda x: x * x, [0, 1, 2, 3, 4])

print squares
# => [0, 1, 4, 9, 16]

Он не принимает именованную функцию, а берёт анонимную, определённую через lambda. Параметры lambda определены слева от двоеточия. Тело функции – справа. Результат возвращается неявным образом.

Нефункциональный код в следующем примере принимает список имён и заменяет их случайными прозвищами.

import random

names = ['Маша', 'Петя', 'Вася']
code_names = ['Шпунтик', 'Винтик', 'Фунтик']

for i in range(len(names)):
    names[i] = random.choice(code_names)

print names
# => ['Шпунтик', 'Винтик', 'Шпунтик']

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

Перепишем это через map:

import random

names = ['Маша', 'Петя', 'Вася']

secret_names = map(lambda x: random.choice(['Шпунтик', 'Винтик', 'Фунтик']), names)

Упражнение 1. Попробуйте переписать следующий код через map. Он принимает список реальных имён и заменяет их прозвищами, используя более надёжный метод.

names = ['Маша', 'Петя', 'Вася']

for i in range(len(names)):
    names[i] = hash(names[i])

print names
# => [6306819796133686941, 8135353348168144921, -1228887169324443034]
Моё решение:
names = ['Маша', 'Петя', 'Вася']

secret_names = map(hash, names)

Reduce

Reduce принимает функцию и набор пунктов. Возвращает значение, получаемое комбинированием всех пунктов.

Пример простого reduce. Возвращает сумму всех пунктов в наборе:

sum = reduce(lambda a, x: a + x, [0, 1, 2, 3, 4])

print sum
# => 10

x – текущий пункт, а – аккумулятор. Это значение, которое возвращает выполнение lambda на предыдущем пункте. reduce() перебирает все значения, и запускает для каждого lambda на текущих значениях а и х, и возвращает результат в а для следующей итерации.

А чему равно а в первой итерации? Оно равно первому элементу коллекции, и reduce() начинает работать со второго элемента. То есть, первый х будет равен второму предмету набора.

Следующий пример считает, как часто слово «капитан» встречается в списке строк:

sentences = ['капитан джек воробей',
             'капитан дальнего плавания',
             'ваша лодка готова, капитан']

cap_count = 0
for sentence in sentences:
    cap_count += sentence.count('капитан')

print cap_count
# => 3

Тот же код с использованием reduce:

sentences = ['капитан джек воробей',
             'капитан дальнего плавания',
             'ваша лодка готова, капитан']

cap_count = reduce(lambda a, x: a + x.count('капитан'),
                   sentences,
                   0)

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

Почему map и reduce лучше?

Во-первых, они обычно укладываются в одну строку.

Во-вторых, важные части итерации,– коллекция, операция и возвращаемое значение,– всегда находятся в одном месте map и reduce.

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

В-четвёртых, map и reduce – элементарные операции. Вместо построчного чтения циклов читателю проще воспринимать map и reduce, встроенные в сложные алгоритмы.

В-пятых, у них есть много друзей, позволяющих полезное, слегка изменённое поведение этих функций. Например, filter, all, any и find.

Упражнение 2: перепишите следующий код, используя map, reduce и filter. Filter принимает функцию и коллекцию. Возвращает коллекцию тех вещей, для которых функция возвращает True.

people = [{'имя': 'Маша', 'рост': 160},
    {' рост ': 'Саша', ' рост ': 80},
    {'name': 'Паша'}]

height_total = 0
height_count = 0
for person in people:
    if 'рост' in person:
        height_total += person[' рост ']
        height_count += 1

if height_count > 0:
    average_height = height_total / height_count

    print average_height
    # => 120
Моё решение:
people = [{'имя': 'Маша', 'рост': 160},
    {' рост ': 'Саша', ' рост ': 80},
    {'name': 'Паша'}]

heights = map(lambda x: x['рост'],
              filter(lambda x: 'рост' in x, people))

if len(heights) > 0:
    from operator import add
    average_height = reduce(add, heights) / len(heights)

Пишите декларативно, а не императивно

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

Примеры вывода:

 -
 - -
 - -

 - -
 - -
 - - -

 - - -
 - -
 - - -

 - - - -
 - - -
 - - - -

 - - - -
 - - - -
 - - - - -

Текст программы:

from random import random

time = 5
car_positions = [1, 1, 1]

while time:
    # decrease time
    time -= 1

    print ''
    for i in range(len(car_positions)):
        # move car
        if random() > 0.3:
            car_positions[i] += 1

        # draw car
        print '-' * car_positions[i]

Код императивен. Функциональная версия была бы декларативной – она бы описывала, что нужно сделать, а не то, как это надо сделать.

Используем функции

Декларативности можно достичь, вставляя код в функции:
from random import random

def move_cars():
    for i, _ in enumerate(car_positions):
        if random() > 0.3:
            car_positions[i] += 1

def draw_car(car_position):
    print '-' * car_position

def run_step_of_race():
    global time
    time -= 1
    move_cars()

def draw():
    print ''
    for car_position in car_positions:
        draw_car(car_position)

time = 5
car_positions = [1, 1, 1]

while time:
    run_step_of_race()
    draw()

Для понимания программы читатель просматривает основной цикл. «Если осталось время, пройдём один шаг гонки и выведем результат. Снова проверим время». Если читателю надо будет разобраться, как работает шаг гонки, он сможет прочесть его код отдельно.

Комментарии не нужны, код объясняет сам себя.

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

Вот функциональная версия этой программы:

from random import random

def move_cars(car_positions):
    return map(lambda x: x + 1 if random() > 0.3 else x,
               car_positions)

def output_car(car_position):
    return '-' * car_position

def run_step_of_race(state):
    return {'time': state['time'] - 1,
            'car_positions': move_cars(state['car_positions'])}

def draw(state):
    print ''
    print '\n'.join(map(output_car, state['car_positions']))

def race(state):
    draw(state)
    if state['time']:
        race(run_step_of_race(state))

race({'time': 5,
      'car_positions': [1, 1, 1]})

Теперь код разбит на функциональные функции. Тому есть три признака. Первый – нет расшаренных переменных. time и car_positions передаются прямиком в race(). Второе – функции принимают параметры. Третье – переменные не меняются внутри функций, все значения возвращаются. Каждый раз, когда run_step_of_race() проделывает следующий шаг, он передаётся опять в следующий.

Вот вам две функции zero() и one():

def zero(s):
    if s[0] == "0":
        return s[1:]

def one(s):
    if s[0] == "1":
        return s[1:]

zero() принимает строку s. Если первый символ – 0, то возвращает остаток строки. Если нет – тогда None. one() делает то же самое, если первый символ – 1.

Представим функцию rule_sequence(). Она принимает строку и список из функций-правил, состоящий из функций zero и one. Она вызывает первое правило, передавая ему строку. Если не возвращено None, то берёт возвращённое значение и вызывает следующее правило. И так далее. Если возвращается None, rule_sequence() останавливается и возвращает None. Иначе – значение последнего правила.

Примеры входных и выходных данных:

print rule_sequence('0101', [zero, one, zero])
# => 1

print rule_sequence('0101', [zero, zero])
# => None

Императивная версия rule_sequence():

def rule_sequence(s, rules):
    for rule in rules:
        s = rule(s)
        if s == None:
            break

    return s

Упражнение 3. Этот код использует цикл. Перепишите его в декларативном виде с использованием рекурсии.

Моё решение:
def rule_sequence(s, rules):
    if s == None or not rules:
        return s
    else:
        return rule_sequence(rules[0](s), rules[1:])

Используйте конвейеры (pipelines)

Теперь перепишем другой вид циклов при помощи приёма под названием конвейер.

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

bands = [{'name': 'sunset rubdown', 'country': 'UK', 'active': False},
         {'name': 'women', 'country': 'Germany', 'active': False},
         {'name': 'a silver mt. zion', 'country': 'Spain', 'active': True}]

def format_bands(bands):
    for band in bands:
        band['country'] = 'Canada'
        band['name'] = band['name'].replace('.', '')
        band['name'] = band['name'].title()

format_bands(bands)

print bands
# => [{'name': 'Sunset Rubdown', 'active': False, 'country': 'Canada'},
#     {'name': 'Women', 'active': False, 'country': 'Canada' },
#     {'name': 'A Silver Mt Zion', 'active': True, 'country': 'Canada'}]

Название функции «format» слишком общее. И вообще, код вызывает некоторое беспокойство. В одном цикле происходят три разные вещи. Значение ключа ‘country’ меняется на ‘Canada’. Убираются точки и первая буква имени меняется на заглавную. Сложно понять, что код должен делать, и сложно сказать, делает ли он это. Его тяжело использовать, тестировать и распараллеливать.

Сравните:

print pipeline_each(bands, [set_canada_as_country,
                            strip_punctuation_from_name,
                            capitalize_names])

Всё просто. Вспомогательные функции выглядят функциональными, потому что они связаны в цепочку. Выход предыдущей – вход следующей. Их просто проверить, использовать повторно, проверять и распараллеливать.

pipeline_each() перебирает группы по одной, и передаёт их функциям преобразования, вроде set_canada_as_country(). После применения функции ко всем группам, pipeline_each() делает из них список и передаёт следующей.

Посмотрим на функции преобразования.

def assoc(_d, key, value):
    from copy import deepcopy
    d = deepcopy(_d)
    d[key] = value
    return d

def set_canada_as_country(band):
    return assoc(band, 'country', "Canada")

def strip_punctuation_from_name(band):
    return assoc(band, 'name', band['name'].replace('.', ''))

def capitalize_names(band):
    return assoc(band, 'name', band['name'].title())

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

Всё вроде как нормально. Оригиналы данных защищены от изменений. Но в коде есть два потенциальных места для изменений данных. В strip_punctuation_from_name() создаётся имя без точек через вызов calling replace() с оригинальным именем. В capitalize_names() создаётся имя с первой прописной буквой на основе title() и оригинального имени. Если replace и time не функциональны, то и strip_punctuation_from_name() с capitalize_names() не функциональны.

К счастью, они функциональны. В Python строки неизменяемы. Эти функции работают с копиями строк. Уфф, слава богу.

Такой контраст между строками и словарями (их изменяемостью) в Python демонстрирует преимущества языков типа Clojure. Там программисту не надо думать, не изменит ли он данные. Не изменит.

Упражнение 4. Попробуйте сделать функцию pipeline_each. Задумайтесь над последовательностью операций. Группы – в массиве, передаются по одной для первой функции преобразования. Затем полученный массив передаётся по одной штучке для второй функции, и так далее.

Моё решение:
def pipeline_each(data, fns):
    return reduce(lambda a, x: map(x, a),
                  fns,
                  data)

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

set_canada_as_country = call(lambda x: 'Canada', 'country')
strip_punctuation_from_name = call(lambda x: x.replace('.', ''), 'name')
capitalize_names = call(str.title, 'name')

print pipeline_each(bands, [set_canada_as_country,
                            strip_punctuation_from_name,
                            capitalize_names])

Или, жертвуя читаемостью:

print pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
                            call(lambda x: x.replace('.', ''), 'name'),
                            call(str.title, 'name')])

Код для call():

def assoc(_d, key, value):
    from copy import deepcopy
    d = deepcopy(_d)
    d[key] = value
    return d

def call(fn, key):
    def apply_fn(record):
        return assoc(record, key, fn(record.get(key)))
    return apply_fn

Что тут у нас происходит.

Один. call – функция высшего порядка, т.к. принимает другую функцию как аргумент и возвращает функцию.

Два. apply_fn() похожа на функции преобразования. Получает запись (группу). Ищет значение record[key]. Вызывает fn. Присваивает результат в копию записи и возвращает её.

Три. call сам ничего не делает. Всю работу делает apply_fn(). В примере использования pipeline_each(), один экземпляр apply_fn() задаёт ‘country’ значение ‘Canada’. Другой – делает первую букву прописной.

Четыре. При выполнении экземпляра apply_fn() функции fn и key не будут доступны в области видимости. Это не аргументы apply_fn() и не локальные переменные. Но доступ к ним будет. При определении функции она сохраняет ссылки на переменные, которые она замыкает – те, что были определены снаружи функции, и используются внутри. При запуске функции переменные ищутся среди локальных, затем среди аргументов, а затем среди ссылок на замкнутые. Там и найдутся fn и key.

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

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

Остаётся ещё одна обработка данных групп. Убрать всё, кроме имени и страны. Функция extract_name_and_country():

def extract_name_and_country(band):
    plucked_band = {}
    plucked_band['name'] = band['name']
    plucked_band['country'] = band['country']
    return plucked_band

print pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
                            call(lambda x: x.replace('.', ''), 'name'),
                            call(str.title, 'name'),
                            extract_name_and_country])

# => [{'name': 'Sunset Rubdown', 'country': 'Canada'},
#     {'name': 'Women', 'country': 'Canada'},
#     {'name': 'A Silver Mt Zion', 'country': 'Canada'}]

extract_name_and_country() можно было бы написать в обобщённом виде под названием pluck(). Использовалась бы она так:

print pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
                            call(lambda x: x.replace('.', ''), 'name'),
                            call(str.title, 'name'),
                            pluck(['name', 'country'])])

Упражнение 5. pluck принимает список ключей, которые надо извлечь из записей. Попробуйте её написать. Это буде функция высшего порядка.

Моё решение:
def pluck(keys):
    def pluck_fn(record):
        return reduce(lambda a, x: assoc(a, x, record[x]),
                      keys,
                      {})
    return pluck_fn

И что теперь?

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

Вспомните про Машу, Петю и Васю. Превратите итерации по спискам в map и reduces.

Вспомните гонки. Разбейте код на функции, и сделайте их функциональными. Превратите цикл в рекурсию.

Вспомните про группы. Превратите последовательность операций в конвейер.

Возможности Python 3, достойные того, чтобы ими пользовались

Многие программисты начали переходить со второй версии Python на третью из-за того, что уже довольно скоро поддержка Python 2 будет прекращена. Автор статьи, перевод которой мы публикуем, отмечает, что основной объём Python 3-кода, который ему доводилось видеть, выглядит как код со скобками, написанный на Python 2. По его словам, он и сам грешит чем-то подобным. Здесь он приводит примеры некоторых замечательных возможностей, доступных лишь тем, кто пользуется Python 3. Он надеется, что эти возможности облегчат жизнь тем, кто о них узнает.

Все примеры, приведённые в этом материале, написаны с использованием Python 3.7. В описании каждой возможности имеются сведения о минимальной версии Python, необходимой для её применения.

Форматные строки (3.6+)


Без использования строк сложно написать что-то полезное на любом языке программирования. Но для эффективной работы со строками разработчику нужны удобные инструменты. Такие инструменты, которые позволяют оперировать сложными структурами, не теряя при этом душевного равновесия. Большинство Python-разработчиков пользуется методом format:
user = "Jane Doe"
action = "buy"
log_message = 'User {} has logged in and did an action {}.'.format(
  user,
  action
)
print(log_message)
# User Jane Doe has logged in and did an action buy.

Python 3, наряду с методом format, поддерживает форматные строки (f-strings, f-строки). Они представляют собой гибкий инструмент для выполнения различных манипуляций со строками. Вот как выглядит предыдущий пример, переписанный с использованием форматных строк:
user = "Jane Doe"
action = "buy"
log_message = f'User {user} has logged in and did an action {action}.'
print(log_message)
# User Jane Doe has logged in and did an action buy.

Модуль pathlib (3.4+)


Форматные строки — замечательная технология, но для работы с некоторыми строками, вроде путей к файлам, созданы специальные инструменты, сильно упрощающие манипуляции с ними. В Python 3 имеется модуль pathlib, который представляет собой удобную абстракцию для работы с путями к файлам. Если вы пока не уверены в полезности этого модуля для решения ваших задач — взгляните на этот материал.
from pathlib import Path
root = Path('post_sub_folder')
print(root)
# post_sub_folder
path = root / 'happy_user'
# Делаем путь абсолютным
print(path.resolve())
# /home/weenkus/Workspace/Projects/DataWhatNow-Codes/how_your_python3_should_look_like/post_sub_folder/happy_user

Аннотации типов (3.5+)


Что лучше — статическая или динамическая типизация? Пожалуй, почти каждый программист имеет собственный ответ на этот непростой вопрос. Я оставляю на усмотрение читателей то, как именно они типизируют свои программы. Но я считаю, что всем полезно будет хотя бы знать о том, что Python 3 поддерживает аннотации типов.
def sentence_has_animal(sentence: str) -> bool:
  return "animal" in sentence
sentence_has_animal("Donald had a farm without animals")
# True

Перечисления (3.4+)


Python 3 поддерживает, благодаря классу Enum, простой механизм работы с перечислениями. Перечисления удобно использовать для хранения списков констант. Константы, в противном случае, оказываются беспорядочно разбросанными в коде.
from enum import Enum, auto
class Monster(Enum):
    ZOMBIE = auto()
    WARRIOR = auto()
    BEAR = auto()
    
print(Monster.ZOMBIE)
# Monster.ZOMBIE

Из документации по Python 3 можно узнать о том, что перечисление — это набор символических имён (членов), привязанных к уникальным, неизменным значениям. Члены одного перечисления можно сравнивать на идентичность. Перечисления можно обходить.
for monster in Monster:
    print(monster)
# Monster.ZOMBIE
# Monster.WARRIOR
# Monster.BEAR

Встроенный LRU-кэш (3.2+)


В наши дни механизмы кэширования применяются практически во всех программных и аппаратных системах. Python 3 значительно упрощает кэширование благодаря декоратору lru_cache, который реализует алгоритм LRU-кэширования (Least Recently Used).

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

import time
def fib(number: int) -> int:
    if number == 0: return 0
    if number == 1: return 1
    
    return fib(number-1) + fib(number-2)
start = time.time()
fib(40)
print(f'Duration: {time.time() - start}s')
# Duration: 30.684099674224854s

Теперь используем lru_cache для оптимизации этой функции (такая техника оптимизации называется мемоизацией). В результате время выполнения функции, которое раньше измерялось секундами, теперь измеряется наносекундами.
from functools import lru_cache
@lru_cache(maxsize=512)
def fib_memoization(number: int) -> int:
    if number == 0: return 0
    if number == 1: return 1
    
    return fib_memoization(number-1) + fib_memoization(number-2)
start = time.time()
fib_memoization(40)
print(f'Duration: {time.time() - start}s')
# Duration: 6.866455078125e-05s

Распаковка итерируемых объектов (3.0+)


При распаковке итерируемых объектов можно использовать переменные, перед именами которых ставят звёздочку. В такие переменные попадает всё то, что не попало в другие переменные. Так, в следующем примере в переменные head и tail попадают первое и последнее значения из списка, сформированного командой range(5). В переменную body попадает всё то, что находится между первым и последним значением.
head, *body, tail = range(5)
print(head, body, tail)
# 0 [1, 2, 3] 4
py, filename, *cmds = "python3.7 script.py -n 5 -l 15".split()
print(py)
print(filename)
print(cmds)
# python3.7
# script.py
# ['-n', '5', '-l', '15']
first, _, third, *_ = range(10)
print(first, third)
# 0 2

Классы данных (3.7+)


В Python 3 появились классы данных (data classes). Они дают программисту достаточно большую свободу действий. Их можно использовать для уменьшения объёма шаблонного кода. Дело в том, что декоратор dataclass автоматически генерирует специальные методы, такие как __init__() и __repr__(). В официальном тексте соответствующего предложения они описаны как «изменяемые именованные кортежи со значениями по умолчанию». Вот пример создания класса без использования декоратора dataclass:
class Armor:
    
    def __init__(self, armor: float, description: str, level: int = 1):
        self.armor = armor
        self.level = level
        self.description = description
                 
    def power(self) -> float:
        return self.armor * self.level
    
armor = Armor(5.2, "Common armor.", 2)
armor.power()
# 10.4
print(armor)
# <__main__.Armor object at 0x7fc4800e2cf8>

Вот то же самое, но уже написанное с применением dataclass:
from dataclasses import dataclass
@dataclass
class Armor:
    armor: float
    description: str
    level: int = 1
    
    def power(self) -> float:
        return self.armor * self.level
    
armor = Armor(5.2, "Common armor.", 2)
armor.power()
# 10.4
print(armor)
# Armor(armor=5.2, description='Common armor.', level=2)

Поддержка папок пакетов без файла __init__.py (3.3+)


Один из способов структурирования Python-кода заключается в использовании пакетов (пакеты размещаются в папках, в которых есть файл __init__.py). Вот пример из официальной документации:
sound/                          Пакет верхнего уровня
      __init__.py               Инициализация пакета sound
      formats/                  Подпакет для преобразования форматов файлов
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Подпакет для звуковых эффектов
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Подпакет для фильтров
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

При использовании Python 2 в каждой из папок, упомянутых в примере, должен был быть файл __init__.py. Благодаря этому файлу папка воспринимается в виде Python-пакета. В Python 3, с появлением возможности Implicit Namespace Packages, наличие в папках подобных файлов больше не является обязательным.
sound/                          Пакет верхнего уровня
      __init__.py               Инициализация пакета sound
      formats/                  Подпакет для преобразования форматов файлов
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Подпакет для звуковых эффектов
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Подпакет для фильтров
              equalizer.py
              vocoder.py
              karaoke.py
              ...

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

Итоги


В этом материале рассмотрены далеко не все интересные возможности Python 3, но мы надеемся, что вы нашли здесь что-то полезное. Код примеров можно найти в этом репозитории.

Уважаемые читатели! Какие возможности Python 3 вы добавили бы в приведённый здесь список?

Почему существует так много Питонов? / Хабр

Питон изумителен.

Удивительно, но это довольно неоднозначное заявление. Что я имею ввиду под “Питоном”? Может, абстрактный интерфейс Питона? Или CPython, распространенная реализация Питона (не путать с похожим по названию Cython)? Или я имею ввиду что-то совсем иное? Может, я косвенно ссылаюсь на Jython, или IronPython, или PyPy. Или может я отвлекся так сильно, что говорю о RPython или RubyPython (которые очень сильно отличаются).

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

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

В этом посте я начну с нуля и пройдусь по разным реализациям Питона, а закончу подробным введением в PyPy, за которым, по моему мнению, будущее языка.

Все начинается с понимания того, чем на самом деле является “Питон”.

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

Питон интерпретируемый или компилируемый?

Это распространенный источник непонимания среди новичков Питона.

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

Второе: “интерпретируемый” и “компилируемый” это свойства имплементации, но не интерфейса.

Так что сам вопрос не совсем корректен.

В случае с самой распространенной реализацией (CPython: написанный на C, часто называемый просто “Python”, и, конечно, именно тот, который вы используете, если понятия не имеете о чем я толкую) ответ: интерпретируемый, с некоторой компиляцией. CPython компилирует* исходный код на Питоне в байткод, а затем интерпретирует этот байткод, запуская его в процессе.

* Замечание: это не совсем “компиляция” в традиционном смысле. Обычно, мы считаем, что “компиляция” это конвертация из высокоуровневого языка в машинный код. Тем не менее – в некотором роде это “компиляция”.

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

Байткод или машинный код

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

— Cи компилируется в машинный код, который впоследствии запускается напрямую процессором. Каждая инструкция заставляет процессор производить разные действия.
— Java компилируется в байткод, который впоследствии запускается на Виртуальной машине Java (Java Virtual Machine, JVM), абстрактном компьютере, который запускает программы. Каждая инструкция обрабатывается JVM, который взаимодействует с компьютером.

Сильно упрощая: машинный код намного быстрее, но байткод лучше переносим и защищен.

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

Возвращаясь к CPython, цепочка операций выглядит следующим образом:

1. CPython компилирует ваш исходный код на Питоне в байткод.
2. Этот байткод запускается на виртуальной машине CPython.

Новички зачастую допускают, что Питон компилируемый из-за наличия .pyc-файлов. Это отчасти верно: .pyc-файлы – это скомпилированный байткод, который впоследствии интерпретируется. Так что если вы запускали ваш код на Питоне, и у вас есть .pyc-файл, то во второй раз он будет работать быстрее, потому что ему не нужно будет заново компилироваться в байткод.
Альтернативные виртуальные машины: Jython, IronPython и другие

Как я говорил выше, у Питона существует несколько реализаций. Опять же, как говори-лось выше, самой популярной является CPython. Эта версия Питона написана на C и считается имплементацией “по умолчанию”.

Но как насчет альтернатив? Одна из наиболее видных это Jython, реализация Питона на Java, которая использует JVM. В то время как CPython генерирует байткод для запуска на CPython VM, Jython генерирует байткод Java для запуска на JVM (это то же самое, что генерируется при компиляции программы на Java).

“Зачем может понадобиться использовать альтернативную реализацию?”, спросите вы. Ну, для начала, разные реализации хорошо ладят с разными наборами технологий.

CPython упрощает написание C-расширений для кода на Питоне потому что в конце он запускается интерпретатором Cи. Jython в свою очередь упрощает работу с другими программами на Java: вы можете импортировать любые Java-классы без дополнительных усилий, призывая и используя ваши Java-классы из программ на Jython. (Замечание: если вы еще не думали об этом всерьез, это довольно безумно. Мы дожили до того времени, когда можно смешивать разные языки и компилировать их в одну сущность. Как заметил Rostin, программы, смешивающие код на Фортране с Cи появились довольно давно, так что это не совсем новинка. Но это все же круто.)

В качестве примера, вот корректный код на Jython:

[Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_51
>>> from java.util import HashSet
>>> s = HashSet(5)
>>> s.add("Foo")
>>> s.add("Bar")
>>> s
[Foo, Bar]

IronPython это другая популярная реализация Питона, написанная полностью на C# и предназначенная для .NET. В частности, она запускается на виртуальной машине .NET, если ее можно так назвать, на Common Language Runtime (CLR), от Майкрософт, сравнимым с JVM.

Можно сказать, что Jython: Java :: IronPython: C#. Они работают на соответствующих виртуальных машинах, есть возможность импортировать классы C# в код IronPython и классы Java в код Jython, и так далее.

Вполне реально выжить, не прикасаясь к ни к чему, кроме CPython. Но, переходя на другие имплементации, вы получаете преимущество, в основном из-за используемого стека технологий. Используете много языков, основанных на JVM? Jython может вам подойти. Все на .NET? Возможно, стоит попробовать IronPython (и, возможно, вы уже сделали).

Кстати, хоть это и не станет причиной для перехода на другую имплементацию, стоит упомянуть, что имплементации эти на самом деле отличаются поведением. Это касается не только способов интерпретации кода на Питоне. Однако эти отличия, как правило, не-значительны, они исчезают и появляются со временем из-за активной разработки. К примеру, IronPython использует строки Unicode по умолчанию; однако CPython использует ASCII в версиях 2.x (выдавая ошибку UnicodeEncodeError для не-ASCII символов), и при этом поддерживает символы Unicode по умолчанию в версиях 3.x.

Компиляция на лету (Just-in-Time Compilation): PyPy и будущее

Итак, у нас есть имплементация Питона, написанная на Си, еще одна – на Java, и третья на C#. Следующий логичный шаг: имплементация Питона, написанная на… Питоне. (Подготовленный читатель заметит, что это утверждение немного обманчиво).

Вот почему это может сбивать с толку. Для начала, давайте обсудим компиляцию на лету (just-in-time или JIT).

JIT. Почему и как

Напомню, что нативный машинный код намного быстрее байткода. Ну, а что, если бы можно было компилировать часть байткода и запускать его как нативный код? Пришлось бы “заплатить” некоторую цену (иными словами: время) за компиляцию байткода, но если результат будет работать быстрее, то это здорово! Этим и мотивируется JIT-компиляция, гибридная техника, которая совмещает в себе преимущества интерпретато-ров и компиляторов. В двух словах – JIT старается использовать компиляцию, чтобы ускорить систему интерпретации.

Например, вот распространенный подход JIT:

  1. Определить байткод, который запускается часто.
  2. Скомпилировать его в нативный машинный код.
  3. Закэшировать результат.
  4. Всегда когда необходимо запустить тот же самый байткод, использовать уже скомпилированный машинный код и пожинать плоды (в частности, прирост скорости).

В этом вся суть PyPy: использовать JIT в Питоне (в дополнении можно найти предыдущие попытки). Конечно, есть и другие цели: PyPy нацелен на кроссплатформенность, работу с небольшим количеством памяти и поддержку stackless (отказа от стека вызовов языка Си в пользу собственного стека). Но JIT это главное преимущество. В среднем на основе временных тестов, фактор ускорения составляет 6.27. Более подробные данные можно получить из схемы от PyPy Speed Center:

В PyPy сложно разобраться

У PyPy есть огромный потенциал, и в данный момент он хорошо совместим с CPython (так что на нем можно запускать Flask, Django, и т.д.).

Но с PyPy есть много путаницы. (оцените, к примеру, это бессмысленное предложение создать PyPyPy…). По моему мнению основная причина в том, что PyPy одновременно является:

1. Интерпретатором Питона, написанным на RPython (не Python (я обманул вас до этого)). RPython это подмножество Python со статичной типизацией. В Python, вести тщательные беседы о типах “в целом невозможно” почему это так сложно? рассмотрите следующее:

x = random.choice([1, "foo"])

это корректный код на Python (спасибо Ademan‘у). Какой тип у x? Как мы можем обсуждать типы переменных, когда типы даже не форсируются?). В RPython мы жертвуем некоторой гибкостью, но взамен получаем возможность гораздо проще управлять памятью и много чего еще, что помогает при оптимизации.

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

Для простоты описания, я буду называть их PyPy (1) и PyPy (2).

Зачем могут понадобиться эти две вещи, и почему – в одном наборе? Думайте об этом так: PyPy (1) это интерпретатор, написанный на RPython. То есть он берет пользовательский код на Питоне и компилирует его в байткод. Но чтобы сам интерпретатор (написанный на RPython) мог работать, он должен быть интерпретирован другой реализацией Пи-тона, верно?

Итак, можно просто использовать CPython чтобы запускать интерпретатор. Но это будет не слишком быстро.

Вместо этого мы используем PyPy (2) (называемый RPython Toolchain) чтобы компилировать интерпретатор PyPy в код для другой платформы (например, C, JVM, или CLI) для запуска на конечной машине, с добавлением JIT. Это волшебно: PyPy динамически добавляет JIT к интерпретатору, генерируя собственный компилятор! (Опять же, это безумие: мы компилируем интерпретатор, добавляя другой отдельный, самостоятельный компилятор).

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

Повторим: настоящая красота PyPy в том, что мы можем написать себе кучу разных интерпретаторов Питона на RPython не волнуясь о JIT (не считая пары деталей). После этого PyPy реализует для нас JIT, используя RPython Toolchain/PyPy (2).

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

В качестве отступления я бы хотел заметить, что сам JIT совершенно восхитителен. Он использует технику под названием “отслеживание” (tracing), которая работает следующим образом:

  1. Запустить интерпретатор и интерпретировать все (не добавляя JIT).
  2. Провести легкое профилирование интерпретированного кода.
  3. Определить операции, которые уже выполнялись ранее.
  4. Скомпилировать эти части кода в машинный код.

Узнать больше можно из этой легкодоступной и очень интересной публикации.

Подытожим: мы используем PyPy-компилятор RPython-в-Си (или другую целевую плат-форму), чтобы скомпилировать реализованный на RPython интерпретататор PyPу.

Заключение

Почему все это так восхитительно? Почему стоит гнаться за этой безумной идеей? По-моему, Алекс Гейнор объяснил это очень хорошо в своем блоге: “[За PyPy будущее] потому что [он] более быстрый, более гибкий и является лучшей платформой для развития Питона”.

Вкратце:

  • Он быстрый – потому что компилирует исходный код в нативный код (используя JIT).
  • Он гибкий – потому что добавляет JIT в интерпретатор без особых усилий.
  • Он гибкий (опять) – потому что вы можете писать интерпретаторы в RPython, что впоследствии упрощает расширение по сравнению с тем же Си (на самом деле упрощает настолько, что даже есть инструкция по написанию собственных интерпретаторов).
Дополнение: другие названия, которые вы, возможно, слышали

Python 3000 (Py3k): альтернативное название Python 3.0, основной релиз Питона с обратной совместимостью, который появился в 2008. году. Команда Py3k предсказала, что новой версии понадобится примерно пять лет чтобы полностью прижиться. И в то время, как большинство (внимание: надуманное утверждение) разработчиков на Питоне продолжают использовать Python 2.x, люди все больше задумываются о Py3k.

Cython: надмножество Python, включающее возможность вызывать функции Си.

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

Numba: “специализированный just-in-time компилятор”, который добавляет JIT в снабженный примечаниями код на Питоне. Проще говоря, вы даете ему подсказки, а он ускоряет некоторые части вашего кода. Numba является частью дистрибутива Anaconda набора пакетов для анализа и управления данными.

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

Psyco: модуль расширения Питона, одна из первых попыток Питона в области JIT. Давно помечен как “неподдерживаемый и мертвый”. Главный разработчик Psyco Армин Риго сейчас работает над PyPy.

Привязки к языкам
  • RubyPython: мост между виртуальными машинами Ruby и Python. Позволяет встраивать код на Питоне в код на Ruby. Вы обозначаете, где начинается и заканчивается Питон, а RubyPython обеспечивает передачу данных между виртуальными машинами.
  • PyObjc: языковое соединение между Python и Objective-C, которые ведет себя как мост между ними. На практике это означает, что вы можете использовать библиотеки Objective-C (включая все, что нужно для создания приложения под OS X) в коде на Питоне, и модули Питона в коде на Objective-C. Это удобно, потому что CPython написан на Си, который является подмножеством Objective-C.
  • PyQt: в то время как PyObjc позволяет связать Питон с компонентами OS X GUI, PyQt делает то же для фреймворка Qt. Это дает возможность создавать полноценные графические интерфейсы, обращаться к SQL базам данных и так далее. Еще один инструмент, нацеленный на перенос простоты Питона в другие фреймворки.
JavaScript фреймворки
  • pyjs (Pyjamas): фреймворк для создания веб и десктопных приложений на Питоне. Включает в себя компилятор Python-в-JavaScript, набор виджетов и некоторые другие инструменты.
  • Brython: виртуальная машина Python, написанная на Javascript. Позволяет запустить код на Py3k в веб-браузере.

10 шагов к успешному Python-проекту / Блог компании RUVDS.com / Хабр

Материал, перевод которого мы сегодня публикуем, посвящён инструментам, которые позволяют оснащать Python-проекты средствами форматирования кода, тестирования, непрерывной интеграции и анализа зависимостей. Это помогает ускорить процесс разработки, способствует повышению качества, единообразия и безопасности кода. Предполагается, что у читателя этого материала уже есть некоторый опыт Python-разработки и проект на Python, с которым он, в ходе чтения, будет экспериментировать. Если такого проекта у вас нет — здесь можно узнать о том, как подготовить среду разработки и создать Python-пакет. Примеры, которые будут здесь приводиться, подготовлены с использованием macOS и Python 3.7.



Шаг 1. Установка Black



Код проекта должен следовать соглашениям о стиле кода. Black — это Python-пакет, который автоматически форматирует код, приводя его внешний вид к стандарту PEP 8. Black — это сравнительно новый проект, но у него уже набралось больше миллиона загрузок. Его использование быстро стало признаком хорошего тона в Python-разработке. Вот руководство по Black.

Я, в качестве редактора кода, использую Atom, поэтому я добавил в Atom пакет Python-Black. О том, как его установить, можно узнать здесь. После установки этого пакета Atom будет переформатировать код после сохранения файла.

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

Добавьте параметр black==18.9b0 в первую нашедшуюся свободную строку файла requirements_dev.txt и выполните команду install -r requirements_dev.txt.

Black, по умолчанию, устанавливает длину строки кода равной 88 символов. В некоторых руководствах по стилю, например — в Sphinx, требуется использовать длину строки, равную 79 символов. В настройках пакета Black-Atom можно задать желаемую длину строки.

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

Шаг 2. Создание файла .pypirc


Когда для отправки сборок приложения в TestPyPI и PyPI используется twine, сведения для входа в систему требуется вводить вручную. Если вы не знакомы с twine — взгляните на этот материал. Сейчас мы будем автоматизировать этот процесс.

Twine умеет работать с файлом .pypirc, который должен находиться в нашей домашней директории. Это средство, выгружая данные, берёт из данного файла URL, логин и пароль.

Итак, создадим в домашней директории файл .pypirc:

touch ~/.pypirc

Добавим в него следующий текст:
[distutils]
index-servers =
    pypi
    testpypi

[testpypi]
repository: https://test.pypi.org/legacy
username = your_username
password = your_pypitest_password

[pypi]
username = your_username
password = your_pypi_password

Понятно, что сюда надо вписать ваши реальные имя пользователя и пароль. Кроме того, проверьте, чтобы этот файл был бы сохранён в домашней директории, а не в текущей рабочей директории. Если вы хотите защитить этот файл от других пользователей, вы можете, пользуясь средствами командной строки, настроить его разрешения:
chmod 600 ~/.pypirc

Теперь ваш пакет можно загружать в TestPyPI пользуясь следующей командой:
twine upload -r testpypi dist/*

В обычный PyPI можно загружать пакеты так:
twine upload dist/*

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

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

Шаг 3. Установка и настройка pytest



Pytest — это самая популярная, лёгкая в использовании библиотека для тестирования кода, написанного на Python. В этом примере мы добавим в проект простые тесты. Вот, если вас интересуют подробности о pytest, хорошее вводное руководство по этому инструменту.

Добавим сведения о pytest в файл requirements_dev.txt:

pytest==4.3.0

Выполним установку пакета:
pip install requirements_dev.txt

Теперь выполним следующую команду, которая позволит pytest обнаружить наш пакет:
pip install -e .

Если вы деактивировали свою виртуальную среду разработки, то вам, для запуска тестов, понадобится снова выполнить обе команды pip.

Шаг 4. Создание тестов


Добавьте папку test в корневую директорию вашего проекта. Поместите в неё файл test_your_package_name.py. Мой файл называется test_notebookc.py. Если имя файла начинается с test_, pytest может автоматически обнаружить такой файл.

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

"""Tests for `notebookc` package."""
import pytest
from notebookc import notebookc


def test_convert(capsys):
    """Correct my_name argument prints"""
    notebookc.convert("Jill")
    captured = capsys.readouterr()
    assert "Jall" in captured.out

Что здесь происходит?

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

После этого мы вызываем функцию (convert), передавая ей в качестве аргумента имя Jill. Далее — захватываем то, что выводит функция. Тут стоит сказать, что рассматриваемая функция крайне проста. Она берёт параметр my_name и делает следующее:

print(f"I’ll convert a notebook for you some day, {my_name}.")

Pytest проверяет, содержится ли Jall в том, что выводит функция. Там этой строки быть не должно, так как мы передаём функции Jill. Вот документация по pytest, в которой можно найти сведения о перехвате вывода.

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


В ходе теста выявлена ошибка

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

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


Успешное завершение теста

Теперь всё хорошо. Тест позволяет убедиться в том, что если кто-то передаёт нашей функции строку, эта строка попадёт в тот текст, который выводит эта функция.

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

Когда мы создавали предыдущий тест, мы писали код, который приводит к успешному завершению теста. Это называется разработкой через тестировании (Test-Driven Development, TDD). TDD — это доказавший свою ценность подход к программированию, помогающий писать код, в котором оказывается меньше ошибок, чем в нём было бы без использования TDD. Вот полезный материал по TDD.

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

После того, как пакет успешно прошёл тесты — мы готовы к тому, чтобы воспользоваться системой непрерывной интеграции.

Шаг 5. Регистрация в сервисе Travis CI и его настройка



Travis CI — это «распределённый веб-сервис для сборки и тестирования программного обеспечения». Его недавно купила компания Idera. Существуют и другие системы непрерывной интеграции, но Travis CI — это популярный, бесплатный для опенсорсного применения и хорошо документированный инструмент, поэтому мы будем пользоваться именно им.

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

Cоздайте учётную запись на сайте https://travis-ci.org/. Далее, щёлкните по ссылке Review and add your authorized organizations на странице профиля. Вам предложат ввести пароль для доступа к GitHub. Щёлкните по Grant в разделе Organization access.


Настройка учётной записи Travis CI

Мне понадобилось синхронизировать аккаунт для того, чтобы в учётной записи появились бы сведения о notebooktoall и о репозитории notebookc. Обычно для того чтобы обработка кода средствами Travis CI заработала бы, требуется что-то около минуты. После этого нужно активировать репозиторий, воспользовавшись переключателем, показанным на следующем рисунке.


Активация репозитория

Теперь нужно щёлкнуть по кнопке Settings. Тут нужно указать, может ли Travis выполнять сборку на основе отправленных в репозиторий пулл-запросов или веток.


Настройка сборки проекта

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

Шаг 6. Создание файла .travis.yml


В корневой папке проекта создайте файл .travis.yml со следующим содержимым:
dist: xenial
language: python
python: 3.7.2
install:
  - pip install -r requirements_dev.txt
  - pip install -e .

script:
  - pytest

Строка dist: xenial нужна для того чтобы указать Travis на необходимость использования для организации виртуального окружения Ubuntu Xenial 16.04. Для тестирования кода Python 3.7 нужна именно Ubuntu Xenial, подробности об этом можно почитать здесь.

Раздел install позволяет обеспечить установку пакетов, используемых при разработке проекта. Команда pip install -e . выполняет установку нашего пакета в виртуальном окружении Travis. После этого Travis, запуская pytest, сможет найти наш пакет.

Шаг 7. Тестирование в Travis CI


Выполните коммит изменений, отправьте их на GitHub, выполните PR. Travis должен, в течение нескольких секунд, начать работу.
Travis за работой

Вот чем занимается Travis, обрабатывая проект.


Действия, выполняемые Travis при обработке проекта

Если PR оказался неудачным — Travis об этом сообщит. Обратите внимание на то, что если пулл-запрос оказался неудачным, то можно отправить изменения в ту же ветку и Travis автоматически примется за работу.

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

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


Сборка проекта выполнена успешно

Если же на странице нет ни зелёных, ни красных надписей, откройте меню More options и выберите пункт Requests. Если вы видите тут сообщения об ошибках, выводимые красным цветом — проанализируйте их. Если вы видите сообщение об ошибке Build config file is required, это означает, что Travis не может найти в репозитории ваш файл .travis.yml. Исправьте это и ошибка исчезнет.

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

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

Теперь проанализируем наш проект на предмет покрытия кода тестами.

Шаг 8. Оценка покрытия кода тестами


Отчёт о покрытии кода тестами позволяет узнать о том, какая часть кода проекта, пусть и небольшая, протестирована. Для создания подобных отчётов мы будем пользоваться пакетом pytest-cov.

В файл requirements_dev.txt добавим следующую строку:

pytest-cov==2.6.1

Выполним такую команду:
pytest --cov=my_project_name

В моём случае, после выполнения команды pytest --cov=notebookc был выведен следующий отчёт.
Отчёт о покрытии кода тестами

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

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

Шаг 9. Использование Coveralls


Проект Coveralls позволяет поддерживать исторические сведения о покрытии кода тестами.
Coveralls

Для того, чтобы воспользоваться возможностями этого проекта, нужно зарегистрироваться на сайте https://coveralls.io/, используя данные учётной записи GitHub. Потом нужно подключить репозиторий.

В файл requirements_dev.txt нужно добавить строку coveralls==1.6.0. Этот файл, кстати, на данном этапе работы над проектом должен выглядеть так:

pip==19.0.3
wheel==0.33.0
twine==1.13.0
pytest==4.3.0
pytest-cov==2.6.1
coveralls==1.6.0

Отредактируем файл .travis.yml, приведя его к такому виду (в вашем случае здесь будет название вашего проекта):
dist: xenial
language: python
python: 3.7.2
install:
  — pip install -r requirements_dev.txt
  — pip install -e .

script:
  — pytest --cov=my_package_name
after_success:
  — coveralls

Теперь, когда Travis будет собирать проект, он установит необходимые пакеты, запустит тесты и создаст отчёт о покрытии кода тестами. Затем этот отчёт будет отправлен на сервис Coveralls.

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


Обработка проекта, отчёт о покрытии кода тестами

Теперь среди проверок PR присутствует и проверка, выполняемая средствами Coveralls.

На странице Coveralls можно убедиться в том, что проект покрыт тестами на 100%.


Сведения о покрытии кода тестами

Теперь давайте оснастим наш проект ещё одним полезным инструментом.

Шаг 10. Работа с PyUp


Сервис PyUp.io позволяет разработчику узнавать о том, устарели ли используемые им зависимости, а также о том, имеют ли они уязвимости. Этот сервис автоматически выполняет пулл-запросы, направленные на обновление пакета на GitHub. Для того чтобы воспользоваться возможностями этого проекта, нужно зарегистрироваться, пользуясь учётной записью GitHub, на его сайте — https://pyup.io/. При добавлении репозитория рекомендуется установить периодичность обновлений (Update Schedules) в значение every week. При таком подходе, если у вашего проекта множество зависимостей, вы не столкнётесь со слишком большим количеством пулл-запросов.
Настройка обновлений

Вот как выглядят сведения о пакетах, некоторые из которых устарели, на сайте PyUp.io.


Сведения о пакетах

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

Итоги


Из этого материала вы узнали о том, как пользоваться при разработке Python-проектов такими средствами, как Black, pytest, Travis CI, Coveralls и PyUp. Они помогают контролировать зависимости проектов, форматировать и тестировать код, проверять и собирать проекты. Надеемся, вам эти инструменты пригодятся.

Уважаемые читатели! Какими инструментами вы пользуетесь при разработке Python-проектов?

более чем 30-кратное ускорение Python-кода / Блог компании RUVDS.com / Хабр

Python — это язык, который любят многие программисты. Этим языком невероятно легко пользоваться. Всё дело в том, что код, написанный на Python, отличается интуитивной понятностью и хорошей читабельностью. Однако в разговорах о Python часто можно слышать одну и ту же жалобу на этот язык. Особенно тогда, когда о Python говорят знатоки языка C. Вот как она звучит: «Python — это медленно». И те, кто так говорят, не грешат против истины.

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

Есть несколько способов ускорения Python-программ. Например, можно применять библиотеки, рассчитанные на использование нескольких ядер процессора. Тем, кто работает с Numpy, Pandas или Scikit-Learn, можно посоветовать взглянуть на программный комплекс Rapids, позволяющий задействовать GPU при проведении научных расчётов.

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

Но как быть в том случае, если ваш код — это чистый Python? Что если у вас есть большой цикл for, который вам совершенно необходимо использовать, и выполнение которого просто нельзя распараллелить из-за того, что обрабатываемые в нём данные должны обрабатываться последовательно? Можно ли как-то ускорить сам Python?

Ответ на этот вопрос даёт Cython — проект, используя который можно значительно ускорить код, написанный на Python.

Что такое Cython?


Cython, по своей сути, это промежуточный слой между Python и C/C++. Cython позволяет писать обычный Python-код с некоторыми незначительными модификациями, который затем напрямую транслируется в C-код.

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

x = 0.5

При использовании Cython при объявлении переменной нужно указать её тип:
cdef float x = 0.5

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

Установка Cython предельно проста:

pip install cython

Типы в Cython


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

Если речь идёт о переменных, то тут нам доступны следующие типы:

  • cdef int a, b, c
  • cdef char *s
  • cdef float x = 0.5 (число одинарной точности)
  • cdef double x = 63.4 (число двойной точности)
  • cdef list names
  • cdef dict goals_for_each_play
  • cdef object card_deck

Обратите внимание на то, что тут, фактически, показаны типы C/C++!

При работе с функциями нам доступны следующие типы:

  • def — обычная Python-функция, вызывается только из Python.
  • cdef — Cython-функция, которую нельзя вызвать из обычного Python-кода. Такие функции можно вызывать только в пределах Cython-кода.
  • cpdef — Функция, доступ к которой можно получить и из C, и из Python.

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

Ускорение кода с использованием Cython


Начнём с создания Python-бенчмарка. Это будет цикл for, в котором выполняется вычисление факториала числа. Соответствующий код на чистом Python будет выглядеть так:
def test(x):
    y = 1
    for i in range(1, x+1):
        y *= i
    return y

Cython-эквивалент этой функции очень похож на её исходный вариант. Соответствующий код нужно поместить в файл с расширением .pyx. Единственное изменение, которое нужно внести в код, заключается в добавлении в него сведений о типах переменных и функции:
cpdef int test(int x):
    cdef int y = 1
    cdef int i
    for i in range(1, x+1):
        y *= i
    return y

Обратите внимание на то, что перед функцией стоит ключевое слово cpdef. Это позволяет вызывать данную функцию из Python. Кроме того, тип назначен и переменной i, играющей роль счётчика цикла. Не будем забывать о том, что типизировать нужно все переменные, объявленные в функции. Это позволит компилятору C узнать о том, какие именно типы ему использовать.

Теперь создадим файл setup.py, который поможет нам преобразовать Cython-код в C-код:

from distutils.core import setup
from Cython.Build import cythonize

setup(ext_modules = cythonize('run_cython.pyx'))

Выполним компиляцию:
python setup.py build_ext --inplace

Теперь С-код готов к использованию.

Если взглянуть в папку, в которой находится Cython-код, там можно будет найти все файлы, необходимые для запуска C-кода, включая файл run_cython.c. Если вам интересно — откройте этот файл и посмотрите на то, какой С-код сгенерировал Cython.

Теперь всё готово к тестированию нашего сверхбыстрого C-кода. Ниже приведён код, используемый для тестирования и сравнения двух вариантов программы.

import run_python
import run_cython
import time

number = 10

start = time.time()
run_python.test(number)
end =  time.time()

py_time = end - start
print("Python time = {}".format(py_time))

start = time.time()
run_cython.test(number)
end =  time.time()

cy_time = end - start
print("Cython time = {}".format(cy_time))

print("Speedup = {}".format(py_time / cy_time))

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

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


Итоги


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

Уважаемые читатели! Используете ли вы Cython в своих проектах?

Синтаксис языка Python | Python 3 для начинающих и чайников

Синтаксис языка Python, как и сам язык, очень прост.

Синтаксис

  • Конец строки является концом инструкции (точка с запятой не требуется).

  • Вложенные инструкции объединяются в блоки по величине отступов. Отступ может быть любым, главное, чтобы в пределах одного вложенного блока отступ был одинаков. И про читаемость кода не забывайте. Отступ в 1 пробел, к примеру, не лучшее решение. Используйте 4 пробела (или знак табуляции, на худой конец).

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

    Основная инструкция:
        Вложенный блок инструкций

Несколько специальных случаев

  • Иногда возможно записать несколько инструкций в одной строке, разделяя их точкой с запятой:

    a = 1; b = 2; print(a, b)

    Но не делайте это слишком часто! Помните об удобочитаемости. А лучше вообще так не делайте.

  • Допустимо записывать одну инструкцию в нескольких строках. Достаточно ее заключить в пару круглых, квадратных или фигурных скобок:

    if (a == 1 and b == 2 and
        c == 3 and d == 4):  # Не забываем про двоеточие
          print('spam' * 3)
  • Тело составной инструкции может располагаться в той же строке, что и тело основной, если тело составной инструкции не содержит составных инструкций. Ну я думаю, вы поняли :). Давайте лучше пример приведу:

    if x > y: print(x)

Полное понимание синтаксиса, конечно, приходит с опытом, поэтому я советую вам заглянуть в рубрику «Примеры программ».

Также советую прочитать PEP 8 — руководство по написанию кода на Python и Документирование кода в Python. PEP 257.

Для вставки кода на Python в комментарий заключайте его в теги <pre><code>Ваш код</code></pre>

Как получить описание метода Python?

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
  3. Вакансии Программирование и связанные с ним технические возможности карьерного роста
  4. Талант Нанимайте технических специалистов и создавайте свой бренд работодателя
  5. Реклама Обратитесь к разработчикам и технологам со всего мира
  6. О компании
.Практическое руководство по дескриптору

— документация Python 3.8.5

Автор

Раймонд Хеттингер

Контакт

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

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

В общем, дескриптор — это атрибут объекта с «поведением привязки», один доступ к атрибуту которого был переопределен методами в дескрипторе протокол. Эти методы: __get __ () , __set __ () и __ удалить __ () . Если какой-либо из этих методов определен для объекта, он называется дескриптором.

Поведение по умолчанию для доступа к атрибутам — получить, установить или удалить атрибут из словаря объекта. Например, a.x имеет поисковую цепочку начиная с a .__ dict __ ['x'] , затем type (a) .__ dict __ ['x'] и продолжая через базовые классы типа (a) , исключая метаклассы. Если искомое значение — это объект, определяющий один из методов дескриптора, затем Python может переопределить поведение по умолчанию и вместо этого вызвать метод дескриптора.То, где это происходит в цепочке приоритетов, зависит от того, какие методы дескриптора были определены.

Дескрипторы — это мощный протокол общего назначения. Они механизм за свойствами, методами, статическими методами, методами класса и super () . Они используются в самом Python для реализации новых классов стилей. введен в версии 2.2. Дескрипторы упрощают базовый C-код и предлагают гибкий набор новых инструментов для повседневных программ на Python.

описание__get __ (self, obj, type = None) -> значение

descr .__ set __ (self, obj, value) -> Нет

descr .__ delete __ (self, obj) -> Нет

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

Если объект определяет __set __ () или __delete __ () , он считается дескриптор данных. Вызываются дескрипторы, которые определяют только __get __ () . дескрипторы без данных (они обычно используются для методов, но другие применения возможно).

Дескрипторы данных и не-данные различаются тем, как вычисляются переопределения с относительно записей в словаре экземпляра. Если словарь экземпляра имеет запись с тем же именем, что и дескриптор данных, дескриптор данных имеет приоритет. Если в словаре экземпляра есть запись с таким же name как дескриптор, не связанный с данными, словарная запись имеет приоритет.

Чтобы создать дескриптор данных только для чтения, определите как __get __ (), и __set __ () с __set __ () , вызывающим AttributeError , когда называется.Определение метода __set __ () с возбуждением исключения заполнителя достаточно, чтобы сделать его дескриптором данных.

Дескриптор может быть вызван напрямую по имени его метода. Например, d .__ получить __ (obj) .

В качестве альтернативы, дескриптор обычно вызывается автоматически при доступе к атрибуту. Например, obj.d ищет d в словаре. из obj . Если d определяет метод __get __ () , то d.__get __ (объект) вызывается в соответствии с правилами приоритета, перечисленными ниже.

Детали вызова зависят от того, является ли obj объектом или классом.

Для объектов оборудование находится в object .__ getattribute __ () который преобразует b.x в типа (b) .__ dict __ ['x'] .__ get __ (b, type (b)) . В реализация работает через цепочку приоритетов, которая дает дескрипторы данных приоритет над переменными экземпляра, приоритет переменных экземпляра над не данными дескрипторы и назначает самый низкий приоритет __getattr __ () , если он предоставлен.Полную реализацию C можно найти в PyObject_GenericGetAttr () в Объекты / object.c.

Для классов оборудование относится к типу .__ getattribute __ () , который преобразует B.x в B .__ dict __ ['x'] .__ get __ (None, B) . На чистом Python это выглядит нравится:

 def __getattribute __ (self, key):
    "Эмулировать type_getattro () в Objects / typeobject.c"
    v = объект .__ getattribute __ (сам, ключ)
    если hasattr (v, '__get__'):
        return v .__ get __ (Нет, сам)
    вернуть v
 

Важно помнить:

Объект, возвращаемый функцией super () , также имеет собственный __getattribute __ () метод вызова дескрипторов.Поиск по атрибуту super (B, obj) .m ищет obj .__ class __.__ mro__ для базового класса A сразу после B а затем возвращает A .__ dict __ ['m'] .__ get __ (obj, B) . Если не дескриптор, м. возвращено без изменений. Если нет в словаре, m возвращается к поиск с использованием объекта .__ getattribute __ () .

Реализация

.

функций Python


Функция — это блок кода, который выполняется только при вызове.

В функцию можно передавать данные, называемые параметрами.

В результате функция может возвращать данные.


Создание функции

В Python функция определяется с использованием def ключевое слово:

Пример

def my_function ():
print («Привет от функции»)


Вызов функции

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

Пример

def my_function ():
print («Привет от функции»)

my_function ()

Попробуй сам »

Аргументы

Информация может передаваться в функции как аргументы.

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

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

Пример

def my_function ( fname ):
print (fname + «Refsnes»)

my_function ( «Emil» )
my_function ( «Tobias» )
my_function ( «Linus» )

Попробуй сам »

Аргументы часто сокращаются до args в документации Python.



Параметры или аргументы?

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

С точки зрения функции:

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

Аргумент — это значение, которое отправляется функции при ее вызове.


Количество аргументов

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

Пример

Эта функция ожидает 2 аргумента и получает 2 аргумента:

def my_function (fname, lname):
print (fname + «» + lname)

my_function («Emil», «Refsnes»)

Попробуй сам » Если вы попытаетесь вызвать функцию с 1 или 3 аргументами, вы получите ошибку:

Пример

Эта функция ожидает 2 аргумента, но получает только 1:

def my_function (fname, lname):
print (fname + «» + lname)

my_function («Emil»)

Попробуй сам »

Произвольные аргументы, * args

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

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

Пример

Если количество аргументов неизвестно, добавьте перед именем параметра * :

def my_function (* kids):
print («Самый младший ребенок is «+ kids [2])

my_function (» Эмиль «,» Тобиас «,» Линус «)

Попробуй сам »

Произвольные аргументы часто сокращаются до * args в документации Python.


Аргументы ключевого слова

Вы также можете отправлять аргументы с синтаксисом ключ = значение .

Таким образом, порядок аргументов не имеет значения.

Пример

def my_function (child3, child2, child1):
print («Самый младший ребенок is «+ child3»

my_function (child1 = «Emil», child2 = «Tobias», child3 = «Linus»)

Попробуй сам »

Фраза аргументы ключевого слова часто сокращается до kwargs в документации Python.


Аргументы произвольного ключевого слова, ** kwargs

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

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

Пример

Если количество аргументов ключевого слова неизвестно, добавьте двойной ** перед именем параметра:

def my_function (** kid):
print («Его фамилия» + kid [«lname»])

my_function (fname = «Tobias», lname = «Refsnes»)

Попробуй сам »

Произвольные аргументы Kword часто сокращаются до ** kwargs в документации Python.


Значение параметра по умолчанию

В следующем примере показано, как использовать значение параметра по умолчанию.

Если мы вызываем функцию без аргументов, она использует значение по умолчанию:

Пример

def my_function ( country = «Норвегия» ):
print («Я из» + страна)

my_function («Швеция»)
my_function («Индия»)
my_function ()
my_function («Бразилия»)

Попробуй сам »

Передача списка в качестве аргумента

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

Например, если вы отправите список в качестве аргумента, он все равно будет списком, когда он достигает функции:

Пример

def my_function (food):
для x в food:
print (x)

fruit = [«яблоко», «банан», «вишня»]

my_function (fruit)

Попробуй сам »

Возвращаемые значения

Чтобы функция возвращала значение, используйте возврат выписка:

Пример

def my_function (x):
return 5 * x

print (my_function (3))
print (my_function (5))
печать (моя_функция (9))

Попробуй сам »

Пропуск Заявление

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


Рекурсия

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

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

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

В этом примере tri_recursion () — это функция, которую мы определили для вызова самой себя («рекурсивная»). В качестве данных мы используем переменную k, которая уменьшается на (-1) каждый раз, когда мы выполняем рекурсию. Рекурсия заканчивается, когда условие не больше 0 (т.е. когда оно равно 0).

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

Пример

Пример рекурсии

def tri_recursion (k):
если (k> 0):
результат = k + tri_recursion (k — 1)
print (результат)
еще:
результат = 0
вернуть результат

print («\ n \ nРезультаты примера рекурсии»)
tri_recursion (6)

Попробуй сам »

.

3. Модель данных — документация Python 3.8.5

3.1. Объекты, значения и типы

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

У каждого объекта есть идентификатор, тип и значение. идентичность объекта никогда изменяется после создания; вы можете думать об этом как об адресе объекта в объем памяти.Оператор « is » сравнивает идентичность двух объектов; в id () Функция возвращает целое число, представляющее его личность.

Детали реализации CPython: Для CPython id (x) — это адрес памяти, где хранится x .

Тип объекта определяет операции, которые поддерживает объект (например, «выполняет имеет длину? »), а также определяет возможные значения для объектов этого тип. Функция type () возвращает тип объекта (который является объектом сам).Как и его идентичность, объект типа также неизменен.

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

Объекты никогда не уничтожаются явным образом; однако, когда они становятся недоступными они могут быть собраны мусором. Реализации разрешено откладывать мусор собирать или опускать вовсе — это вопрос качества реализации как реализована сборка мусора, если не собираются объекты, все еще доступны.

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

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

Некоторые объекты содержат ссылки на «внешние» ресурсы, такие как открытые файлы или окна. Подразумевается, что эти ресурсы освобождаются, когда объект сборщиком мусора, но поскольку сборка мусора не гарантируется, такие объекты также предоставляют явный способ освободить внешний ресурс, обычно метод close () .Программам настоятельно рекомендуется явно закрыть такие объекты. Заявление « попробуйте , наконец, » и оператор « с » предоставляют удобные способы сделать это.

Некоторые объекты содержат ссылки на другие объекты; они называются контейнерами . Примеры контейнеров: кортежи, списки и словари. Ссылки часть стоимости контейнера. В большинстве случаев, когда мы говорим о стоимости контейнер, мы подразумеваем значения, а не идентичности содержащихся в нем объектов; однако, когда мы говорим об изменчивости контейнера, только тождества подразумеваются непосредственно содержащиеся объекты.Итак, если неизменяемый контейнер (как кортеж) содержит ссылку на изменяемый объект, его значение изменяется, если этот изменяемый объект изменен.

Типы влияют практически на все аспекты поведения объекта. Даже важность В некотором смысле затрагивается идентичность объекта: для неизменяемых типов операции, вычисление новых значений может фактически вернуть ссылку на любой существующий объект с один и тот же тип и значение, а для изменяемых объектов это не допускается. Например., после a = 1; b = 1 , a и b могут или не могут относиться к одному и тому же объекту со значением один, в зависимости от реализации, но после c = []; d = [] , c и d гарантированно относятся к двум разным, уникальным, новым создал пустые списки.(Обратите внимание, что c = d = [] назначает один и тот же объект обоим c и d .)

3.2. Стандартная иерархия типов

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

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

Нет

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

NotImplemented

Этот тип имеет единственное значение. Это единственный объект с этим значением. это Доступ к объекту осуществляется через встроенное имя NotImplemented . Числовые методы и методы расширенного сравнения должны возвращать это значение, если они не реализуют операция для предоставленных операндов. (Затем переводчик попробует отраженная операция или какой-либо другой откат, в зависимости от оператора.) Это значение истины верно.

См. Выполнение арифметических операций Больше подробностей.

Ellipsis

Этот тип имеет одно значение. Это единственный объект с этим значением. это Доступ к объекту осуществляется через литерал ... или встроенное имя Эллипсис . Его истинность истинна.

чисел.Номер

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

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

числа. Интеграл

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

Есть два типа целых чисел:

Целые числа ( int )

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

Booleans ( bool )

Они представляют истинные значения False и True. Два объекта, представляющие значения False и True — единственные логические объекты. Логический тип — это подтип целочисленного типа, а логические значения ведут себя как значения 0 и 1, соответственно, почти во всех контекстах, за исключением того, что при преобразовании в строка, возвращаются строки «Ложь» или «Истина» соответственно.

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

числа. Реальные ( с плавающей запятой )

Они представляют числа с плавающей запятой двойной точности машинного уровня. Ты во власти базовой машинной архитектуры (и C или Java реализация) для допустимого диапазона и обработка переполнения. Python не поддержка чисел с плавающей запятой одинарной точности; экономия процессора и использование памяти, которое обычно является причиной их использования, затмевается накладные расходы на использование объектов в Python, поэтому нет причин усложнять язык с двумя видами чисел с плавающей запятой.

числа. Комплексный ( сложный )

Они представляют комплексные числа как пару машинного уровня двойной точности числа с плавающей запятой. Применяются те же предостережения, что и для чисел с плавающей запятой. Действительная и мнимая части комплексного числа z могут быть получены с помощью атрибуты только для чтения z.real и z.imag .

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

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

.