Как стать автором
Обновить

И еще несколько полезных библиотек для Python (с примерами)

Время на прочтение9 мин
Количество просмотров19K

Введение

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

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

Стилизация print

icecream

Для форматирования вывода существует одна удобная библиотека под названием icecream. Она помогает упростить написание логов или принтов для отладки. Рассмотрим пример её работы:

from icecream import ic
text = "Привет"
ic(text)
>>> ic| text: 'Привет'

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

ic.configureOutput(includeContext=True)
from icecream import ic
ic.configureOutput(includeContext=True)
text = "Привет"
ic(text)
>>> ic| test_modules.py:5 in <module>- text: 'Привет'

Это помогает более точно понять в каком месте происходит сбой в работе программы:

class A:
   def changeText(self, text):
       ic(text)
A().changeText("Привет!")
>>> ic| test_modules.py:10 in changeText()- text: 'Привет!'

Также можно поменять префикс, который добавляется в начале строки, по дефолту он задан “ic|”. Удобно добавить время для вывода, чтобы видеть в какой момент времени и сколько занимал переход от одного принта к другому.

ic.configureOutput(prefix="PRINT| ")
text = "Привет"
ic(text)
>>> PRINT| test_modules.py:7 in <module>- text: 'Привет'
from icecream import ic
from datetime import datetime as dt
ic.configureOutput(includeContext=True)
ic.configureOutput(prefix=lambda : f'{dt.now().strftime("%d-%m-%Y %H:%M:%S:%f")}: ')
text = "Привет"
ic(text)
>>> 22-05-2021 15:58:40:851233: test_modules.py:10 in <module>
                            text: 'Привет'

Если у вас уже имеются расставленные принты в коде, то легко можно переприсвоить print на ic:

print = ic
text = "Привет"
print(text)
>>> 22-05-2021 15:59:54:291440: test_modules.py:8 in <module>
                            text: 'Привет'

Рассмотрим пример вывода более сложных структур, например, словарей:

from icecream import ic
ic.configureOutput(includeContext=True)
ic.configureOutput(prefix="ВЫВОДИМ СЛОВАРЬ:")
print = ic
import random as rnd
def createRndData():
  result = {}
  namesMale = ["Иван", "Петр", "Александр", "Алексей"]
  namesFemale = ["Кристина", "Екатерина", "Мария", "Дарья"]
  hobbies = ["Футбол", "Чтение книг", "Спортивная ходьба",
             "Стрельба из лука", "Плавание", "Поэзия"]
  cities = ["Москва", "Владивосток", "Воронеж", "Сочи", "Архангельск",
            "Анапа", "Мурманск", "Магнитогорск"]
  for name in namesMale:
      result[name] = {"возраст": rnd.randint(20, 60),
                      "хобби": rnd.choice(hobbies),
                      "пол": "М",
                      "город":rnd.choice(cities),
                      "любимое число": rnd.randint(1, 100)}
  for name in namesFemale:
      result[name] = {"возраст": rnd.randint(20, 60),
                      "хобби": rnd.choice(hobbies),
                      "пол": "Ж",
                      "город":rnd.choice(cities),
                      "любимое число": rnd.randint(1, 100)}
  print(result)
createRndData()

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

Также эта библиотека предоставляет возможность стилизовать вывод в зависимости от предоставляемых данных. Например, если есть необходимость дополнительно оформлять текст ошибки (Exception) или есть желание дополнительно выводить тип данных:

from icecream import ic
ic.configureOutput(includeContext=True)
def styleOutputVars(obj):
   if isinstance(obj, Exception):
       obj = f"Ваш доп текст для ошибки: {obj}"
   elif isinstance(obj, int):
       if obj > 100:
           obj = f"Пришло число > 100, непорядок: {obj}"
   return repr(obj)

ic.configureOutput(argToStringFunction=styleOutputVars)
print = ic
try:
   assert isinstance(1, str), ("Ошибка в сравнении типов")
except Exception as e:
   print(e)

print(1000, 1, 2, 3, 4, 200)

colorama

Еще одна полезная библиотека - colorama, она позволит раскрашивать текст в консоли. Её удобно использовать совместно с библиотекой icecream. Рассмотрим пару примеров:

from colorama import Fore, Back, Style
from datetime import datetime as dt
from icecream import ic
ic.configureOutput(includeContext=True)
ic.configureOutput(prefix=lambda : f'{dt.now().strftime("%M:%S:%f")}: ')
def styleOutputVars(obj):
   if isinstance(obj, Exception):
       obj = f"Ваш доп текст для ошибки: {Fore.RED}{obj}"
   elif isinstance(obj, int):
       if obj > 100:
           obj = f"{Fore.RED}Пришло число > 100, непорядок: {obj}"

   return str(obj)
ic.configureOutput(argToStringFunction=styleOutputVars)
try:
   assert isinstance(1, str), ("Ошибка в сравнении типов")
except Exception as e:
   print(Fore.BLACK + Back.YELLOW + ic.format(e) + Style.RESET_ALL)
print(ic.format(1000))

Многопоточность (многозадачность)

multitasking

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

Достаточно выполнить простую установку: pip install multitasking и далее оборачивать метод декоратором @task

import multitasking
import time
import random as rnd
multitasking.set_max_threads(10)
@multitasking.task
def tryToSleep(name):
   for i in range(5):
       ts = rnd.randint(2, 6)/2
       print(name, i, 'спит:', ts)
       time.sleep(ts)
tryToSleep("Первый")
tryToSleep("Второй")

И если удалить @multitasking.task, код выполнится последовательно:

Связь с другими языками

pythonnet

Иногда возникает потребность запустить код, написанный на другом языке, через Python, например, в целях проверки работы какого-либо стороннего модуля или для оптимизации кода. Существует несколько библиотек, позволяющих сделать это, например, pythonnet позволяет запустить некоторую часть кода, написанную на C# в Python (pythonnet позволяет рассматривать множество элементов clr, как модули в python).

Создаем проект библиотеки классов C# в visual studio, создаем в неё нужный класс или методы (в случае примера класс, содержащий метод вычисления дискриминанта), создаем .dll и запускаем через pythonnet (более подробно тут):

// имя проекта MyTestCS
namespace MyTestCS
{
    public class MyClassCS
    {
        public float discriminant(float a, float b, float c)
        {
            return b * b - 4 * a * c;
        }
    }
}

Обращаемся к C# через Python

import clr
import os
clr.AddReference(os.getcwd() + "\\MyTestCS.dll")
from MyTestCS import MyClassCS
mc = MyClassCS()
print(mc.discriminant(4, 4, 1))
>>> 0.0
print(mc.discriminant(4, 4, 2))
>>> -16.0
print(mc.discriminant(6, 24, 11))
>>> 312.0

JPype

Для этих же целей существует библиотека, которая позволяет запустить Java код в Python. Эта библиотека называется - JPype. Рассмотрим пример работы библиотеки.

Для начала установим её pip install jpype1, далее создадим Java проект, который в будущем скомпилируем в .jar архив, в проекте необходимо создать пакет, в нём класс и прописать следующий код (код вычисляет объем цилиндра):

package pkg_java;
import java.util.*;
public class JavaPrime {
   public static double cylinderVolume(double r, double h){
       double pi = 3.141592653589793;
       return r*r*pi*h;
   }
}

Теперь можно создать .jar решение проекта.

В python коде импортируем библиотеку jpype, запустим JVM и пропишем путь к созданному .jar архиву. Далее по аналогии с pythonnet импортируем необходимые пакеты и классы:

from jpype import *
jarpath = "java_is_prime.jar"
startJVM(getDefaultJVMPath(), "-ea", "-Djava.class.path=%s" % (jarpath))
pkgJava = JPackage("pkg_java")
javaPrimeClass = pkgJava.JavaPrime()
print(javaPrimeClass.cylinderVolume(43 ,262.5))
>>> 1524811.264327976
print(javaPrimeClass.cylinderVolume(2 ,5.5352))
>>> 69.55737462460088

Таким образом, pythonnet и jpype - отличные решения для интеграции кода C# и Java в  Python проект.

Создание десктопных приложений и UI

EEL

Для работы с созданием графических приложений есть несколько популярных библиотек, в частности встроенный tkinter и Qt. Но когда необходимо сделать красивое, легковесное графическое приложение, то хотелось бы использовать что-то более мощное, например, html+css+js, именно с этим может помочь библиотека EEL. Она позволяет создать десктопное приложение, где в качестве графической оболочки используется html, css и js (можно использовать различные фреймворки), а в качестве языка для написания бэк-части используется Python (подробнее тут).

Приведем простой пример использования библиотеки. Python код:

import eel
import random as rnd
values = ["Привет", "Привет, мир", "Вывод", "Test EEL"]
@eel.expose
def testfunc():
   global values
   return rnd.choice(values)
eel.init("front")
eel.start("index.html")

Файл index.html:

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <title>Title</title>
   <script src="eel.js"></script>
</head>
<body>
   <p id="text-p">Hello</p>
   <button id="click-elem">Click</button>
   <script>
       document.getElementById("click-elem").onclick = async () => {
          document.getElementById("text-p").textContent = await eel.testfunc()();
       }
   </script>
</body>
</html>

И сама структура проекта должна выглядеть так:

Можно запустить файл main.py и убедиться, что всё работает:

Анимация математических операций и функций

manim

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

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

from manim import *
class PointWithTrace(Scene):
   def construct(self):
       path = VMobject()
       dot = Dot(point=[0,2,0])
       path.set_points_as_corners([dot.get_center(), dot.get_center()])
       def update_path(path):
           previous_path = path.copy()
           previous_path.add_points_as_corners([dot.get_center()])
           path.become(previous_path)
       path.add_updater(update_path)
       self.add(path, dot)
       self.play(dot.animate.shift([-1,-2,0]))
       self.play(dot.animate.shift([2, 1.4, 0]))
       self.play(dot.animate.shift([-2, 0, 0]))
       self.play(dot.animate.shift([2, -1.4, 0]))
       self.play(dot.animate.shift([-1, 2, 0]))
       tex = Tex(r'$f: A \rightarrow B$', tex_template=TexFontTemplates.french_cursive).scale(2)
       tex.shift(2*DOWN)
       self.play(Write(tex))
       mtex = MathTex(r'f(x) &= 3x^2 - 5x + 1').scale(2)
       mtex.shift(3*UP)
       self.play(Write(mtex))

Также более серьёзный пример работы:

Оценка производительности

pympler

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

from pympler import asizeof
class A():
   def __init__(self):
       self.a = []
       self.b = []
mya = A()
mya.a = [1,2,3, (6,7), "Hi"]
print(asizeof.asized(mya))
>>> size 728, flat 56, refs[0], name '<__main__.A object at 0x00000296ECED9CF8>'
print(asizeof.asized(mya.a, detail=1).format())
>>> [1, 2, 3, (6, 7), 'Hi'] size=384 flat=104
    (6, 7) size=128 flat=64
    'Hi' size=56 flat=56
    1 size=32 flat=32
    2 size=32 flat=32
    3 size=32 flat=32

Проследим за изменением памяти, занимаемой классом A:

from pympler import tracker
from pympler import classtracker
class A():
   def __init__(self):
       self.a = []
       self.b = []
   def full(self):
       for i in range(10000):
           self.a.append(i**100)
       for i in range(10000):
           self.b.append({i:  i**7})
tr = classtracker.ClassTracker()
tr.track_class(A)
tr.create_snapshot()
mya = A()
mya.a.extend([1, 2, 3, 4, 5, 6, 7, 8, 9])
mya.full()
mya2 = A()
mya2.full()
tr.create_snapshot()
tr.stats.print_summary()

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

tr2 = tracker.SummaryTracker()
mya = A()
mya.a.extend([1, 2, 3, 4, 5, 6, 7, 8, 9])
mya.full()
mya2 = A()
mya2.full()
tr2.print_diff()

py-spy

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

В таком случае можно воспользоваться библиотекой py-spy, она позволяет без остановки программы проверить, сколько времени какие процессы в ней занимают. Установить библиотеку можно через pip install py-spy. Усложним код предыдущей программы, создадим дополнительный модуль sec.py:

import random as rnd
def getRndList():
   l = []
   for i in range(1, 1000000):
       l.append(rnd.randint(100,10000)%i)
   return l

модуль main.py:

import time
from sec import *
time.sleep(5)
print("start")
class A:
   def __init__(self):
       self.a = []
       self.b = []
       self.c = []

   def full(self):
       for i in range(1000000):
           self.a.append(i**100)
       for i in range(1000000):
           self.b.append({i:  i**7})
mya = A()
mya.a.extend([1, 2, 3, 4, 5, 6, 7, 8, 9])
mya.full()
mya2 = A()
mya2.full()
mya2.c = getRndList()
mya.c = getRndList()

Теперь через терминал достаточно прописать команду py-spy top -- python main.py и вы будете получать информацию о времени работы каждого метода программы:

Обработка языков

pymorphy2

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

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

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

Небольшой пример использования библиотеки:

import pymorphy2
morph = pymorphy2.MorphAnalyzer()
info = morph.parse('окна')
print('ВСЯ ИНФОРМАЦИЯ О СЛОВЕ:')
print(info)

info = morph.parse('окна')[0]
print('НАЧАЛЬНАЯ ФОРМА:')
print(info.normal_form)

newinfo = info.inflect({'gent', 'plur'})
print("СТАВИМ В РОД.ПАДЕЖ, МН.Ч:")
print(newinfo)
print(newinfo.word)

print('ПОЛУЧАЕМ ВСЕ СКЛОНЕНИЯ СЛОВА')
print(info.lexeme)

Заключение

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

P.s. тесты проводил на win-10 x64, python: v3.6.6.

Теги:
Хабы:
+18
Комментарии3

Публикации

Изменить настройки темы

Истории

Работа

Python разработчик
132 вакансии
Data Scientist
60 вакансий

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн