Разное

Код python: Примеры программ | Python 3 для начинающих и чайников

Содержание

10 практик кода, ускоряющих выполнение программ на Python

«Питон – медленный». Наверняка вы не раз сталкивались с этим утверждением, особенно от людей, пришедших в Python из C, C++ или Java. Во многих случаях это верно. Циклы или сортировка массивов, списков или словарей иногда действительно работают медленно. В конце концов, главная миссия Python – сделать программирование приятным и легким. Ради лаконичности и удобочитаемости пришлось отчасти принести в жертву производительность.

Тем не менее, в последние годы предпринято немало усилий для решения проблемы. Теперь мы можем эффективно обрабатывать большие наборы данных с помощью NumPy, SciPy, Pandas и numba, поскольку все эти библиотеки написаны на C/C++. Еще один интересный проект – PyPy ускоряет код Python в 4.4 раза по сравнению с CPython (оригинальная реализация Python).

Недостаток PyPy – нет поддержки некоторых популярных модулей, например, Matplotlib, SciPy.

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

Список встроенных функций Python 3

В Python много работающих очень быстро реализованных на C встроенных функций. Они покрывают большинство тривиальных вычислительных операций (abs, min, max, len, sum). Хороший разработчик должен их знать, чтобы в подходящем месте не изобретать неуклюжие велосипеды, а брать надёжное стандартное решение. Возьмём в качестве примеров встроенные функции set() и sum(). Сравним их работу с кастомными реализациями того же функционала.

Пример для set():

        import random
random.seed(666)
a_long_list = [random.randint(0, 50) for i in range(1000000)]

# 1. Кастомная реализация set
%%time
unique = []
for n in a_long_list:
  if n not in unique:
    unique.append(n)

# Вывод в консоли:
# CPU times: user 316 ms, sys: 1.36 ms, total: 317 ms
# Wall time: 317 ms

# 2. Встроенная функция set
%%time  
unique = list(set(a_long_list))

# Вывод в консоли:
# CPU times: user 8.74 ms, sys: 110 μs, total: 8.85 ms
# Wall time: 8.79 ms
    

Пример для sum():

        # 1. Кастомная реализация sum
%%time
sum_value = 0
for n in a_long_list:
  sum_value += n
print(sum_value)

# Вывод в консоли:
# 25023368
# CPU times: user 9.91 ms, sys: 2.2 ms, total: 101 ms
# Wall time: 100 ms

# 2. Встроенная функция sum
%%time
sum_value = sum(a_long_list)
print(sum_value)

# Вывод в консоли:
# 25023368
# CPU times: user 4.74 ms, sys: 277 μs, total: 5.02 ms
# Wall time: 4.79 ms
    

Стандартные варианты в 36 (set) и 20 (sum) раз быстрее, чем функции, написанные самим разработчиком.

Если нам просто нужен отсортированный список, при этом неважно, что будет с оригиналом, sort() будет работать немного быстрее, чем sorted(). Это справедливо для базовой сортировки:

        # 1. Дефолтная сортировка с использованием sorted()
%%time
sorted(a_long_list)

# Вывод в консоли:
# CPU times: user 12 ms, sys: 2.51 ms, total: 14.5 ms
# Wall time: 14.2 ms

# 2. Дефолтная сортировка с использованием sort()
%%time
a_long_list.sort()

# Вывод в консоли:
# CPU times: user 8.52 ms, sys: 82 μs, total: 8.6 ms
# Wall time: 10 ms
    

Справедливо и для сортировки с использованием ключа – параметра key, который определяет сортировочную функцию:

        # 1. Сортировка с ключом с использованием sorted()
%%time
str_list1 = "Although both functions can sort list, there are small differences".split()
result = sorted(str_list1, key=str.lower)
print(result)

# Вывод в консоли:
# ['Although', 'are', 'both', 'can', 'differences', 'functions', 'list,', 'small', 
'sort', 'there']
# CPU times: user 29 μs, sys: 0 ns, total: 29 μs
# Wall time: 32.9 μs

# 2. Сортировка с ключом с использованием sort()
%%time
str_list2 = "Although both functions can sort list, there are small differences".split()
str_list2.sort(key=str.lower)
print(str_list2)

# Вывод в консоли:
# ['Although', 'are', 'both', 'can', 'differences', 'functions', 'list,', 'small', 
'sort', 'there']
# CPU times: user 26 μs, sys: 0 ns, total: 26 μs
# Wall time: 29.8 μs

# 3. Сортировка с ключом (лямбда) с использованием sorted()
%%time
str_list1 = "Although both functions can sort list, there are small differences".split()
result = sorted(str_list1, key=lambda str: len(str))
print(result)

# Вывод в консоли:
# ['can', 'are', 'both', 'sort', 'list,', 'there', 'small', 'Although', 'functions', 'differences']
# CPU times: user 61 μs, sys: 3 μs, total: 64 μs
# Wall time: 59.8 μs

# 4. Сортировка с ключом (лямбда) с использованием sort()
%%time
str_list2 = "Although both functions can sort list, there are small differences".split()
str_list2.sort(key=lambda str: len(str))
print(str_list2)

# Вывод в консоли:
# ['can', 'are', 'both', 'sort', 'list,', 'there', 'small', 'Although', 'functions', 'differences']
# CPU times: user 36 μs, sys: 0 ns, total: 36 μs
# Wall time: 38.9 μs
    

Так происходит потому, что метод sort() изменяет список прямо на месте, в то время как sorted() создает новый отсортированный список, сохраняя исходный нетронутым. Другими словами, порядок значений внутри a_long_list фактически уже изменился.

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

        a_dict = {'A': 1, 'B': 3, 'C': 2, 'D': 4, 'E': 5}

# 1. Дефолтная сортировка по ключам
%%time
result = sorted(a_dict) 
print(result)

# Вывод в консоли:
# ['A', 'B', 'C', 'D', 'E']
# CPU times: user 4 μs, sys: 0 ns, total: 4 μs
# Wall time: 6.91 μs

# 2. Cортировка по значениям, результат в виде списка кортежей
%%time
result = sorted(a_dict.items(), key=lambda item: item[1]) 
print(result)

# Вывод в консоли:
# [('A', 1), ('C', 2), ('B', 3), ('D', 4), ('E', 5)]
# CPU times: user 7 μs, sys: 0 ns, total: 7 μs
# Wall time: 8.82 μs

# 3. Сортировка по значениям, результат в виде словаря
%%time
result = {key: value for key, value in sorted(a_dict.items(), key=lambda item: item[1])}
print(result)

# Вывод в консоли:
# {'A': 1, 'C': 2, 'B': 3, 'D': 4, 'E': 5}
# CPU times: user 8 μs, sys: 0 ns, total: 8 μs
# Wall time: 11.2 μs
    

Когда нужен пустой словарь или список, вместо dict() или list(), можно напрямую вызвать {} и [] (для пустого множества все еще нужна функция set()). Этот прием не обязательно ускорит ваш код, но сделает его более «pythonic».

        # 1. Создание пустого словаря с помощью dict()
%%time
sorted_dict1 = dict()
for key, value in sorted(a_dict.items(), key=lambda item:item[1]):
  sorted_dict1[key] = value

# Вывод в консоли:
# CPU times: user 10 μs, sys: 0 ns, total: 10 μs
# Wall time: 12.2 μs

# 2. Создание пустого словаря с помощью литерала словаря
%%time
sorted_dict2 = {}
for key, value in sorted(a_dict.items(), key=lambda item:item[1]):
  sorted_dict2[key] = value

# Вывод в консоли:
# CPU times: user 9 μs, sys: 0 ns, total: 9 μs
# Wall time: 11 μs

# 3. Создание пустого списка с помощью list()
%%time
list()

# Вывод в консоли:
# CPU times: user 3 μs, sys: 0 ns, total: 3 μs
# Wall time: 3.81 μs

# 4. Создание пустого списка с помощью литерала списка
%%time
[]

# Вывод в консоли:
# CPU times: user 2 μs, sys: 0 ns, total: 2 μs
# Wall time: 3.1 μs
    

4. Генераторы списков

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

Например, отберём все чётные числа из списка another_long_list:

        even_num = []
for number in another_long_list:
    if number % 2 == 0:
        even_num.append(number)

    

Но есть более лаконичный и элегантный способ сделать то же самое. Код цикла for можно сократить до одной-единственной строки с помощью генератора списка, выиграв при этом в скорости почти в два раза:

        import random
random.seed(666)
another_long_list = [random.randint(0,500) for i in range(1000000)]

# 1. Создание нового списка с помощью цикла for
%%time
even_num = []
for number in another_long_list:
  if number % 2 == 0:
    even_num.append(number)

# Вывод в консоли:
# CPU times: user 113 ms, sys: 3.55 ms, total: 117 ms
# Wall time: 117 ms

# 2. Создание нового списка с помощью генератора списка
%%time
even_num = [number for number in another_long_list if number % 2 == 0]

# Вывод в консоли:
# CPU times: user 56.6 ms, sys: 3.73 ms, total: 60.3 ms
# Wall time: 64.8 ms
    

Сочетая это правило с Правилом #3 (использование литералов), мы легко можем превратить список в словарь или множество, просто изменив скобки:

        a_dict = {'A': 1, 'B': 3, 'C': 2, 'D': 4, 'E': 5}

sorted_dict3 = {key: value for key, value 
  in sorted(a_dict.items(), key=lambda item: item[1])}
print(sorted_dict3)

# Вывод в консоли:
# {'A': 1, 'C': 2, 'B': 3, 'D': 4, 'E': 5}
    

Разберёмся в коде:

  • Выражение sorted(a_dict.items(), key=lambda item: item[1]) возвращает список кортежей [('A', 1), ('C', 2), ('B', 3), ('D', 4), ('E', 5)].
  • Далее мы распаковываем кортежи и присваиваем первый элемент каждого кортежа в переменную key, а второй – в переменную value.
  • Наконец, сохраняем каждую пару keyvalue в словаре.

Иногда при переборе списка нужны и значения, и их индексы. Чтобы вдвое ускорить код используйте enumerate() для превращения списка в пары индекс-значение:

        import random
random.seed(666)
a_short_list = [random.randint(0,500) for i in range(5)]

# 1. Получение индексов с помощью использования длины списка
%%time
for i in range(len(a_short_list)):
  print(f'number {i} is {a_short_list[i]}')

# Вывод в консоли:
# number 0 is 233
# number 1 is 462
# number 2 is 193
# number 3 is 222
# number 4 is 145
# CPU times: user 189 μs, sys: 123 μs, total: 312 μs
# Wall time: 214 μs

# 2. Получение индексов с помощью enumerate()
for i, number in enumerate(a_short_list):
  print(f'number {i} is {number}')

# Вывод в консоли:
# number 0 is 233
# number 1 is 462
# number 2 is 193
# number 3 is 222
# number 4 is 145
# CPU times: user 72 μs, sys: 15 μs, total: 87 μs
# Wall time: 90.1 μs


    

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

        list1 = ['a', 'b', 'c', 'd', 'e']
list2 = ['1', '2', '3', '4', '5']

pairs_list = [pair for pair in zip(list1, list2)]
print(pairs_list)

# Вывод в консоли:
[('a', '1'), ('b', '2'), ('c', '3'), ('d', '4'), ('e', '5')]
    

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

И наоборот, чтобы получить доступ к элементам каждого кортежа, мы можем распаковать список кортежей, добавив звездочку (*) и используя множественное присваивание:

        # 1. Распаковка списка кортежей с помощью zip()
%%time
letters1, numbers1 = zip(*pairs_list)
print(letters1, numbers1)

# Вывод в консоли:
('a', 'b', 'c', 'd', 'e') ('1', '2', '3', '4', '5')
# CPU times: user 5 μs, sys: 1e+03 ns, total: 6 μs
# Wall time: 6.91 μs

# 2. Распаковка списка кортежей простым перебором
letters2 = [pair[0] for pair in pairs_list]
numbers2 = [pair[1] for pair in pairs_list]
print(letters2, numbers2)

# Вывод в консоли:
['a', 'b', 'c', 'd', 'e'] ['1', '2', '3', '4', '5']
# CPU times: user 5 μs, sys: 1e+03 ns, total: 6 μs
# Wall time: 7.87 μs
    

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

        import random
random.seed(666)
another_long_list = [random.randint(0,500) for i in range(1000000)]

def check_membership(n):
    for element in another_long_list:
        if element == n:
            return True
    return False
    

Однако есть более характерный для Python способ сделать это – использовать оператор in:

        # 1. Проверка наличия значения в списке перебором элементов
%%time
check_membership(900)

# Вывод в консоль
# CPU times: user 29.7 ms, sys: 847 μs, total: 30.5 ms
# Wall time: 30.2 ms

# 2. Проверка наличия значения в списке с помощью in
900 in another_long_list

# Вывод в консоль
# CPU times: user 10.2 ms, sys: 79 μs, total: 10.3 ms
# Wall time: 10.3 ms
    

Повысить эффективность можно предварительным удалением из списка дубликатов с помощью set. Таким образом, мы сократим количество элементов для проверки. Кроме того, оператор in очень быстро работает с множествами.

        # Убираем дубликаты
check_list = set(another_long_list)

# Вывод в консоль
# CPU times: user 19.8 ms, sys: 204 μs, total: 20 ms
# Wall time: 20 ms

# Проверяем наличие значения в списке
900 in check_list

# Вывод в консоль
# CPU times: user 2 μs, sys: 0 ns, total: 2 μs
# Wall time: 5.25 μs
    

Преобразование списка в множество заняло 20 мс. Но это одноразовые затраты. Зато сама проверка заняла 5 мкс – то есть в 2 тыс. раз меньше, что становится важным при частых обращениях.

Практически в любой программе необходимо проверять, являются ли переменные/списки/словари/… пустыми. На этих проверках тоже можно немножко сэкономить.

Не следует явно указывать == True или is True в условии if, достаточно указать имя проверяемой переменной. Это экономит ресурсы, которые использует «магическая» функция __eq__ для сравнения значений.

        string_returned_from_function = 'Hello World'

# 1. Явная проверка на равенство
%%time
if string_returned_from_function == True:
  pass

# Вывод в консоль
# CPU times: user 3 μs, sys: 0 ns, total: 3 μs
# Wall time: 5.01 μs

# 2. Явная проверка с использованием оператора is
%%time
if string_returned_from_function is True:
  pass

# Вывод в консоль
# CPU times: user 2 μs, sys: 1 ns, total: 3 μs
# Wall time: 4.05 μs

# 3. Неявное равенство
%%time
if string_returned_from_function:
  pass

# Вывод в консоль
# CPU times: user 3 μs, sys: 0 ns, total: 3 μs
# Wall time: 4.05 μs
    

Аналогично можно проверять обратное условие, добавив оператор not:

        if not string_returned_from_function:
  pass
    

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

        %%time
num_counts = {}
for num in a_long_list:
    if num in num_counts:
        num_counts[num] += 1
    else:
        num_counts[num] = 1

# Вывод в консоль
# CPU times: user 448 ms, sys: 1.77 ms, total: 450 ms
# Wall time: 450 ms
    

Однако более эффективный способ для решения этой задачи – использование Counter() из модуля collections. Весь код при этом уместится в одной строчке:

        %%time
num_counts2 = Counter(a_long_list)

# Вывод в консоль
# CPU times: user 40.7 ms, sys: 329 μs, total: 41 ms
# Wall time: 41.2 ms
    

Этот фрагмент будет работать примерно в 10 раз быстрее, чем предыдущий.

У Counter также есть удобный метод most_common, позволяющий получить самые часто встречающиеся значения:

        for number, count in num_counts2.most_common(10):
  print(number, count)

# Вывод в консоль
29 19831
47 19811
7 19800
36 19794
14 19761
39 19748
32 19747
16 19737
34 19729
33 19729

    

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

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

        def compute_cubic1(number):
  return number**3

%%time
new_list_cubic1 = [compute_cubic1(number) for number in a_long_list]

# Вывод в консоль
# CPU times: user 335 ms, sys: 14.3 ms, total: 349 ms
# Wall time: 354 ms
    

Однако правильнее будет перевернуть конструкцию – и поместить цикл внутрь функции.

        def compute_cubic2():
  return [number**3 for number in a_long_list]

%%time
new_list_cubic2 = compute_cubic2()

# Вывод в консоль
# CPU times: user 261 ms, sys: 15.7 ms, total: 277 ms
# Wall time: 277 ms
    

В данном примере для миллиона итераций (длина a_long_list) мы сэкономили около 22% времени.

***

Будем рады, если вы поделитесь в комментариях своими подходами к ускорению кода в Python. Вот ещё несколько статей, которые могут вас заинтересовать:

Инструменты для анализа кода Python. Часть 1

Рассмотрим популярные инструменты для анализа кода Python и подробно расскажем об их специфике и основных принципах работы.

Автор: Валерий Шагур, teacher assistance на курсе Программирование на Python

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

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

Анализаторы и автоматическое форматирование кода

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

И первая, и вторая группы включают в себя как простые утилиты командной строки для решения узкоспециализированных задач (например, проверка docstring или сортировка импортов), так и богатые по возможностям библиотеки, объединяющие в себе более простые утилиты. Средства анализа кода из первой группы принято называть линтерами (linter). Название происходит от lint — статического анализатора для языка программирования Си и со временем ставшего нарицательным. Программы второй группы называют форматировщиками (formatter).

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

Список рассматриваемых инструментов для анализа кода Python

Часть 1

Часть 2

Соглашения принятые в статье и общие замечания

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

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

Установка всех программ в обзоре практически однотипна и сводится к использованию пакетного менеджера pip.

$ python3.6 -m pip install --upgrade <package_name>

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

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

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

Содержание файла example.py:

import os
import notexistmodule

def Function(num,num_two):
return num

class MyClass:
"""class MyClass """

def __init__(self,var):
self.var=var

def out(var):
print(var)


if __name__ == "__main__":
my_class = MyClass("var")
my_class.out("var")
notexistmodule.func(5)

В коде допущено несколько ошибок:

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

Руководства по стилям: для тех, кто впервые сталкивается с темой оформления кода, в качестве знакомства предлагаем прочитать официальные руководства по стилю для языка Python PEP8 и PEP257. В качестве примера внутрикорпоративных соглашений можно рассмотреть Google Python Style Guide — https://github.com/google/styleguide/blob/gh-pages/pyguide.md

Pycodestyle

Pycodestyle — простая консольная утилита для анализа кода Python, а именно для проверки кода на соответствие PEP8. Один из старейших анализаторов кода, до 2016 года носил название pep8, но был переименован по просьбе создателя языка Python Гвидо ван Россума.

Запустим проверку на нашем коде:

$ python3 -m pycodestyle example.py 
example.py:4:1: E302 expected 2 blank lines, found 1
example.py:4:17: E231 missing whitespace after ','
example.py:7:1: E302 expected 2 blank lines, found 1
example.py:10:22: E231 missing whitespace after ','
example.py:11:17: E225 missing whitespace around operator

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

<имя файла>: <номер строки> :<положение символа>: <код и короткая расшифровка ошибки>

Возможности программы по проверке соглашений ограничены: нет проверок на правильность именования, проверка документации сводится к проверки длины docstring. Тем не менее функционал программы нельзя назвать “спартанским”, он позволяет настроить необходимый уровень проверок и получить различную информацию о результатах анализа. Запуск с ключом —statistics -qq выводит статистику по ошибкам:

$ python3 -m pycodestyle --statistics -qq example.py 
1 E225 missing whitespace around operator
2 E231 missing whitespace after ','
2 E302 expected 2 blank lines, found 1

Более наглядный вывод можно получить при использовании ключа —show-source. После каждого сообщения об ошибке будет выведена строка исходного кода, в которой содержится ошибка.

$ python3 -m pycodestyle --show-source example.py 
example.py:4:1: E302 expected 2 blank lines, found 1
def Function(num,num_two):
^
example.py:4:17: E231 missing whitespace after ','
def Function(num,num_two):
^
example.py:7:1: E302 expected 2 blank lines, found 1
class MyClass:
^
example.py:10:22: E231 missing whitespace after ','
def __init__(self,var):
^
example.py:11:17: E225 missing whitespace around operator
self.var=var
^

Если есть необходимость посмотреть, какие из соглашений PEP8 были нарушены, используйте ключ — show-pep8. Программа выведет список всех проверок с выдержками из PEP8 для случаев нарушений. При обработке файлов внутри директорий предусмотрена возможность фильтрации по шаблону. Pycodestyle позволяет сохранять настройки поиска в конфигурационных файлах как глобально, так и на уровне проекта.

Pydocstyle

Утилиту pydocstyle мы уже упоминали в статье Работа с документацией в Python: поиск информации и соглашения. Pydocstyle проверяет наличие docstring у модулей, классов, функций и их соответствие официальному соглашению PEP257.

$ python3 -m pydocstyle example.py
example.py:1 at module level:
D100: Missing docstring in public module
example.py:4 in public function `Function`:
D103: Missing docstring in public function
example.py:7 in public class `MyClass`:
D400: First line should end with a period (not 's')
example.py:7 in public class `MyClass`:
D210: No whitespaces allowed surrounding docstring text
example.py:10 in public method `__init__`:
D107: Missing docstring in __init__
example.py:13 in public method `out`:
D102: Missing docstring in public method

Как мы видим из листинга, программа указала нам на отсутствие документации в определениях функции, методов класса и ошибки оформления в docstring класса. Вывод можно сделать более информативным, если использовать ключи —explain и —source при вызове программы. Функционал pydocstyle практически идентичен описанному выше для pycodestyle, различия касаются лишь названий ключей.

Pyflakes

В отличие от уже рассмотренных инструментов для анализа кода Python pyflakes не делает проверок стиля. Цель этого анализатора кода — поиск логических и синтаксических ошибок. Разработчики pyflakes сделали упор на скорость работы программы, безопасность и простоту. Несмотря на то, что данная утилита не импортирует проверяемый файл, она прекрасно справляется c поиском синтаксических ошибок и делает это быстро. С другой стороны, такой подход сильно сужает область проверок.
Функциональность pyflakes — “нулевая”, все что он умеет делать — это выводить результаты анализа в консоль:

$ python3 -m pyflakes example.py 
example.py:1: 'os' imported but unused

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

Pylint

До сих пор мы рассматривали утилиты, которые проводили проверки на наличие либо стилистических, либо логических ошибок. Следующий в обзоре статический инструмент для анализа кода Python — Pylint, который совместил в себе обе возможности. Этот мощный, гибко настраиваемый инструмент для анализа кода Python отличается большим количеством проверок и разнообразием отчетов. Это один из самых “придирчивых” и “многословных” анализаторов кода. Анализ нашего тестового скрипта выдает весьма обширный отчет, состоящий из списка найденных в ходе анализа недочетов, статистических отчетов, представленных в виде таблиц, и общей оценки кода:

$ python3.6 -m pylint --reports=y text example.py
************* Module text
/home/ququshka77/.local/lib/python3.6/site-packages/pylint/reporters/text.py:79:22: W0212: Access to a protected member _splitstrip of a client class (protected-access)
************* Module example
example.py:4:16: C0326: Exactly one space required after comma
def Function(num,num_two):
                           ^ (bad-whitespace)
example.py:10:21: C0326: Exactly one space required after comma
    def __init__(self,var):
                             ^ (bad-whitespace)
example.py:11:16: C0326: Exactly one space required around assignment
        self.var=var
                    ^ (bad-whitespace)
example.py:1:0: C0111: Missing module docstring (missing-docstring)
example.py:2:0: E0401: Unable to import 'notexistmodule' (import-error)
example.py:4:0: C0103: Function name "Function" doesn't conform to snake_case naming style (invalid-name)
example.py:4:0: C0111: Missing function docstring (missing-docstring)
example.py:4:17: W0613: Unused argument 'num_two' (unused-argument)
example.py:13:4: C0111: Missing method docstring (missing-docstring)
example.py:13:4: E0213: Method should have "self" as first argument (no-self-argument)
example.py:7:0: R0903: Too few public methods (1/2) (too-few-public-methods)
example.py:18:4: C0103: Constant name "my_class" doesn't conform to UPPER_CASE naming style (invalid-name)
example.py:19:4: E1121: Too many positional arguments for method call (too-many-function-args)
example.py:1:0: W0611: Unused import os (unused-import)

Report
======
112 statements analysed.

Statistics by type
+----------+----------+---------------+-------------+-------------------+---------------+
|type     |number      |old number        |difference      |%documented       |%badname |
+======+======+========+========+===========+========+
|module   |2           |2                 |=               |50.00             |0.00            |
+-----------+----------+---------------+-------------+-------------------+---------------+
|class    |5           |5                 |=               |100.00            |0.00            |
+-----------+----------+---------------+-------------+-------------------+---------------+
|method   |11          |11                |=               |90.91             |0.00            |
+-----------+----------+---------------+-------------+-------------------+---------------+
|function |4           |4                 |=               |75.00             |25.00          |
+-----------+----------+---------------+-------------+-------------------+---------------+

External dependencies
::
    pylint 
      \-interfaces (text)
      \-reporters (text)
      | \-ureports 
      |   \-text_writer (text)
      \-utils (text)


Raw metrics
+-------------+----------+-------+-----------+-------------+
|type        |number |%     |previous    |difference |
+=======+======+=====+=====+========+
|code        |128    |48.30 |128         |=               |
+-------------+----------+--------+-----------+------------+
|docstring   |84     |31.70 |84          |=               |
+-------------+----------+--------+-----------+------------+
|comment     |16     |6.04  |16          |=               |
+-------------+----------+--------+-----------+------------+
|empty       |37     |13.96 |37          |=               |
+-------------+----------+--------+-----------+------------+

Duplication
+-------------------------------+------+------------+-------------+
|                            |now      |previous      |difference |
+=================+=====+======+========+
|nb duplicated lines         |0        |0             |=              |
+-------------------------------+-------+------------+------------+
|percent duplicated lines    |0.000    |0.000         |=              |
+-------------------------------+-------+------------+------------+

Messages by category
+--------------+----------+-----------+-------------+
|type            |number |previous |difference |
+========+======+======+========+
|convention      |8       |8       |=               |
+--------------+----------+-----------+-------------+
|refactor        |1       |1       |=               |
+--------------+-----------+----------+-------------+
|warning         |3       |3       |=               |
+--------------+-----------+----------+-------------+
|error           |3       |3       |=               |
+--------------+-----------+----------+-------------+

% errors / warnings by module
+-----------+--------+-----------+----------+--------------+
|module   |error    |warning |refactor |convention   |
+======+=====+======+======+========+
|example  |100.00   |66.67   |100.00   |100.00       |
+-----------+---------+----------+-----------+-------------+
|text     |0.00     |33.33   |0.00     |0.00         |
+-----------+---------+----------+-----------+-------------+

Messages
+-----------------------------+----------------+
|message id                  |occurrences |
+=================+=========+
|missing-docstring           |3                 |
+-----------------------------+----------------+
|bad-whitespace              |3                 |
+------------------------------+---------------+
|invalid-name                |2                 |
+------------------------------+---------------+
|unused-import               |1                 |
+------------------------------+---------------+
|unused-argument             |1                 |
+------------------------------+---------------+
|too-many-function-args      |1                 | 
+------------------------------+---------------+
|too-few-public-methods      |1                 |
+------------------------------+---------------+
|protected-access            |1                 |
+------------------------------+---------------+
|no-self-argument            |1                 |
+------------------------------+---------------+
|import-error                |1                 |
+------------------------------+---------------+

------------------------------------------------------------------------------------------
Your code has been rated at 7.59/10 (previous run: 7.59/10, +0.00)

Программа имеет свою внутреннюю маркировку проблемных мест в коде:

[R]efactor — требуется рефакторинг,
[C]onvention — нарушено следование стилистике и соглашениям,
[W]arning — потенциальная ошибка,
[E]rror — ошибка,
[F]atal — ошибка, которая препятствует дальнейшей работе программы.

Для вывода подробного отчета мы использовали ключ командной строки —reports=y.
Более гибко настроить вывод команды позволяют разнообразные ключи командной строки. Настройки можно сохранять в файле настроек rcfile. Мы не будем приводить подробное описание ключей и настроек, для этого есть официальная документация — https://pylint.readthedocs.io/en/latest/index.html#, остановимся лишь на наиболее интересных, с нашей точки зрения, возможностях утилиты:

— Генерация файла настроек (—generate-rcfile). Позволяет не писать конфигурационный файл с нуля. В созданном rcfile содержатся все текущие настройки с подробными комментариями к ним, вам остается только отредактировать его под собственные требования.

— Отключение вывода в коде. При редактировании кода есть возможность вставить блокирующие вывод сообщений комментарии. Чтобы продемонстрировать это, в определение функции в файле примера example.py добавим строку:

# pylint: disable=unused-argument

и запустим pylint. Из результатов проверки “исчезло” сообщение:

example.py:4:17: W0613: Unused argument 'num_two' (unused-argument)

— Создание отчетов в формате json (—output-format=json). Полезно, если необходимо сохранение или дальнейшая обработка результатов работы линтера. Вы также можете создать собственный формат вывода данных.

— Параллельный запуск (-j 4). Запуск в нескольких параллельных потоках на многоядерных процессорах сокращает время проверки.

— Встроенная документация. Вызов программы с ключом —help-msg=<key> выведет справку по ключевому слову key. В качестве ключевого слова может быть код сообщения (например: E0401) или символическое имя сообщения (например: import-error). Ниже приведен листинг получения справки по ключу import-error:

$ python3.6 -m pylint --help-msg=import-error
:import-error (E0401): *Unable to import %s*
Used when pylint has been unable to import a module. This message belongs to
the imports checker.

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

— Плагины — отличная возможность изменять поведение pylint. Их применение может оказаться полезным в случаях, когда pylint неправильно обрабатывает код и есть “ложные” срабатывания, или когда требуется отличный от стандартного формат вывода результатов.

Vulture

Vulture — небольшая утилита для поиска “мертвого” кода в программах Python. Она использует модуль ast стандартной библиотеки и создает абстрактные синтаксические деревья для всех файлов исходного кода в проекте. Далее осуществляется поиск всех объектов, которые были определены, но не используются. Vulture полезно применять для очистки и нахождения ошибок в больших базовых кодах.

Продолжение следует

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

Еще статьи по Python

10 предпочтительных методов рефакторинга кода на Python / Хабр

Сделайте свой Python код читабельнее и производительнее

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

1. Включение (прим. пер.: абстракции) списков, словарей и множеств

Есть три типичных изменяемых (прим. пер.: мутабельных) контейнерных типа данных в Python: списки, словари и множества. Если у нас есть итерируемый объект, то мы можем воспользоваться циклом for, чтобы перебрать этот объект для создания на его основе нового списка. Однако характерный для Python способ – использовать включение списка, синтаксис которого выглядит следующим образом: [выражение for x in итерируемый_объект if условие]. Следует заметить, что часть с вычисляемым условием необязательна. Давайте посмотрим на следующий код:

>>> # Начнем с создания списка чисел
>>> numbers = list(range(5))
>>> print(numbers)
[0, 1, 2, 3, 4]
>>> 
>>> # Вместо этого:
>>> squares0 = []
>>> for number in numbers:
...     if number%2 == 0:
...         squares0.append(number*number)
... 
>>> print(squares0)
[0, 4, 16]
>>> 
>>> # Характерный для Python способ:
>>> squares1 = [x*x for x in numbers if x%2 == 0]
>>> print(squares1)
[0, 4, 16]

Включение списка

Помимо включения списка мы также можем использовать включения для словарей и множеств, что соответствует терминам «включение словаря» и «включение множества». Для включения словаря используется синтаксис: {выражение_ключа: выражение_значения for элемент in итерируемый_объект}, в то время как синтаксис включения множества – {выражение for элемент in итерируемый_объект}. Данный код демонстрирует их использование:

>>> # Включение словаря
>>> {x: x*x for x in range(5)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> 
>>> # Включение множества
>>> {x*x for x in range(5)}
{0, 1, 4, 9, 16}

Включения словаря и множества

2. F-строки

Строки – привычный примитивный тип данных, который мы используем почти во всех проектах. В большинстве случаев для отображения строковых данных нам нужно сделать дополнительный шаг — определенным образом отформатировать их. Можно выполнить форматирование в стиле языка C, что подразумевает использование символа %, либо воспользоваться методом format, которым обладают строки в Python.

Однако в одном из недавних релизов Python был представлен новый метод форматирования строк. Он известен как «f-строки», что означает «форматируемые строковые литералы» – лаконичный и читабельный способ форматирования строк. Давайте посмотрим на сравнение этих разных подходов к форматированию

>>> # Определим коэффициент конвертации
>>> usd_to_eur = 0.89
>>> 
>>> # Вместо этого:
>>> print('1 USD = {rate:.2f} EUR'.format(rate=usd_to_eur))
1 USD = 0.89 EUR
>>> print('1 USD = %.2f EUR' % usd_to_eur)
1 USD = 0.89 EUR
>>> 
>>> # Характерный для Python способ:
>>> print(f'1 USD = {usd_to_eur:.2f} EUR')
1 USD = 0.89 EUR

F-строки

Естественно, вышеприведенный код показывает только основы использования f-строк, которые в сущности реализуют почти все стили форматирования, поддерживаемые форматированием в стиле C или методом str.format. Можете прочитать больше о f-строках в официальной документации или в моей недавней статье.

3. Множественное присваивание и распаковка кортежей

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

>>> # Вместо этого:
>>> code = 404
>>> message = "Not Found"
>>> 
>>> # Характерный для Python способ:
>>> code, message = 404, "Not Found"

Множественное присваивание

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

>>> # Определяем и распаковываем кортеж
>>> http_response = (404, "Not Found")
>>> code, message = http_response
>>> print(f'code: {code}; message: {message}')
code: 404; message: Not Found

Распаковка кортежа

4. Распаковка с ловушками

В предыдущем разделе мы рассмотрели, как распаковать кортеж, используя наиболее базовый формат – количество переменных соответствовало количеству элементов кортежа. Однако, когда в кортеже есть несколько элементов, иногда может понадобиться распаковать, используя метод ловушек (прим. пер.: англ. catch-all method – под ловушкой подразумеваю переменную с префиксом-звездочкой, принимающую/захватывающую некоторое количество элементов контейнера при его распаковке, т. е. это не общепринятый термин, т. к. общепринятого не нашел, но это лучшее, что пришло на ум). А именно – все элементы, которые явно не обозначены переменными, будут захвачены переменной с префиксом-звездочкой. Для получения того же результата, нехарактерный для Python подход обычно подразумевает использование срезов, что чревато ошибками, если мы зададим некорректные индексы.

>>> # Определяем кортеж для распаковки
>>> numbers = tuple(range(8))
>>> numbers
(0, 1, 2, 3, 4, 5, 6, 7)
>>> 
>>> # Вместо этого:
>>> first_number0 = numbers[0]
>>> middle_numbers0 = numbers[1:-1]
>>> last_number0 = numbers[-1]
>>> 
>>> # Характерный для Python способ:
>>> first_number1, *middle_numbers1, last_number1 = numbers
>>> 
>>> # Проверяем результаты
>>> print(first_number0 == first_number1)
True
>>> print(middle_numbers0 == middle_numbers1)
False
>>> print(last_number0 == last_number1)
True

Распаковка с ловушками

Как вы могли заметить, значения middle_numbers0 и middle_numbers1 не равны. Это потому, что распаковка с ловушками (с использованием звездочки) по умолчанию генерирует объект списка. Таким образом, чтобы у распакованных данных на выходе был тот же тип данных, мы можем использовать конструктор кортежа, как показано ниже:

>>> # Конвертируем распакованный список в кортеж
>>> print(middle_numbers0 == tuple(middle_numbers1))
True

Создание кортежа из переменной-ловушки

5. Выражение присваивания

В выражении присваивания, более известном как «моржовое выражение», используется «оператор-морж» :=, который напоминает моржа с парой глаз и бивнями, не правда ли? Как понятно из его имени, выражение присваивания позволяет присваивать значение переменной и, в то же время, оно может быть использовано в качестве выражения, например таком, как условный оператор. Определение звучит запутанно, но давайте посмотрим на его использование в следующем фрагменте кода:

>>> # Определим некоторые вспомогательные функции
>>> def get_account(social_security_number):
...     pass
... 
>>> def withdraw_money(account_number):
...     pass
... 
>>> def found_no_account():
...     pass
... 
>>> # Вместо этого:
>>> account_number = get_account("123-45-6789")
>>> if account_number:
...     withdraw_money(account_number)
... else:
...     found_no_account()
... 
>>> # Характерный для Python способ:
>>> if account_number := get_account("123-45-6789"):
...     withdraw_money(account_number)
... else:
...     found_no_account()

Выражение присваивания

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

Некоторые бы стали утверждать, что экономия одной строки кода не так важна, но это делает более явным наше намерение – переменная account_number актуальна только в области видимости условного оператора. Если у вас есть опыт программирования на Swift, то использование выражения присваивания в условном операторе очень похоже на технику опционального связывания (прим. пер.: или опциональный биндинг), как показано ниже. По сути временная переменная accountNumber, если она содержит значение, используется только в идущей далее области видимости. Таким образом, вам следует познакомиться с выражением присваивания, и через некоторое время вы будете считать такой код более читабельным.

if let accountNumber = getAccount("123-45-6789") {
    withdrawMoney(accountNumber)
} else {
    foundNoAccount()
}

Опциональное связывание

6. Итерирование с использованием функции enumerate

Почти в каждом проекте нам неминуемо приходится заставлять нашу программу повторять определенные операции для всех элементов в списке, кортеже или каком-либо другом контейнерном типе данных. Добиться выполнения этих повторяющихся операций можно с помощью цикла for. Как правило, мы можем использовать его базовую форму: for элемент in итерируемый_объект. Однако, при таком итерировании, если нам нужен подсчет текущей итерации цикла, лучше воспользоваться функцией enumerate, которая может создать для нас счетчик. Более того, мы можем установить первоначальное значение счетчика.

>>> # Создаем список студентов для итерирования
>>> students = ['John', 'Jack', 'Jennifer', 'June']
>>> 
>>> # Вместо этого:
>>> for i in range(len(students)):
...     student = students[i]
...     print(f"# {i+1}: {student}")
... 
# 1: John
# 2: Jack
# 3: Jennifer
# 4: June
>>>
>>> # Характерный для Python способ:
>>> for i, student in enumerate(students, 1):
...     print(f"# {i}: {student}")
... 
# 1: John
# 2: Jack
# 3: Jennifer
# 4: June

Итерирование с помощью enumerate

7. Связывание итерируемых объектов с помощью zip/zip_longest

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

>>> # Создаем два списка объектов для использования функции zip
>>> students = ['John', 'Mike', 'Sam', 'David', 'Danny']
>>> grades = [95, 90, 98, 97]
>>> 
>>> # Вместо этого:
>>> grade_tracking0 = {}
>>> for i in range(min(len(students), len(grades))):
...     grade_tracking0[students[i]] = grades[i]
... 
>>> print(grade_tracking0)
{'John': 95, 'Mike': 90, 'Sam': 98, 'David': 97}
>>> 
>>> # Предпочтительный способ:
>>> grade_tracking1 = dict(zip(students, grades))
>>> print(grade_tracking1)
{'John': 95, 'Mike': 90, 'Sam': 98, 'David': 97}
>>>
>>> from itertools import zip_longest
>>> grade_tracking2 = dict(zip_longest(students, grades))
>>> print(grade_tracking2)
{'John': 95, 'Mike': 90, 'Sam': 98, 'David': 97, 'Danny': None}

Использование zip с итерируемыми объектами

Функция zip создает итератор – объект класса zip, каждый элемент которого представляет собой кортеж, состоящий из элементов итерируемых объектов, переданных в функцию. Еще стоит отметить, что по умолчанию функция zip остановится, когда будет в каком-либо итерируемом объекте закончатся элементы. В противоположность этому, функция zip_longest будет ориентироваться на наиболее длинный итерируемый объект.

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

>>> for student, grade in zip(students, grades):
...     print(f"{student}: {grade}")
... 
John: 95
Mike: 90
Sam: 98
David: 97

Итерирование с использованием функции zip

8. Конкатенация (прим. пер.: сцепление) итерируемых объектов

В примере выше мы использовали функцию zip для поэлементного объединения итерируемых объектов. Что делать, если возникла необходимость в конкатенации итерируемых объектов? Предположим, что нам нужно пройтись по двум итерируемым объектам одной и той же категории для выполнения одной и той же операции. Мы можем получить такую возможность, используя функцию chain. Давайте посмотрим на пример ее использования:

>>> # Определяем данные и вспомогательную функцию
>>> from itertools import chain
>>> group0 = ['Jack', 'John', 'David']
>>> group1 = ['Ann', 'Bill', 'Cathy']
>>> 
>>> def present_project(student):
...     pass
... 
>>> # Вместо этого:
>>> for group in [group0, group1]:
...     for student in group:
...         present_project(student)
... 
>>> for student in group0 + group1:
...     present_project(student)
... 
>>> # Характерный для Python способ:
>>> for student in chain(group0, group1):
...     present_project(student)

Сцепление итерируемых объектов

Как показано выше, нехарактерный для Python подход подразумевает необходимость создания дополнительных списков — не самый эффективный с точки зрения памяти путь. В отличие от этого, функция chain создает итерируемый объект из тех итерируемых, которые были предварительно определены. Более того, функция chain гибкая и может принимать итерируемые объекты любого типа: словари, множества, списки, объекты классов zip и map (посредством использования функции map), а также многие другие типы итерируемых объектов Python.

9. Тернарное выражение

Что если нам нужно присвоить что-либо переменной таким образом, чтобы на основе условия были присвоены разные значения? В этом случае мы можем использовать условный оператор, чтобы вычислить условие и определить, какое значение должно быть использовано для присваивания. Обычно это подразумевает несколько строк кода. Однако, чтобы справиться с этой работой, мы можем использовать тернарное выражение, которое занимает всего лишь одну строку кода и обладает следующим основным синтаксисом: var = значение_в_случае_true if условие else значение_в_случае_false. Давайте рассмотрим соответствующие примеры.

>>> # Определяем вспомогательную функцию
>>> from random import randint
>>> def got_even_number():
...     return randint(1, 10) % 2 == 0
... 
>>> # Вместо этого: 
>>> if got_even_number():
...     state = "Even"
... else:
...     state = "Odd"
... 
>>> state = "Odd"
>>> if got_even_number():
...     state = "Even"
... 
>>> # Характерный для Python способ:
>>> state = "Even" if got_even_number() else "Odd"

Тернарное выражение

10. Использование генераторов

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

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

>>> # Определяем вспомогательную функцию
>>> def process_data(row_data):
...     pass
... 
>>> # Вместо этого:
>>> with open('large_file') as file:
...     read_data = file.read().split("\n")
...     for row_data in read_data:
...         process_data(row_data)
...
>>> # Характерный для Python способ:
>>> with open('large_file') as file:
...     for row_data in file:
...         process_data(row_data)

Генераторы

Заключение

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

Спасибо за чтение этого материала, и удачного программирования.

Оптимизация кода на Python с помощью ctypes / Блог компании OTUS. Онлайн-образование / Хабр

Перевод статьи подготовлен специально для студентов курса «Разработчик Python».


Внимание: код в этой статье лицензирован под GNU AGPLv3.

Я написал это руководство, поскольку не смог найти такого, которое будет объединять в себе все полезное о ctypes. Надеюсь, эта статья сделает чью-то жизнь намного легче.

Содержание:

  1. Базовые оптимизации
  2. сtypes
  3. Компиляция под Python
  4. Структуры в Python
  5. Вызов вашего кода на С
  6. PyPy

Базовые оптимизации

Перед переписыванием исходного кода Python на С, рассмотрим базовые методы оптимизации в Python.

Встроенные структуры данных

Встроенные структуры данных в Python, такие как set и dict, написаны на С. Они работают гораздо быстрее, чем ваши собственные структуры данных, написанные как классы Python. Другие структуры данных, помимо стандартных set, dict, list и tuple описаны в документации модуля collections.

Списочные выражения

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

# Slow
      mapped = []
      for value in originallist:
          mapped.append(myfunc(value))
      
      # Faster
      mapped = [myfunc(value) in originallist]

ctypes

Модуль ctypes позволяет вам взаимодействовать с кодом на С из Python без использования модуля subprocess или другого подобного модуля для запуска других процессов из CLI.

Тут всего две части: компиляция кода на С для загрузки в качестве shared object и настройка структур данных в коде на Python для сопоставления их с типами С.

В этой статье я буду соединять свой код на Python с lcs.c, которая находит самую длинную подпоследовательность в двух списках строк. Я хочу, чтобы в Python работало следующее:

list1 = ['My', 'name', 'is', 'Sam', 'Stevens', '!']
      list2 = ['My', 'name', 'is', 'Alex', 'Stevens', '.']
      
      common = lcs(list1, list2)
      
      print(common)
      # ['My', 'name', 'is', 'Stevens']

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

Компиляция кода на С под Python

Сначала, исходный код на С (lcs.c) компилируется в lcs.so, чтобы загружаться в Python.

gcc -c -Wall -Werror -fpic -O3 lcs.c -o lcs.o
      gcc -shared -o lcs.so lcs.o

  • Wall отобразит все предупреждения;
  • Werror обернет все предупреждения в ошибки;
  • fpic сгенерирует независимые от положения инструкции, которые понадобятся, если вы захотите использовать эту библиотеку в Python;
  • O3 максимизирует оптимизацию;

А теперь мы начнем писать код на Python, используя полученный файл shared object.

Структуры в Python

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

struct Sequence
      {
          char **items;
          int length;
      };
      
      struct Cell
      {
          int index;
          int length;
          struct Cell *prev;
      };

А вот перевод этих структур на Python.

import ctypes
      class SEQUENCE(ctypes.Structure):
          _fields_ = [('items', ctypes.POINTER(ctypes.c_char_p)),
                      ('length', ctypes.c_int)]
      
      class CELL(ctypes.Structure):
          pass
      
      CELL._fields_ = [('index', ctypes.c_int), ('length', ctypes.c_int),
                       ('prev', ctypes.POINTER(CELL))]

Несколько заметок:

  • Все структуры – это классы, наследуемые от ctypes.Structure.
  • Единственное поле _fields_ представляет из себя список кортежей. Каждый кортеж это (<variable-name>, <ctypes.TYPE>).
  • В ctypes есть похожие типы c_char (char) и c_char_p (*char).
  • В ctypes также есть POINTER(), который создает указатель типа из каждого типа ему переданного.
  • Если у вас есть рекурсивное определение, как в CELL, вы должны передать начальное объявление, а затем добавить поля _fields_, чтобы позже получить ссылку на себя же.
  • Поскольку я не использовал CELL в своем коде на Python, мне не нужно было писать эту структуру, но у нее есть интересное свойство в рекурсивном поле.

Вызов вашего кода на С

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

def list_to_SEQUENCE(strlist: List[str]) -> SEQUENCE:
          bytelist = [bytes(s, 'utf-8') for s in strlist]
          arr = (ctypes.c_char_p * len(bytelist))()
          arr[:] = bytelist
          return SEQUENCE(arr, len(bytelist))
      
      
      def lcs(s1: List[str], s2: List[str]) -> List[str]:
          seq1 = list_to_SEQUENCE(s1)
          seq2 = list_to_SEQUENCE(s2)
      
          # struct Sequence *lcs(struct Sequence *s1, struct Sequence *s2)
          common = lcsmodule.lcs(ctypes.byref(seq1), ctypes.byref(seq2))[0]
      
          ret = []
      
          for i in range(common.length):
              ret.append(common.items[i].decode('utf-8'))
          lcsmodule.freeSequence(common)
      
          return ret
      
      lcsmodule = ctypes.cdll.LoadLibrary('lcsmodule/lcs.so')
      lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE)
      
      list1 = ['My', 'name', 'is', 'Sam', 'Stevens', '!']
      list2 = ['My', 'name', 'is', 'Alex', 'Stevens', '.']
      
      common = lcs(list1, list2)
      
      print(common)
      # ['My', 'name', 'is', 'Stevens']

Немного заметок:

  • **char (список строк) проводит сопоставление напрямую в список байт в Python.
  • В lcs.c есть функция lcs() с сигнатурой struct Sequence *lcs(struct Sequence *s1, struct Sequence *s2). Чтобы настроить возвращаемый тип, я использую lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE).
  • Чтобы сделать вызов с ссылкой на структуру Sequence, я использую ctypes.byref(), который возвращает «легкий указатель» на ваш объект (быстрее, чем ctypes.POINTER()).
  • common.items – это список байт, они могут быть декодированы для получения ret в виде списка str.
  • lcsmodule.freeSequence(common) просто освобождает память, ассоциированную с common. Это важно, поскольку сборчик мусора (AFAIK) его не соберет автоматически.

Оптимизированный код на Python – это код, который вы написали на С и обернули в Python.

Кое-что еще: PyPy

Внимание: Сам я никогда PyPy не пользовался.
Одна из самых простых оптимизаций заключается в запуске ваших программ в среде выполнения PyPy, которая содержит в себе JIT-компилятор (just-in-time), который ускоряет работу циклов, компилируя их в машинный код при многократном выполнении.

Если у вас появятся комментарии или вы захотите что-то обсудить, напишите мне ([email protected]).

На этом все. До встречи на курсе!

🐍 4 ошибки в коде на Python, которые выдают в вас новичка

Привет! Меня зовут Маша, я уже шесть лет занимаюсь коммерческой разработкой на Python, а ещё пишу задачи и объясняю теорию для студентов курса «Мидл Python-разработчик» от Яндекс.Практикума. По опыту знаю, что начинающий разработчик чаще всего хорошо знает синтаксис языка, но не до конца разбирается с тем, что у Python «под капотом».

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

1. Полагаетесь на изменяемые типы в значениях по умолчанию

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

        def pow(number, mod=2):
    pass

    

Или так:

        class Cat:
    legs = 4

    

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

В чём подвох? Значения по умолчанию работают правильно только с неизменяемыми объектами – строками, числами, frozen-объектами и boolean-типами. Если же вы укажете в качестве значения по умолчанию изменяемый объект, например, list, set или dict , то Python не будет ругаться, но преподнесёт вам неприятный сюрприз. Вот один из примеров: кота заводили дома, а он поселился ещё и в офисе:

        class House:
    cats = []

my_house = House()
office = House()

my_house.cats.append('Tom')

print(my_house.cats)  # ["Tom"]
print(office.cats)  # ["Tom"]

    

Чем объясняется проблема? Инструкции, объявляющие класс, выполнятся один раз. У всех экземпляров класса House будет ссылка на один и тот же массив – cats.

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

Как решить проблему? Привыкайте вместо значений по умолчанию указывать None:

        class House:
    cats: list = None
    
    def __init__(self):
        self.cats = []

my_house = House()
office = House()

my_house.cats.append('Tom')

print(my_house.cats)  # ["Tom"]
print(office.cats)  # []

    

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

2. Вызываете функцию в значении по умолчанию

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

        from datetime import datetime

def create_log_entry(user, action, time=datetime.now()):
    return f'{time}: {user} {action}'

    

После этого вы, спокойные и довольные собой, ушли на работу.

В чём подвох? Вернувшись домой, вы решили проверить, как записалось каждое событие, посмотреть актуальные даты и описания. Ожидание:

        create_log_entry('Алла', 'вышла из дома')
'2020-09-14 15:20:03.333333: Алла вышла из дома'
create_log_entry('Том', 'поймал мышь')
'2020-09-14 15:25:12.795328: Том поймал мышь'
create_log_entry('Адорианец', 'заварил кофе')
'2020-09-14 15:40:33.173500: Адорианец заварил кофе'
create_log_entry('Агент Кей', 'применил нейралайзер')
'2020-09-14 15:41:48.922357: Агент Кей применил нейралайзер'

    

Реальность – все события как будто произошли в одно и то же время:

        create_log_entry('Алла', 'вышла из дома')
'2020-09-14 15:20:00.333333: Алла вышла из дома'
create_log_entry('Том', 'поймал мышь')
'2020-09-14 15:20:00.333333: Том поймал мышь'
create_log_entry('Адорианец', 'заварил кофе')
'2020-09-14 15:20:00.333333: Адорианец заварил кофе'
create_log_entry('Агент Кей', 'применил нейралайзер')
'2020-09-14 15:20:00.333333: Агент Кей применил нейралайзер'

    

Чем объясняется проблема? Это произошло из-за того, что datetime.now сработал всего один раз – в тот момент, когда интерпретатор встретил объявление функции конструкцией def create_log_entry. Python запомнил, какая дата и время были на момент запуска программы, и постоянно использовал это значение.

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

        from datetime import datetime

def create_log_entry(user, action, time=None):
    time = datetime.now() if time is None else time
    return f'{time}: {user} {action}'

    

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

3. Используете одновременно int и bool как ключи dict

Предположим, вы решили написать простой переводчик с компьютерного языка на человеческий для своего умного дома. Вам нужно, чтобы True отображалось как «Правда», False – как «Ложь», а 1 и 0 переводились как «Есть» и «Нет». Зафиксируем все переводы в словаре:

        vocabulary = {
    True: "Правда", 
    False: "Ложь",
    1: "Есть",
    0: "Нет"
}

    

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

        print(vocabulary[True])     # 'Есть'
print(vocabulary[False])    # 'Нет'
print(vocabulary[1])        # 'Есть'
print(vocabulary[0])        # 'Нет'

    

Кажется, что-то пошло не так. Давайте заглянем в сам словарь:

        print(vocabulary)           # {True: 'Есть', False: 'Нет'}

    

Из него пропали два варианта перевода, а те, что остались – неверные.

Чем объясняется проблема? Чтобы разобраться в произошедшем, нужно понимать две вещи: что такое класс bool и как работает словарь.

  1. Класс bool, добавленный в Python 2.3, реализован как наследник класса int. То есть глобальные объекты True и False – всего лишь два экземпляра класса bool, представляющие собой 1 и 0. В этом классе переопределены методы __repr__ и __str__, которые отвечают за отображение экземпляра, но «под капотом» они остаются простыми цифрами. Это можно проверить, сравнив True и число. Зная это, вы можете использовать boolean-переменные в математических выражениях. Но я так поступать не рекомендую: как сказано в дзене Python (вы можете прочитать его, введя в интерпретатор import this), «читаемость имеет значение». Подробнее о реализации boolean можно прочитать в PEP-0285.
  2. Также внутри словаря находится hash-таблица: то есть все новые ключи, которые добавляются в словарь, проходят через hash-функцию, и именно она определяет, где расположить элемент в памяти. Таким образом, поиск и вставка данных становятся намного быстрее, чем в обычном массиве. Если хочется узнать больше подробностей о работе словарей в Python, рекомендую заглянуть на stackowerflow.

Как решить проблему? Для корректной реализации переводчика следует привести все ключи к одному типу данных – str.

        vocabulary = {
    "True": "Правда", 
    "False": "Ложь",
    "1": "Есть",
    "0": "Нет"
}

    

Hash-функции ключей перестанут совпадать, и ответ словаря будет таким, как мы хотели, – общий язык с умным домом всё-таки будет найден:

        vocabulary[str(True)]   # "Правда"

    

4. Используете set для ускорения вычислений

Среди разработчиков бытует распространённое мнение, что поиск элемента в set работает быстрее, чем в list. Поэтому нередко можно встретить следующий вариант кода:

        animals = ['cat', 'dog', 'bird', 'mouse', 'rat', 'elephant']

# <какой-то код, дополняющий или модифицирующий список>
if 'dog' in set(animals):
    # <дальнейшие вычисления>

    

В чём подвох? Рассмотрим конструкцию с точки зрения интерпретатора:

        animals = ['cat', 'dog', 'bird', 'mouse', 'rat', 'elephant']

animals_set = set(animals)  
# Нужно пройтись по всем элементам list и добавить каждый из них в set (cложность: O(n))
if 'dog' in animals_set:  # Нужно найти элемент во множестве O(1)
    # <дальнейшие вычисления>

    

Без оптимизации интерпретатор остановил бы поиск на втором элементе, но код заставил его сначала пройтись по всему списку, а потом выполнить дополнительное действие с set. В итоге вместо двух шагов получилось семь – никакого ускорения, только дополнительные расходы на память!

Чем объясняется проблема? Прежде всего – разной природой list и set. При объявлении типа list резервируется участок памяти, в котором будут храниться ссылки на другие данные в памяти. Список может хранить ссылки на любые объекты: строки, числа, другие массивы и даже на самого себя. Все объекты в списке хранятся последовательно.

Чтобы найти нужный элемент, интерпретатор последовательно идёт по ссылкам, начиная с первой, и сравнивает объект с искомым: найдя нужные данные, он останавливает поиск. Чем длиннее список, тем больше времени занимает процесс. В O-нотации это записывается как O(n).

set, так же, как и list, хранит элементы, но работает принципиально иначе. Во-первых, он содержит в себе только уникальные элементы, во-вторых, в нём нельзя хранить изменяемые структуры, и, наконец, в-третьих, данные будут размещены не в заданном вами порядке, а в наиболее удобном для Python.

Так как расположение в множестве определяется содержимым элемента, поиск по set и правда работает гораздо быстрее. Выполняя команду x in set_y, интерпретатору нужно взять hash-функцию от x и посмотреть, есть ли в set_y данные по полученному адресу. Никакого последовательного просмотра элементов и нудного сравнения!

O-нотация называет такую сложность O(1): вне зависимости от размеров множества поиск будет происходить за одинаковое количество времени.

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

        animals = ['cat', 'dog', 'bird', 'mouse', 'rat', 'elephant']

if 'dog' in animals: 
    # <дальнейшие вычисления>

    

Как говорится в дзене Python, «простое лучше сложного».

Советы для новичков в Python

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

Чтобы не оказаться тем самым новичком, у которого ничего не работает, я советую:

  1. Прочувствовать на себе дзен Python. Мало прочитать, что «простое лучше, чем сложное»: важно применять этот принцип на практике и не создавать себе дополнительных трудностей.
  2. Зрить в корень. Про типы, классы, структуры данных и операции с ними рассказывают на первых уроках по программированию. Ваша задача – выяснить не только «для чего они используются» и «что могут», но и «как они работают».
  3. Не соблазняться фрилансом. В начале пути вам точно стоит поработать в компаниях с высокой инженерной культурой. Так вы сможете перенимать опыт от людей, которые умеют и любят писать хороший код, а не набивать шишки самостоятельно.

Анализ кода в Python

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

Начнем с pylint

Пакет pylint не входит в Python, так что вам нужно будет посетить PyPI (Python Package Index), или непосредственно сайт пакета для загрузки. Вы можете использовать следующую команду, которая сделает всю работу за вас:

Если все идет по плану, то pylint установится, и мы сможем пойти дальше.

Анализ вашего кода

После установки pylint вы можете запустить его в командной строке, без каких либо аргументов, что бы увидеть, какие опции он принимает. Если это не сработало, можете прописать полный путь, вот так:

c:\Python34\Scripts\pylint



c:\Python34\Scripts\pylint

Теперь нам нужен какой-нибудь код для анализа. Вот часть кода, которая содержит четыре ошибки. Сохраните её в файле под названием crummy_code.py:

import sys

class CarClass:
«»»»»»

def __init__(self, color, make, model, year):
«»»Constructor»»»
self.color = color
self.make = make
self.model = model
self.year = year

if «Windows» in platform.platform():
print(«You’re using Windows!»)

self.weight = self.getWeight(1, 2, 3)

def getWeight(this):
«»»»»»
return «2000 lbs»


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import sys

 

class CarClass:

    «»»»»»

    

    def __init__(self, color, make, model, year):

        «»»Constructor»»»

        self.color = color

        self.make = make

        self.model = model

        self.year = year

    

        if «Windows» in platform.platform():

            print(«You’re using Windows!»)

        

        self.weight = self.getWeight(1, 2, 3)

 

    def getWeight(this):

        «»»»»»

        return «2000 lbs»

Можете увидеть ошибки не запуская код? Давайте посмотрим, может ли pylint найти их!

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!


После запуска этой команды вы увидите большую выдачу на вашем экране. Вот частичный пример:

c:\py101>c:\Python34\Scripts\pylint crummy_code.py

No config file found, using default configuration
************* Module crummy_code
C: 2, 0: Trailing whitespace (trailing-whitespace)
C: 5, 0: Trailing whitespace (trailing-whitespace)
C: 12, 0: Trailing whitespace (trailing-whitespace)
C: 15, 0: Trailing whitespace (trailing-whitespace)
C: 17, 0: Trailing whitespace (trailing-whitespace)
C: 1, 0: Missing module docstring (missing-docstring)
C: 3, 0: Empty class docstring (empty-docstring)
C: 3, 0: Old-style class defined. (old-style-class)
E: 13,24: Undefined variable ‘platform’ (undefined-variable)
E: 16,36: Too many positional arguments for function call (too-many-function-args)
C: 18, 4: Invalid method name «getWeight» (invalid-name)
C: 18, 4: Empty method docstring (empty-docstring)
E: 18, 4: Method should have «self» as first argument (no-self-argument)
R: 18, 4: Method could be a function (no-self-use)
R: 3, 0: Too few public methods (1/2) (too-few-public-methods)
W: 1, 0: Unused import sys (unused-import)


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

c:\py101>c:\Python34\Scripts\pylint crummy_code.py

 

No config file found, using default configuration

************* Module crummy_code

C: 2, 0: Trailing whitespace (trailing-whitespace)

C: 5, 0: Trailing whitespace (trailing-whitespace)

C: 12, 0: Trailing whitespace (trailing-whitespace)

C: 15, 0: Trailing whitespace (trailing-whitespace)

C: 17, 0: Trailing whitespace (trailing-whitespace)

C: 1, 0: Missing module docstring (missing-docstring)

C: 3, 0: Empty class docstring (empty-docstring)

C: 3, 0: Old-style class defined. (old-style-class)

E: 13,24: Undefined variable ‘platform’ (undefined-variable)

E: 16,36: Too many positional arguments for function call (too-many-function-args)

C: 18, 4: Invalid method name «getWeight» (invalid-name)

C: 18, 4: Empty method docstring (empty-docstring)

E: 18, 4: Method should have «self» as first argument (no-self-argument)

R: 18, 4: Method could be a function (no-self-use)

R: 3, 0: Too few public methods (1/2) (too-few-public-methods)

W: 1, 0: Unused import sys (unused-import)

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

  • С – конвенция (convention)
  • R – рефакторинг (refactor)
  • W – предупреждение (warning)
  • E – ошибка (error)

Наш pylint нашел 3 ошибки, 4 проблемы с конвенцией, 2 строки, которые нуждаются в рефакторинге и одно предупреждение. Предупреждение и 3 ошибки – это как раз то, что я искал. Мы попытаемся исправить этот код и устранить ряд проблем. Для начала мы наведем порядок в импортах, и изменить функцию getWeight на get_weight, в связи с тем, что camelCase не используется в названиях методов. Нам также нужно исправить вызов get_weight, чтобы он передавал правильное количество аргументов и исправить его, чтобы “self” выступал в качестве первого аргумента. Взглянем на новый код:

# crummy_code_fixed.py
import platform

class CarClass:
«»»»»»

def __init__(self, color, make, model, year):
«»»Constructor»»»
self.color = color
self.make = make
self.model = model
self.year = year

if «Windows» in platform.platform():
print(«You’re using Windows!»)

self.weight = self.get_weight(3)

def get_weight(self, this):
«»»»»»
return «2000 lbs»


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# crummy_code_fixed.py

import platform

 

class CarClass:

    «»»»»»

    

    def __init__(self, color, make, model, year):

        «»»Constructor»»»

        self.color = color

        self.make = make

        self.model = model

        self.year = year

    

        if «Windows» in platform.platform():

            print(«You’re using Windows!»)

        

        self.weight = self.get_weight(3)

 

    def get_weight(self, this):

        «»»»»»

        return «2000 lbs»

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

c:\py101>c:\Python34\Scripts\pylint crummy_code_fixed.py

No config file found, using default configuration
************* Module crummy_code_fixed
C: 1,0: Missing docstring
C: 4,0: CarClass: Empty docstring
C: 21,4: CarClass.get_weight: Empty docstring
W: 21,25: CarClass.get_weight: Unused argument ‘this’
R: 21,4: CarClass.get_weight: Method could be a function
R: 4,0: CarClass: Too few public methods (1/2)



c:\py101>c:\Python34\Scripts\pylint crummy_code_fixed.py

 

No config file found, using default configuration

************* Module crummy_code_fixed

C: 1,0: Missing docstring

C: 4,0: CarClass: Empty docstring

C: 21,4: CarClass.get_weight: Empty docstring

W: 21,25: CarClass.get_weight: Unused argument ‘this’

R: 21,4: CarClass.get_weight: Method could be a function

R: 4,0: CarClass: Too few public methods (1/2)

Как мы видим, это очень помогло. Если мы добавим docstrings, мы можем снизить количество ошибок вдвое. Теперь мы готовы перейти к pyflakes!

Работаем с pyflakes

Проект pyflakes это часть чего-то, что называется Divmod Project. Pyflakes на самом деле не выполняет проверяемый код также, как и pylint. Вы можете установить pyflakes при помощи pip, easy_install, или из другого источника.

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

Мы начнем с запуска pyflakes в изначальной версии той же части кода, которую мы использовали для проверки pylint. Вот и он:

import sys

class CarClass:
«»»»»»

def __init__(self, color, make, model, year):
«»»Constructor»»»
self.color = color
self.make = make
self.model = model
self.year = year

if «Windows» in platform.platform():
print(«You’re using Windows!»)

self.weight = self.getWeight(1, 2, 3)

def getWeight(this):
«»»»»»
return «2000 lbs»


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import sys

 

class CarClass:

    «»»»»»

    

    def __init__(self, color, make, model, year):

        «»»Constructor»»»

        self.color = color

        self.make = make

        self.model = model

        self.year = year

    

        if «Windows» in platform.platform():

            print(«You’re using Windows!»)

        

        self.weight = self.getWeight(1, 2, 3)

 

    def getWeight(this):

        «»»»»»

        return «2000 lbs»

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

c:\py101>c:\Python34\Scripts\pyflakes.exe crummy_code.py
crummy_code.py:1: ‘sys’ imported but unused
crummy_code.py:13: undefined name ‘platform’



c:\py101>c:\Python34\Scripts\pyflakes.exe crummy_code.py

crummy_code.py:1: ‘sys’ imported but unused

crummy_code.py:13: undefined name ‘platform’

Несмотря на суперски быструю скорость возврата выдачи, pyflakes не нашел все ошибки. Вызов метода getWeight передает слишком много аргументов, также метод getWeight сам по себе определен некорректно, так как у него нет аргумента self. Что-же, вы, собственно, можете называть первый аргумент так, как вам угодно, но в конвенции он всегда называется self. Если вы исправили код, оперируя тем, что вам сказал pyflakes, код не заработает, несмотря на это.

Подведем итоги

Следующим шагом должна быть попытка запуска pylint и pyflakes в вашем собственном коде, либо же в пакете Python, вроде SQLAlchemy, после чего следует изучить полученные в выдаче данные. Вы можете многое узнать о своем коде, используя данные инструменты. pylint интегрирован с Wingware, Editra, и PyDev. Некоторые предупреждения pylint могут показаться вам раздражительными, или не особо уместными. Существует несколько способов избавиться от таких моментов, как предупреждения об устаревании, через опции командной строки. Вы также можете использовать -generate-rcfile для создания примера файла config, который поможет вам контролировать работу pylint. Обратите внимание на то, что pylint и pyflakes не импортируют ваш код, так что вам не нужно беспокоиться о нежелательных побочных эффектах.

Генерация кода на Python при помощи Hy / Хабр

1. Что такое Hy

Hy — диалект Лиспа, который встроен в питон.

Благодаря тому, что Hy трансформирует свой Лиспоподобный код в Абстрактное Синтаксическое Дерево (AST) питона, с помощью Hy весь прекрасный мир питона — на кончиках пальцев и в форме Лиспа.

2. О синтаксисе Hy, очень кратко

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

  1. Отступ не играет роли. Вместо этого — уровни вложенности в выражения из круглых скобочек.
  2. Во всех вызовах функций название функции попадает в скобки со списком аргументов на первое место; запятые в списке аргументов не используются.
  3. Все операторы записываются так, как будто они — функции.
  4. Двоеточия не используются.
  5. Литералы для строк и словарей работают как и раньше; строки записываются в двойных кавычках, кортежи выглядят как вызов функции «,».

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

3. Терминологические замечания

Следует отдельно оговорить используемую терминологию. Основные термины на английском — quoting, unquoting, quaziquoting, splicing, macro expansion. В переводе книги Practical Common Lisp на русский язык для них используются слова «цитирование», «расцитирование», «квазицитирование» — и для последнего из них — «раскрытие макросов». Я не считаю этот вариант перевода удобным.

В данном материале будут использованы в качестве переводов «скрытие» для quoting, «раскрытие» для unquoting, «квазискрытие» для quaziquoting, «структурное раскрытие» для splicing, «расширение макроса» для macro expansion.

В приведённых далее примерах кода, можно увидеть синтаксис этих операций:

  • ' :: скрытие; применяется к последующей форме Hy; вместо её выполнения она будет обработанакак как данные.
  • ` :: квазискрытие; более сложная форма скрытия, позволяющая строить более сложные синтаксические структуры.
  • ~ :: раскрытие; так как , занята в питоне для конструктора кортежей, используемый символ отличается от традиционной для Лиспа запятой. Употребляется в квазискрытой форме и помещает в неё результат выполнения следующей за ней формы.
  • ~@ :: структурное раскрытие; работает аналогично предыдущей операции со следующим различием: результат оценки формы должен быть списком, и его элементы помещаются в объемлющую квазискрытую форму.

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

4. Суть метода

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

(macroexpand '(my-macro param1 param2 (do (print "hello!"))))

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

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

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

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

(with [fd (open "some/python/file.py" "a")]
      (.write fd "\n")
      (.write fd (disassemble code True)))

5. Генерация имён

При генерации кода на питоне, в отличие, например, от написания макросов, для нас является важным, какие названия носят новые символы, т.е. в случае питона — имена вновь сгенерированных функций, классов, переменных. Другими словами, стандартный способ в Лиспе ((gensym)) нам не подходит. Также в hy нет стандартного для многих лиспов (intern), служащего для превращения произвольной строки (с поправкой на ограничения по грамматике) в символ.

К счастью, вся база кода hy доступна, и быстрым поиском мы убеждаемся, что (gensym) работает, создавая объекты HySymbol. Так же можем поступить и мы.

Следующий пример, несмотря на сказанное ранее — макрос.

(defmacro make-vars [data]
  (setv res '())
  (for [element data]
    (setv varname (HySymbol (+ "var" (str element))))
    (setv res (cons `(setv ~varname 0) res)))
  `(do ~@res))

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

6. Пример и замечания

При использовании hy для кодогенерации (в отличие от просто работы на нём), всплывают некоторые аспекты, которые при отправке кода на выполнение оказываются скрытыми.

В первую очередь это касается того, что в контексте AST и контексте выполнения одни и те же выражения обозначают разные вещи.

  • [ ] не просто список питона, а HyList;
  • { } открывает не словарь питона, а HyDict, и в внутренней модели hy представлен как список;
  • "" не просто строковая переменная, а HyString.

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

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

(setv class-def [`(defclass ~class-name [~(HySymbol (. meta-base __name__))]
                    [army_name ~army-name
                     faction_base ~(HyString faction)
                     alternate_factions [~@(map HyString alternate-fac-list)]
                     army_id ~army-id
                     army-factions [~@(map HyString army-factions)]]
                    (defn --init-- [self &optional [parent None]]
                      (apply .--init-- [(super ~class-name self)]
                             {~@(interleave (map HyString class-grouping)
                                            (repeat 'True))
                              "parent" parent})
                      ~@(map (fn [key]
                               `(.add-classes (. self ~(HySymbol key))
                                              [~@(genexpr (HySymbol (. ut __name__))
                                                          [ut (get class-grouping key)])]))
                             class-grouping)))]))))

В приведённом примере производится заполнение списков в полях alternate_factions и army-factions объявляемого класса. Отметим, что в питоновском коде оба этих поля будут через нижнее подчёркивание. Заполнение производится на основе списков строк, поэтому применяется структурное раскрытие результата преобразования находящихся в переменных строк python в HyString.

Из приведённого фрагмента кода на hy можно сгенерировать следующий фрагмент кода на питоне:

class DetachPatrol_adeptus_ministorum(DetachPatrol):
    army_name = u'Adeptus Ministorum (Patrol detachment)'
    faction_base = u'ADEPTUS MINISTORUM'
    alternate_factions = []
    army_id = u'patrol_adeptus_ministorum'
    army_factions = [u'IMPERIUM', u'ADEPTA SORORITAS', u'<ORDER>', u'ADEPTUS MINISTORUM']

    def __init__(self, parent=None):
        super(DetachPatrol_adeptus_ministorum, self).__init__(*[], **{u'heavy': True, u'troops': True, u'transports': True, u'hq': True, u'fast': True, u'elite': True, u'parent': parent, })
        self.heavy.add_classes([Exorcist, Retributors, PenitentEngines])
        self.troops.add_classes([BattleSisters])
        self.transports.add_classes([ASRhino, Immolator])
        self.hq.add_classes([Celestine, Canoness, Jacobus])
        self.fast.add_classes([Dominions, Seraphims])
        self.elite.add_classes([ArcoFlagellants, Assassins, Celestians, Dialogus, Hospitaller, Imagifier, Mistress, Priest, Repentia, PriestV2, Crusaders])
        return None

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

  • Для функций из класса (которые начинаются на .), apply трактует первый позиционный аргумент, ему предоставленный (первый элемент списка, являющегося его вторым параметром) как объект, метод которого вызывается;
  • Можно производить заполнение словаря именованных аргументов при помощи структурного раскрытия;
  • Для сопоставления каждому ключу (строке, преобразованной в HyString) значения, применяется interleave, которое производит итерацию по двум спискам, перемежая их элементы;
  • Символ True подверженный скрытию, в коде python будет преобразован в себя;
  • В скрытой конструкции можно использовать нигде не объявленные (свободные) символы, которые будут преобразованы в переменные с такими же именами. Отметим; хоть в скрытой конструкции и находится объявление символа parent как параметра метода класса, во время выполнения функции, возвращающей скрытую кодовую конструкцию, такого символа не существует;
  • Можно генерировать серии однотипных операций из списков, производя структурное раскрытие списка скрытых конструкций hy (полученных преобразованием из исходного списка).

7. Использованные материалы

При написании данной статьи были использованы материалы из документации Hy и русского перевода Practical Common Lisp.

Python в Visual Studio Code

Работать с Python в Visual Studio Code с использованием расширения Microsoft Python просто, весело и продуктивно. Расширение делает VS Code отличным редактором Python и работает в любой операционной системе с различными интерпретаторами Python. Он использует всю мощь VS Code для обеспечения автозаполнения и IntelliSense, линтинга, отладки и модульного тестирования, а также возможности легко переключаться между средами Python, включая виртуальные среды и среды conda.

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

Учебное пособие Python Hello World

Установите Python и расширение Python

Учебник проведет вас через установку Python и использование расширения. Вы должны самостоятельно установить интерпретатор Python отдельно от расширения. Для быстрой установки используйте Python 3.7 из python.org и установите расширение из VS Code Marketplace.

После установки версии Python активируйте ее с помощью команды Python: Select Interpreter . Если VS Code не находит автоматически искомый интерпретатор, обратитесь к разделу «Среды — указать интерпретатор вручную».

Вы можете настроить расширение Python через настройки. См. Справку по настройкам.

Программа инсайдеров

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

Если вы хотите принять участие в программе, вы можете либо открыть палитру команд (⇧⌘P (Windows, Linux Ctrl + Shift + P)) и выбрать Python: Switch to Insiders Daily / Weekly Channel , либо вы можно открыть настройки (⌘, (Windows, Linux Ctrl +,)) и найти Python: Insiders Channel , чтобы установить для канала значение «ежедневно» или «еженедельно».

Запустить код Python

Чтобы испытать Python, создайте файл (используя проводник) с именем hello.py и вставьте следующий код (при условии Python 3):

  печать («Hello World»)  

Затем расширение Python предоставляет ярлыки для запуска кода Python в текущем выбранном интерпретаторе ( Python: выберите Interpreter в палитре команд):

  • В текстовом редакторе: щелкните правой кнопкой мыши в любом месте редактора и выберите Запустить файл Python в Терминале .При вызове для выборки выполняется только эта выборка.
  • В проводнике: щелкните правой кнопкой мыши файл Python и выберите Запустить файл Python в терминале .

Вы также можете использовать команду Terminal: Create New Integrated Terminal , чтобы создать терминал, в котором VS Code автоматически активирует текущий выбранный интерпретатор. См. «Среды» ниже. Python: Start REPL активирует терминал с текущим выбранным интерпретатором, а затем запускает Python REPL.

Более подробные инструкции по запуску кода см. В руководстве.

Автозаполнение и IntelliSense

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

IntelliSense быстро отображает методы, члены класса и документацию по мере ввода, и вы можете запускать завершение в любое время с помощью ⌃Space (Windows, Linux Ctrl + Space).Вы также можете навести указатель мыши на идентификаторы, чтобы узнать о них подробнее.

Совет : ознакомьтесь с расширением IntelliCode для VS Code (предварительная версия). IntelliCode предоставляет набор возможностей с использованием искусственного интеллекта для IntelliSense в Python, таких как определение наиболее релевантных автозавершений на основе текущего контекста кода.

Линтинг

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

Расширение Python может применять ряд различных линтеров, включая Pylint, pycodestyle, Flake8, mypy, pydocstyle, prospector и pylama. См. Линтинг.

Отладка

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

Для подробностей, связанных с Python, включая настройку запуска .json и удаленную отладку, см. Отладка. Общая отладочная информация VS Code находится в отладочном документе. Учебники Django и Flask также демонстрируют отладку в контексте этих веб-приложений, включая отладку шаблонов страниц Django.

Фрагменты

Snippets выводят производительность на новый уровень. Вы можете настроить свои собственные фрагменты и использовать фрагменты, предоставляемые расширением. Фрагменты отображаются так же, как автозавершение кода ⌃ Пробел (Windows, Linux Ctrl + Пробел).Конкретные примеры с Python см. В руководствах по Django и Flask.

Окружающая среда

Расширение Python автоматически определяет интерпретаторы Python, установленные в стандартных местах. Он также обнаруживает среды conda, а также виртуальные среды в папке рабочей области. См. Настройка сред Python. Вы также можете использовать параметр python.pythonPath , чтобы указать на интерпретатор в любом месте вашего компьютера.

Текущая среда отображается в левой части строки состояния VS Code:

В строке состояния также указывается, что переводчик не выбран:

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

Чтобы изменить текущий интерпретатор, включая переключение на conda или виртуальные среды, выберите имя интерпретатора в строке состояния или используйте команду Python: Select Interpreter .

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

Установка пакетов

Пакеты

устанавливаются с помощью панели Terminal и таких команд, как pip install <имя_пакета> (Windows) и pip3 install (macOS / Linux).VS Code устанавливает этот пакет в ваш проект вместе с его зависимостями. Примеры приведены в учебнике Python, а также в руководствах по Django и Flask.

Ноутбуки Jupyter

Если вы открываете файл записной книжки Jupyter ( .ipynb ) в VS Code, вы можете использовать Jupyter Notebook Editor для непосредственного просмотра, изменения и запуска ячеек кода.

Вы также можете преобразовать и открыть записную книжку как файл кода Python. Ячейки записной книжки разделены в файле Python комментариями # %% , а расширение Python показывает Run Cell или Run All Cells CodeLens.Выбор CodeLens запускает сервер Jupyter и запускает ячейку (я) в интерактивном окне Python:

Открытие записной книжки в виде файла Python позволяет использовать все возможности отладки VS Code. Затем вы можете сохранить файл записной книжки и снова открыть его как записную книжку в редакторе записных книжек, Jupyter или даже загрузить его в такую ​​службу, как Azure Notebooks.

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

Тестирование

Расширение Python поддерживает тестирование с помощью фреймворков unittest, pytest и носового тестирования.

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

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

Конфигурация

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

Другие популярные расширения Python

Расширение Microsoft Python предоставляет все функции, описанные ранее в этой статье. Дополнительную поддержку языка Python можно добавить в VS Code, установив другие популярные расширения Python.

  1. Откройте представление Extensions (⇧⌘X (Windows, Linux Ctrl + Shift + X)).
  2. Отфильтруйте список расширений, набрав «python».

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

Следующие шаги

07.03.2019

.

Редактирование кода Python в Visual Studio Code

Расширение Python предоставляет множество функций для редактирования исходного кода Python в Visual Studio Код:

См. Также Поддержка Linting и Jupyter.

Автозаполнение и IntelliSense

Автозаполнение

и IntelliSense предусмотрены для всех файлов в текущей рабочей папке и для пакетов Python, установленных в стандартных местах.

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

  • Перейти к определению (F12) переходит из вашего кода в код, определяющий объект.Эта команда полезна при работе с библиотеками.

  • Peek Definition (⌥F12 (Windows Alt + F12, Linux Ctrl + Shift + F10)), аналогично, но отображает определение непосредственно в редакторе (освобождая место в окне редактора, чтобы избежать скрытия кода). Нажмите Escape, чтобы закрыть окно Peek, или используйте изображение x в правом верхнем углу.

  • Перейти к объявлению выполняет переход к точке, в которой переменная или другой объект объявлен в вашем коде.

  • Peek Declaration аналогичен, но отображает объявление прямо в редакторе. Снова используйте Escape или x в верхнем правом углу, чтобы закрыть окно Peek.

Настроить поведение IntelliSense

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

Вы также можете настроить общее поведение автозаполнения и IntelliSense, даже чтобы полностью отключить эти функции.См. Раздел Настройка IntelliSense.

Совет : ознакомьтесь с расширением IntelliCode для VS Code (предварительная версия). IntelliCode предоставляет набор возможностей с использованием искусственного интеллекта для IntelliSense в Python, таких как определение наиболее релевантных автозавершений на основе текущего контекста кода. Дополнительные сведения см. В разделе часто задаваемых вопросов о IntelliCode для VS Code.

Включить IntelliSense для пользовательских расположений пакетов

Чтобы включить IntelliSense для пакетов, установленных в других нестандартных местах, добавьте эти места в Python .Коллекция autoComplete.extraPaths в файле настроек (коллекция по умолчанию пуста). Например, вы могли установить Google App Engine в произвольных местах, указанных в app.yaml , если вы используете Flask. В этом случае вы должны указать эти местоположения следующим образом:

Окна:

  "python.autoComplete.extraPaths": [
    "C: / Program Files (x86) / Google / google_appengine",
    "C: / Program Files (x86) / Google / google_appengine / lib / flask-0.12 "]  

macOS / Linux:

  "python.autoComplete.extraPaths": [
    "~ / .local / lib / Google / google_appengine",
    "~ / .local / lib / Google / google_appengine / lib / flask-0.12"]  

Параметр python.autocomplete.addBrackets (по умолчанию false ) также определяет, будет ли VS Code автоматически добавлять круглые скобки ( () ) при автозаполнении имени функции. Например, если вы установите addBrackets на true :

  "питон.autoComplete.addBrackets ": true,  

, а затем напишите import os , а затем os.getc , вы увидите автозаполнение для os.getcwd . Выбор этого автозаполнения добавляет os.getcwd () к вашему исходному коду и помещает курсор в круглые скобки. Если значение параметра равно false, в файл добавляется только os.getcwd .

Для получения более подробной информации о IntelliSense в целом см. IntelliSense.

Поиск и устранение неисправностей

Если автозаполнение и IntelliSense не работают для настраиваемого модуля, проверьте следующие причины:

Причина Решение
Неверный путь к интерпретатору Python Проверьте настройку pythonPath .Если вы внесете исправления, перезапустите VS Code.
Пользовательский модуль находится в нестандартном месте (не устанавливается с помощью pip). Добавьте местоположение в настройку python.autoComplete.extraPaths и перезапустите VS Code.

Быстрые исправления

Добавление импорта Quick Fix позволяет быстро заполнять операторы импорта. Начните с ввода имени пакета в редакторе, и вы заметите, что доступно действие кода для автоматического завершения строки исходного кода (если у вас установлен модуль в среде).Наведите указатель мыши на текст (отмеченный волнистой линией), а затем выберите лампочку Code Action, когда она появится. Затем вы можете выбрать из списка потенциального импорта.

Добавление импорта Code Action также распознает некоторые популярные сокращения для следующих распространенных пакетов Python: numpy как np, tensorflow как tf, pandas как pd, matplotlib.pyplot как plt, matplotlib , как mpl, math как m, scipi.io как spio и scipy как sp.

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

Примечание : Эта функция требует использования Microsoft Python Language Server. Чтобы включить языковой сервер, установите python.jediEnabled на false в своих настройках.json файл. Кроме того, вам необходимо убедиться, что линтинг включен, открыв палитру команд ( View> Command Palette … или ⇧⌘P (Windows, Linux Ctrl + Shift + P)) и запустив Python: Enable Команда Linting (Выберите на в появившемся раскрывающемся меню).

Выбор запуска / Линия в терминале (REPL)

Команда Python: Run Selection / Line in Python Terminal (Shift + Enter) — это простой способ взять любой выбранный код или код в текущей строке, если нет выбора, и запустить его в терминале Python .Идентичная команда Run Selection / Line в Python Terminal также доступна в контекстном меню для выбора в редакторе.

VS Code автоматически удаляет отступы на основе первой непустой строки выделения, соответственно сдвигая все остальные строки влево.

Исходный код, выполняемый в терминале / REPL, накапливается до тех пор, пока текущий экземпляр терминала не будет закрыт.

Команда открывает терминал Python при необходимости; вы также можете открыть интерактивную среду REPL напрямую с помощью команды Python: Start REPL .(Первоначальный запуск может занять некоторое время, особенно если первый запускаемый вами оператор — это import .)

При первом использовании команды Python: Run Selection / Line in Python Terminal VS Code может отправить текст в REPL до того, как эта среда будет готова, и в этом случае выделение или строка не будут запущены. Если вы столкнулись с таким поведением, попробуйте команду еще раз, когда REPL завершит загрузку.

Форматирование

Форматирование упрощает чтение кода людьми за счет применения определенных правил и соглашений для межстрочного интервала, отступов, интервалов между операторами и т. Д. (См. Пример на странице autopep8).Форматирование не влияет на функциональность самого кода. (Линтинг, с другой стороны, анализирует код на предмет общих синтаксических, стилистических и функциональных ошибок, а также нетрадиционных методов программирования, которые могут привести к ошибкам. Хотя есть небольшое совпадение между форматированием и линтингом, эти две возможности дополняют друг друга.)

Расширение Python поддерживает форматирование исходного кода с использованием autopep8 (по умолчанию), черного или yapf.

Общие настройки форматирования

Настройка
(python.форматирование.)
Значение по умолчанию Описание
провайдер "autopep8" Задает используемый форматировщик: autopep8, yapf или black.

Параметры, относящиеся к форматеру

Следующие настройки применяются к отдельным модулям форматирования. Расширение Python ищет средство форматирования в текущем pythonPath . Чтобы использовать средство форматирования в другом месте, укажите это место в соответствующей настройке пользовательского пути.Команды pip install могут потребовать повышения прав.

Форматировщик Установить шаги Настройка аргументов
(python.formatting.)
Настройка пользовательского пути
(python.formatting.)
autopep8 pip install pep8
pip install —upgrade autopep8
autopep8Args autopep8 Путь
черный (см. Примечание) pip install черный черный Args черный Путь
япф pip install yapf yapfArgs yapfPath

Примечание : По умолчанию Black formatter не может быть установлен, когда активна среда Python 2.При попытке сделать это может появиться сообщение «Formatter black не установлен. Установить?». Если вы попытаетесь установить Black в ответ, появится другое сообщение: «Не удалось найти версию, удовлетворяющую требованию black» Для черного не найдено подходящего распределения ».

Чтобы обойти эту проблему и использовать средство форматирования Black с Python 2, сначала установите Black в среде Python 3. Затем установите для параметра python.formatting.blackPath это место установки.

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

  "python.formatting.autopep8Args": ["--max-line-length", "120", "--experimental"],
"python.formatting.yapfArgs": ["--style", "{based_on_style: chromium, indent_width: 20}"],
"python.formatting.blackArgs": ["--line-length", "100"]  

Во втором примере элемент верхнего уровня {based_on_style: chromium, indent_width: 20} является единственным значением, заключенным в фигурные скобки, поэтому пробелы внутри этого значения не выделяют отдельный элемент.

Поиск и устранение неисправностей

Если форматирование не удалось, проверьте следующие возможные причины:

Причина Решение
Неверный путь к интерпретатору python. Проверьте настройку pythonPath .
Форматер не установлен в текущей среде. Откройте командную строку, перейдите в расположение, указанное в настройке pythonPath , и запустите pip install для форматтера.
Неверный путь к форматеру. Проверьте значение соответствующего параметра python.formatting. Path .
Пользовательские аргументы для модуля форматирования неверны. Убедитесь, что соответствующий параметр python.formatting. Path не содержит аргументов и что python.formatting. Args содержит список отдельных элементов аргумента верхнего уровня, таких как "python.formatting.yapfArgs ": [" --style "," {based_on_style: chromium, indent_width: 20} "] .

При использовании черного средства форматирования VS Code выдает следующее предупреждение при вставке исходного кода в редактор: Черный не поддерживает команду «Выбор формата».

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

  "[питон]": {
    "редактор.formatOnPaste ": false
}  

Рефакторинг

Расширение Python добавляет следующие команды рефакторинга: Extract Variable , Extract Method и Sort Imports .

Извлекает все похожие вхождения выделенного текста в текущей области и заменяет их переменной. Новому методу присвоено имя newvariableNNN , где NNN — случайное число.

Вызвано:

  • Контекстное меню: щелкните правой кнопкой мыши выделение и выберите Извлечь переменную .
  • Command Palette (⇧⌘P (Windows, Linux Ctrl + Shift + P)), затем Python Refactor: Extract Variable .
  • Назначьте сочетание клавиш для команды python.refactorExtractVariable .

Извлекает все похожие вхождения выбранного выражения или блока в текущей области и заменяет их вызовом метода. Новому методу присвоено имя newmethodNNN , где NNN — случайное число.

Вызвано:

  • Контекстное меню: щелкните правой кнопкой мыши выделение и выберите Extract Method .
  • Command Palette (⇧⌘P (Windows, Linux Ctrl + Shift + P)), затем Python Refactor: Extract Method .
  • Назначьте сочетание клавиш для команды python.refactorExtractMethod .

Сортировка импорта

Sort Imports использует пакет isort для объединения определенных операций импорта из того же модуля в один оператор import и для организации операторов импорта import в алфавитном порядке.

Вызвано:

  • Щелкните правой кнопкой мыши в редакторе и выберите Сортировка импорта (выбор не требуется)
  • Палитра команд

  • (⇧⌘P (Windows, Linux Ctrl + Shift + P)), затем Рефакторинг Python: Сортировка импорта
  • Назначьте сочетание клавиш для команды python.sortImports

Пользовательские аргументы для isort указаны в настройке python.sortImports.args , где каждый элемент верхнего уровня, разделенный пробелами в командной строке, является отдельным элементом в массиве:

  "питон.sortImports.args ": [" -rc "," --atomic "],  

Чтобы использовать настраиваемый сценарий isort, используйте параметр python.sortImports.path , чтобы указать путь.

Дальнейшие конфигурации могут быть сохранены в файле .isort.cfg , как описано в разделе «Конфигурация isort».

Примечание : Для тех, кто переходит с isort4 на isort5, некоторые флаги интерфейса командной строки и параметры конфигурации были изменены, см. Руководство по обновлению isort5 проекта.

Следующие шаги

  • Линтинг — включение, настройка и применение различных линтеров Python.
  • Debugging — Научитесь отлаживать Python как локально, так и удаленно.
  • Тестирование — настройка тестовых сред и обнаружение, запуск и отладка тестов.
  • Basic Editing — Узнайте о мощном редакторе VS Code.
  • Навигация по коду — быстрое перемещение по исходному коду.
  • IntelliSense — узнайте о функциях IntelliSense.

29.01.2019

.Объекты кода

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

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

PyCodeObject

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

PyTypeObject PyCode_Type

Это экземпляр PyTypeObject , представляющий Python
код тип.

int PyCode_Check (PyObject * co )

Вернуть true, если co является объектом с кодом .

интервал PyCode_GetNumFree (PyCodeObject * co )

Вернуть количество свободных переменных в co .

PyCodeObject * PyCode_New (int argcount , int kwonlyargcount , int nlocals , int stacksize , int flags , PyObject * код , PyObject * consts , имена PyObject * varnames , PyObject * freevars , PyObject * cellvars , PyObject * имя файла , PyObject * имя , int firstlineno , PyObject * lnotab )
Возвращаемое значение: Новая ссылка.

Вернуть новый объект кода. Если вам нужен фиктивный объект кода для создания фрейма,
вместо этого используйте PyCode_NewEmpty () . Вызов PyCode_New () напрямую
может привязать вас к точной версии Python, так как определение байт-кода
часто меняется.

PyCodeObject * PyCode_NewWithPosOnlyArgs (int argcount , int posonlyargcount , int kwonlyargcount , int nlocals , int stacksize , int flags , PyObject , int flags , PyObject * имена , PyObject * varnames , PyObject * freevars , PyObject * cellvars , PyObject * filename ,

.

1. Командная строка и среда — документация Python 3.9.0

Интерпретатор CPython сканирует командную строку и среду на предмет различных
настройки.

Детали реализации CPython: Схемы командной строки других реализаций могут отличаться. Видеть
Альтернативные реализации для получения дополнительных ресурсов.

1.1. Командная строка

При вызове Python вы можете указать любую из этих опций:

 python [-bBdEhiIOqsSuvVWx?] [-C команда | -m имя-модуля | сценарий | -] [аргументы]
 

Самый распространенный вариант использования — это, конечно, простой вызов скрипта:

1.1.1. Варианты интерфейса

Интерфейс интерпретатора похож на интерфейс оболочки UNIX, но предоставляет некоторые
дополнительные методы вызова:

  • При вызове со стандартным входом, подключенным к tty-устройству, запрашивается
    команд и выполняет их до EOF (символа конца файла, вы можете
    произвести, что с Ctrl-D в UNIX или Ctrl-Z, введите в Windows) читается.

  • При вызове с аргументом имени файла или с файлом в качестве стандартного ввода
    читает и выполняет сценарий из этого файла.

  • Когда вызывается с аргументом имени каталога, он читает и выполняет
    сценарий с соответствующим именем из этого каталога.

  • При вызове с помощью команды -c он выполняет оператор (ы) Python, указанный как
    команда . Здесь команда может содержать несколько операторов, разделенных
    новые строки. Ведущие пробелы важны в операторах Python!

  • При вызове с -m имя-модуля , данный модуль находится на
    Путь к модулю Python и выполняется как скрипт.

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

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

-c <команда>

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

Если задана эта опция, первый элемент sys.argv будет
"-c" и текущий каталог будет добавлен в начало
sys.path (позволяет импортировать модули в этом каталоге как верхние
модули уровня).

Вызывает событие аудита cpython.run_command с аргументом command .

-m <имя-модуля>

Найдите sys.path для названного модуля и выполните его содержимое как
модуль __main__ .

Поскольку аргументом является имя модуля , вы не должны указывать расширение файла
(

.

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

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