Показаны сообщения с ярлыком python. Показать все сообщения
Показаны сообщения с ярлыком python. Показать все сообщения

воскресенье, 22 июля 2018 г.

Ускоряем Python через cython


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

В python есть такая вещь как Type Hints. Мы просто можем указать типы данных для передаваемых значений в функции, а также типы возвращаемых значений.

def greeting(name: str) -> str:
    return 'Hello ' + name
Конечно, для интерпретатора, это мало, что значит. Основная идея была в улучшении подсказок в средах разработки, упрощенного анализа кода и поиска багов, может помочь вам писать код строже (потому что вы начнете соблюдать типы и думать о структуре кода, а не пихать все в одну переменную, если вы конечно так делали), так же это может помочь статическим анализаторам кода в нахождении проблем. Штука интересная, но не сильно помогающая в продакшене. Но на самом деле, теперь мы можем сделать очень интересные вещи для ускорения кода. И нам поможет Cython. В детали не будем залазить, возьмем простой пример показывающий общую суть.

def fun(x: float) -> float:
    return x*x-x

def integrate_f(a: float, b: float, N: int):
    s : float = 0
    dx : float = (b - a) / N
    for i in range(N):
        s += fun(a + i * dx)
    return s * dx
Вот пример нашего кода на питоне (прямо из документации). Проверяем:

begin = time.time()
print(tpy.integrate_f(1006, 2610, 100000000))
print(time.time() - begin)

Итог:
5584257516.163277
25.64701795578003

25 секунд, не очень радостное время. Однако, если мы переименуем файл в pyx и дополним тремя декораторами:

import cython

@cython.cfunc  # cdef functions are faster but not callable from python
def fun(x: float) -> float:
    return x*x-x

@cython.locals(i=cython.int, N=cython.int)  # better integration soon
@cython.cdivision(True)  # remove divide by zero protection
def integrate_f(a: float, b: float, N: int):
    s : float = 0
    dx : float = (b - a) / N
    for i in range(N):
        s += fun(a + i * dx)
    return s * dx

Как это добро компилировать смотрим в документации. Проверяем: 

import t (наш cython модуль)
import time

if __name__ == '__main__':
    begin = time.time()
    print(t.integrate_f(1006, 2610, 100000000))
    print(time.time() - begin)

Итог:
5584257516.166599
0.15540504455566406

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

Для сравнения делаем в лоб одинаковое решение на go:

package main

import (
    "log"
    "time"
)

func fun(x float32) float32 {
    return x*x - x
}

func integrate_f(a float32, b float32, N int) float32 {
    s := float32(0.0)
    dx := (b - a) / float32(N)
    for i := 0; i < N; i++ {
        s += fun(a + float32(i)*dx)
    }
    return s * dx
}

func main() {
    start := time.Now()
    integrate_f(1006, 2610, 100000000)
    elapsed := time.Since(start)
    log.Printf("time %s", elapsed)
}


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

четверг, 23 октября 2014 г.

quicksort на python

Описание алгоритма.

def quicksort(array):
    if len(array) <= 1:
        return arr
    else:
        return quicksort([x for x in array[1:] if x < array[0]]) + [array[0]] + quicksort([x for x in array[1:] if x >= array[0]])

вторник, 7 октября 2014 г.

алгоритм Евклида на python

Алгори́тм Евкли́да — эффективный алгоритм для нахождения наибольшего общего делителя двух целых чисел.

Подробнее об алгоритме читаем на википедии.

Пример реализации на python:

def evc(a, b):
    if a < b:
        a, b = b, a
    r = 1
    while r > 0:
        r = a % b
        a = b
        b = r

    return a

понедельник, 3 февраля 2014 г.

определить ip адрес с помощью python

Я находил два решения этой задачи. Первое и самое простое:

import socket
socket.gethostbyname(socket.gethostname())

Но мне такой вариант выдаёт 127.0.0.1, что наверное и неудивительно.

Однако есть и более изощренный способ:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("gmail.com",80))
print(s.getsockname()[0])
s.close()
В этот раз получил правильный адрес машины.

понедельник, 16 сентября 2013 г.

Переход на python 3

Хочу не много затронуть тему о которой по прежнему ведутся споры. Стоит ли переходить на python 3 или нет? На самом деле сейчас ответ мне видится уже очевидным. Если у вас нет большого пласта кода на python 2, который тяжело портировать, или нет библиотек необходимых вам (и вы не можете их портировтаь сами), то только в этом случаи вам стоит оставаться на python 2.

Пора уже смириться с тем, что python 3 должен заменить вторую ветку. Я сам долго оттягивал переход, однако с переходом django на python 3 у меня не осталось аргументов.

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

четверг, 12 сентября 2013 г.

запуск virtualen на ubuntu для python 3

В самой задаче нет никаких сложностей, однако при переходе с винды бывают проблемы. Делается это в два шага:
  • создаём virtualenv:
virtualenv -p /usr/bin/python3 myenv
  • запускаем:
source myenv/bin/activate
Всё. Пользуемся)

понедельник, 6 мая 2013 г.

Python установка exe пакетов в virtual environment в windows

На работе большую часть времени приходиться работать на windows 7, поэтому часто возникает задача ставить exe пакеты в virtual environment. Делается это очень просто:

easy_install file://c:/.../mysql-python.exe

Все пакет установлен и готов к использованию. Удачи.

суббота, 4 мая 2013 г.

Django сериализация одиночного объекта

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

result = serializers.serialize('json', Job.objects.all()).

Но если мы хотим передать одну работу придётся передавать это так:

result = serializers.serialize('json', [Job.objects.get(pk=pk)]). 

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

result = serializers.serialize('json', [Job.objects.get(pk=pk)]) 
object_result = array_result[1:-1]