Архив рубрики: Python

Числа в Python 3

Что приходит в голову при словах «числа в Питоне?»

int и float. Если речь идет о Python 2 — еще и упразднённый long. Наверное, вспомнится очень мало где используемый complex.

Я же хочу рассказать о Decimal и Fraction.

Decimal

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

Что произойдёт, если для денег мы станем использовать float?

Как я писал в статье: 4 грн 31 коп будут на самом деле иметь внутреннюю запись 4.3099999999999996. Да, при печати всё показывается нормально если у вас Python 2.7+ — но внутри это всё же чуть-чуть иное число!

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

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

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

Внутри decimal представлен как знак, набор цифр и положение десятичной точки — т.е. нет никакого округления.

Использование очень простое:

>>> from decimal import Decimal
>>> Decimal("4.31")
Decimal('4.31')
>>> Decimal("4.31") + Decimal("1.10")
Decimal('5.41')

Все стандартные операции над decimal работают так же хорошо, как и с просто числами.

К слову, базы данных как правило имеют встроенную поддержку этого типа, а драйвера DBAPI и ORM вроде Django и SQLAlchemy тоже умеют работать с decimal.

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

Пример:

>>> Decimal("1.10") / 3
Decimal('0.3666666666666666666666666667')

Ой! Зачем так много цифр, ведь у нас гривна с копейками?!!

Дело в том, что помимо Decimal есть еще и Context. По умолчанию у него точность в 28 чисел в дробной части, что явно многовато для валюты. Настроим на 2 знака:

>>> from decimal import getcontext
>>> getcontext().prec = 2
>>> Decimal('1.10') / 3
Decimal('0.37')

Уже лучше.

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

Вернемся к наиболее распространенным задачам.

Что делать, если часть вычислений нужно проводить с точностью «до копеек», а некоторые (например, то же сведение баланса и подсчет налогов) — до сотых долей копеек?

Наиболее практичный способ — создание своего контекста и применение его в with statement:

>>> from decimal import Context, localcontext
>>> with localcontext(Context(4)):
... print(repr(Decimal("1.10") / 3))
Decimal('0.3667')

Округление:

>>> Decimal('1.12').quantize(Decimal('0.1'))
Decimal('1.1')
>>> Decimal('1.16').quantize(Decimal('0.1'))
Decimal('1.2')

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

>>> getcontext().prec = 2
>>> Decimal('1.10').quantize(Decimal('0.000001'))
Traceback (most recent call last):
...
decimal.InvalidOperation: quantize result has too many digits for current context

Вот и всё.

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

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

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

Хватит использовать “print” для отладки: пятиминутное введение в модуль logging Python’а (Перевод)

  • Это руководство короткое
  • Для поиска ошибок в коде Вы можете использовать print для просмотра значений переменных
  • НЕ делайте этого. Используйте модуль logging
Модуль logging предпочтительнее, так как:
  • Легко можно поместить указатель времени в каждое сообщение
  • Вы можете использовать разные уровни срочности ваших сообщений и фильтровать их по этому уровню
  • Когда Вы захотите позже найти / изменить лог-сообщения Вы не перепутаете их с другим выводом команды print
  • Если Вы хотите вывести лог в файл, то очень легко будет игнорировать вывод лог-сообщений
Использование pirnt подходит для тех программистов, у которых достаточно времени. Но лучше использовать logging. Кроме того, научитесь использовать отладчик Python для отладки ошибок и Pylint для предотвращения появления ошибок и для того, чтобы сделать код более читаемым.
Для того, чтобы отобразить лог-сообщения на экране, используйте такой код:

import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
logging.debug('This is a log message.')

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

import logging
logging.basicConfig(filename='log_filename.txt',level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
logging.debug('This is a log message.')

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

import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
fh = logging.FileHandler('log_filename.txt')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.debug('This is a test log message.')

Убедитесь, что переменная logging глобальная, так что её смогут видеть все функции (использовать в них gloval logger не нужно, так как эта переменная только для чтения, не для записи и изменения).

Уровни логирования, от более срочных до менее срочных:
  1. CRITICAL
  2. ERROR
  3. WARNING
  4. INFO
  5. DEBUG
Вызов setLevel() устанавливает минимальный уровень логирования, нужный Вам. Например, если Вы используете fh.setLevel(logging.ERROR), тогда будут сообщения  с уровнем WARNING, INFO и DEBUG не будут записываться в файл (так как fh - обработчик лог-файла в нашем примере, в отличие от ch, который обрабатывает логи для отображения на экране).
Для записи сообщен
ий разного уровня используйте
:
  1. logger.critical('This is a critical message.')
  2. logger.error('This is an error message.')
  3. logger.warning('This is a warning message.')
  4. logger.info('This is an informative message.')
  5. logger.debug('This is a low-level debug message.')
На самом деле Вы можете делать гораздо больше, но это всё, что Вам надо знать, чтобы больше не использовать print для отладки программ.
Кроме того, функция pprint.pprint() хороша для вывода словарей и списков, особенно вложенных. Попробуйте и увидите.

Последний совет: Вы можете использовать команду tail -f logfile.txt чтобы посмотреть файл, куда будет записываться информация. Опция -f означает “follow (следовать)”. Просто оставьте окно терминала открытым с запущенной командой, и в нем автоматически будет появляться записываемый в файл текст. Этот способ избавит Вас от необходимости открывать текстовый редактор.
Команда tail присутствует на Mac OS X и Linux / *nix. На Windows Вы можете использовать для её получения проект Cygwin.

Автор: Ishayahu Lastov

7.3.5. namedtuple() — фабричная функция для кортежей с именованными полями

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

collections.namedtuple(typenamefield_namesverbose=Falserename=False)
Возвращает новый подкласс кортежа с именем typename. Новый подкласс используется для создания кортежеподобных объектов, поля которых доступны как для просмотра через атрибуты, так и для индексации и итерации. Экземпляры этого подкласса так же содержат строку документации (с именем типа и именами полей)  и полезный метод __repr__(),который отображает содержимое кортежа в форме name=value.

field_names — одна строка, где каждое имя поля отделено от другого пробелами и/или запятыми, например 'x y' or 'x, y'. Кроме того, в этом параметре можно передать последовательность строк ['x', 'y'].
Любые корректные идентификаторы Python могут использоваться в качестве имён полей, за исключением имён, начинающихся с нижнего подчёркивания. Корректные идентификаторы состоят из букв, цифр и нижних подчёркиваний, но не начинается с цифры или нижнего подчёркивания, а так же не может совпадать с одним из keyword например classforreturnglobalpass, или raise.
Если rename=true, некорректные имена полей автоматически заменяются позиционными именами, например ['abc', 'def', 'ghi', 'abc'] будет преобразовано в ['abc', '_1', 'ghi', '_3'], заменив ключевое слово def и повторяющееся имя поля abc.
Если verbose = true, определение класса будет напечатано после того, как класс будет создан. Эта опция устаревшая, вместо неё проще распечатать атрибут _source.
Экземпляры именованного кортежа не имеют своего личного словаря, так что они занимают не больше места, чем обычные кортежи..
Изменения в версии 3.1: Добавлена поддержка метода rename.
>>>

>>> # простой пример
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22) # создаём экземпляр с позиционными или именованными аргументами
>>> p[0] + p[1] # можно получать значения по индексу, как в обычном кортеже (11, 22)
33
>>> x, y = p # распаковывается как обычный кортеж
>>> x, y
(11, 22)
>>> p.x + p.y # поля доступны и по именам
33
>>> p # метод __repr__ с форматом name=value
Point(x=11, y=22)
Именованные кортежи особенно полезны для задания имён полей кортежей, возвращаемых модулями csv или sqlite3:
EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')

import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
print(emp.name, emp.title)

import sqlite3
conn = sqlite3.connect('/companydata')
cursor = conn.cursor()
cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
for emp in map(EmployeeRecord._make, cursor.fetchall()):
print(emp.name, emp.title)
В дополнение к методам, унаследованным от кортежей, именованные кортежи поддерживают три дополнительных метода и два атрибута. Для того, чтобы предотвратить конфликты с именами полей, эти методы и атрибуты начинаются с нижних подчёркиваний.
classmethod somenamedtuple._make(iterable)
Метод класса, который создаёт новый экземпляр из существующей последовательности или итератора.
>>>

>>> t = [11, 22]
>>> Point._make(t)
Point(x=11, y=22)
somenamedtuple._asdict()
Возвращает новый OrderedDict который отображает имена полей на соответствующие значения. Обратите внимание, что этот метод уже не требуется, так как тот же самый эффект может быть достигнут при помощи встроенной функции vars():
>>>

>>> vars(p)
OrderedDict([('x', 11), ('y', 22)])
Изменения в версии 3.1: Возвращает OrderedDict вместо обычного dict.
somenamedtuple._replace(kwargs)
Возвращает новый экземпляр именованного кортежа, заменяя определённые пля новыми значениями:
>>>

>>> p = Point(x=11, y=22)
>>> p._replace(x=33)
Point(x=33, y=22)

>>> for partnum, record in inventory.items():
... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
somenamedtuple._source
Строка с исходным кодом Python, используемым для создания класса именованного кортежа. Этот код позволяет легко получить документацию для именованного кортежа. Его можно вывести на экран, выполнить при помощи exec(), или сохранить в файл и импортировать.
Добавлено в версии 3.3.
somenamedtuple._fields
Кортеж строк с именами полей. Полезен для интроспекции и для создания новых типов именованных кортежей из уже существующих.
>>>

>>> p._fields            # просмотреть имена полей
('x', 'y')

>>> Color = namedtuple('Color', 'red green blue')
>>> Pixel = namedtuple('Pixel', Point._fields + Color._fields)
>>> Pixel(11, 22, 128, 255, 0)
Pixel(x=11, y=22, red=128, green=255, blue=0)
Чтобы получить значения полей, чьи имена сохранены как строки, используйте функцию getattr():
>>>

>>> getattr(p, 'x')
11
Что преобразовать словарь в именованный кортеж используйте операцию распаковывания словаря (как описано в Unpacking Argument Lists):
>>>

>>> d = {'x': 11, 'y': 22}
>>> Point(**d)
Point(x=11, y=22)
Так как именованный кортеж — это обычный класс Python, к нему легко добавлять новую функциональность или изменить уже существующую, просто создавая его подклассы. Тут показано как добавить подсчитываемое поле и изменить формат отображения класса:
>>>

>>> class Point(namedtuple('Point', 'x y')):
__slots__ = ()
@property
def hypot(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
>>>

>>> for p in Point(3, 4), Point(14, 5/7):
print(p)
Point: x= 3.000 y= 4.000 hypot= 5.000
Point: x=14.000 y= 0.714 hypot=14.018
Подкласс, показанный выше, устанавливает атрибут __slots__ в пустой кортеж. Это помогает сократить использование памяти, не допуская создавать словари для экземпляров.
Подклассы не очень полезны для добавления новых полей. Вместо этого, лучше создать новый тип именованного кортежа, используя атрибут _fields имеющегося:
>>>

>>> Point3D = namedtuple('Point3D', Point._fields + ('z',))
Значения по умолчанию можно реализовать используя метод _replace() для настройки экземпляра прототипа
>>>

>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
>>> janes_account = default_account._replace(owner='Jane')
Перечисляемые константы можно задать при помощи именованного кортежа, но  проще сделать это используя  простой класс:
>>>

>>> Status = namedtuple('Status', 'open pending closed&#
39;)._make(range(3))
>>> Status.open, Status.pending, Status.closed
(0, 1, 2)
>>> class Status:
open, pending, closed = range(3)
См также

Автор: Ishayahu Lastov

Руководство по анализу производительности скриптов на Python (Перевод)

Поскольку не каждая программа на Python требует тщательного анализа производительности, отрадно знать, что когда такая необходимость возникает у Вас всё же есть отличные инструменты для этой цели.
Анализ производительности программы состоит в поиске ответа на четыре вопроса:
  1. Как быстро выполняется программа?
  2. Какое место определяет скорость её выполнения?
  3. Как много используется памяти?
  4. Где утечки памяти?
Посмотрим, что же у нас есть для ответа на эти вопросы.

Грубая оценка времени выполнения

Давайте начнём  простого и быстрого способа оценки времени выполнения нашего кода: старой доброй утилиты UNIX time:
$ time python yourprogram.py

real 0m1.028s
user 0m0.001s
sys 0m0.003s

Объяснение этого результата можно найти в этой статье. В кратце:

  • real — время от запуска программы до её завершения
  • user — время процессора, затраченное вне ядра
  • sys — время процессора, затраченное в ядре
Таким образом, сложив user и sys, Вы можете оценить сколько процессорного времени потребовалось вашей программе вне зависимости от загрузки системы.
Если эта сумма сильно больше чем реально затраченное время, тогда можно предположить, что проблемы с производительностью вашей программы связаны с ожиданием ввода / вывода.

Более точная оценка времени с использованием менеджера контекста

Вот маленький модуль, который окажет нам бесценную пользу в измерении времени выполнения:
timer.py
import time

class Timer(object):
def __init__(self, verbose=False):
self.verbose = verbose

def __enter__(self):
self.start = time.time()
return self

def __exit__(self, *args):
self.end = time.time()
self.secs = self.end - self.start
self.msecs = self.secs * 1000 # millisecs
if self.verbose:
print 'elapsed time: %f ms' % self.msecs

Для того, чтобы его использовать, мы должны обернуть тот кусок кода, время выполнения которого Вы хотите замерить в полученный менеджер контекста. Он запускает измерение времени в начале выполнения блока кода и завершает после выполнения. Вот пример использования этого модуля:
from timer import Timer
from redis import Redis
rdb = Redis()

with Timer() as t:
rdb.lpush("foo", "bar")
print "=> elasped lpush: %s s" % t.secs

with Timer as t:
rdb.lpop("foo")
print "=> elasped lpop: %s s" % t.secs
Я обычно сохраняю вывод в файл для того, чтобы отслеживать изменения производительности с ростом программы.

Построчный тайминг и частота выполнения с
помощью profiler

У Robert Kern есть замечательный проект под названием line_profiler, которым я часто пользуюсь для того, чтобы понять, как быстро выполняется каждая строчка моего кода. Чтобы его использовать — Вам надо сперва установить его при помощи pip:
$ pip install line_profiler

После установки можно использовать модуль “line_profiler” или скрипт “kernprof.py”.

Чтобы использовать этот инструмент для начала Вам надо изменить свой код, обернув функцию, производительность которой Вы хотите измерить, в декоратор @profile. Не беспокойтесь, для этого Вам не надо будет ничего импортировать. Скрипт kernprof.py автоматически добавит всё, что нужно в ваш скрипт в процессе выполнения.
primes.py
@profile
def primes(n):
if n==2:
return [2]
elif n<2:
return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
re turn [2]+[x for x in s if x]
primes(100)
После того, как Вы настроили свой скрипт — используйте usekernprof.py для его запуска.
$ kernprof.py -l -v fib.py
Опция -l указывает kernprof'у добавить декоратор @profile во встроенную область видимости вашего скрипта, а -v указывает kernprof указать информацию о тайминге после выполнения скрипта. Вывод должен выглядеть так:
Wrote profile results to primes.py.lprof
Timer unit: 1e-06 s

File: primes.py
Function: primes at line 2
Total time: 0.00019 s

Line # Hits Time Per Hit % Time Line Contents
==============================================================
2 @profile
3 def primes(n):
4 1 2 2.0 1.1 if n==2:
5 return [2]
6 1 1 1.0 0.5 elif n<2:
7 return []
8 1 4 4.0 2.1 s=range(3,n+1,2)
9 1 10 10.0 5.3 mroot = n ** 0.5
10 1 2 2.0 1.1 half=(n+1)/2-1
11 1 1 1.0 0.5 i=0
12 1 1 1.0 0.5 m=3
13 5 7 1.4 3.7 while m <= mroot:
14 4 4 1.0 2.1 if s[i]:
15 3 4 1.3 2.1 j=(m*m-3)/2
16 3 4 1.3 2.1 s[j]=0
17 31 31 1.0 16.3 while j 18 28 28 1.0 14.7 s[j]=0
19 28 29 1.0 15.3 j+=m
20 4 4 1.0 2.1 i=i+1
21 4 4 1.0 2.1 m=2*i+3
22 50 54 1.1 28.4 return [2]+[x for x in s if x]

Ищите строки с большим значением hits или time. Это те места, где скрипт можно оптимизировать.

Какое количество памяти мы используем?

Теперь, узнав время выполнения нашего кода, давайте посмотрим на объем используемой им памяти. К счастью для нас, Fabian Pedregosa сделал хороший профилировщик памяти.
Для начала установим его при помощи pip:
$ pip install -U memory_profiler
$ pip install psutil
(Установка пакета psutil сильно ускорит работу memory_profiler).
Как и line_profiler, memory_profiler требует декорирования интересующей вас функции при помощи декоратора @profile:
@profile
def primes(n):
...
...
Для того, чтобы посмотреть сколько памяти использует ваша функция запустите скрипт так:
$ python -m memory_profiler primes.py
Вот пример вывода отчёта после завершения работы скрипта:
Filename: primes.py

Line # Mem usage Increment Line Contents
==============================================
2 @profile
3 7.9219 MB 0.0000 MB def primes(n):
4 7.9219 MB 0.0000 MB if n==2:
5 return [2]
6 7.9219 MB 0.0000 MB elif n<2:
7 return []
8 7.9219 MB 0.0000 MB s=range(3,n+1,2)
9 7.9258 MB 0.0039 MB mroot = n ** 0.5
10 7.9258 MB 0.0000 MB half=(n+1)/2-1
11 7.9258 MB 0.0000 MB i=0
12 7.9258 MB 0.0000 MB m=3
13 7.9297 MB 0.0039 MB while m <= mroot:
14 7.9297 MB 0.0000 MB if s[i]:
15 7.9297 MB 0.0000 MB j=(m*m-3)/2
16 7.9258 MB -0.0039 MB s[j]=0
17 7.9297 MB 0.0039 MB while j 18 7.9297 MB 0.0000 MB s[j]=0
19 7.9297 MB 0.0000 MB j+=m
20 7.9297 MB 0.0000 MB i=i+1
21 7.9297 MB 0.0000 MB m=2*i+3
22 7.9297 MB 0.0000 MB return [2]+[x for x in s if x]

Куда утекает память?

Интерпретатор сPython использует счётчик ссылок
для управления памятью. То есть каждый объект содержит счётчик, который увеличивается на 1, когда ссылка на этот объект сохраняется где-то, и уменьшается при удалении этой ссылки. Когда счётчик становится равным нулю интерпретатор cPython знает, что объект больше не используется и можно освободить занимаемую им память.
Утечки памяти могут возникать в вашей программе если где-то сохраняется ссылка на какой-то уже не используемый объект.
Самый быстрый способ поиска таких утечек памяти — использовать восхитительный инструмент objgraph написанный Marius Gedminas. Этот инструмент помогает увидеть количество объектов в памяти и обнаружить разные места в коде, которые содержат ссылки на эти объекты.
Для начала установите objgraph:
pip install objgraph
После этого вставьте в ваш код выражения для вызова отладчика:
import pdb; pdb.set_trace()

Статистика использования объектов

В процессе выполнения Вы можете посмотреть на 20 наиболее используемых объектов в вашей программе:
(pdb) import objgraph
(pdb) objgraph.show_most_common_types()

MyBigFatObject 20000
tuple 16938
function 4310
dict 2790
wrapper_descriptor 1181
builtin_function_or_method 934
weakref 764
list 634
method_descriptor 507
getset_descriptor 451
type 439

Какие объекты были добавлены и удалены?

Для того, чтобы посмотреть, какие объекты были удалены или добавлены между двумя точками сделайте так:
(pdb) import objgraph
(pdb) objgraph.show_growth()
.
.
.
(pdb) objgraph.show_growth() # this only shows objects that has been added or deleted since last show_growth() call

traceback 4 +2
KeyboardInterrupt 1 +1
frame 24 +1
list 667 +1
tuple 16969 +1

Что ссылается на этот «утёкший объект»?

Давайте для примера возьмём простой скрипт:
x = [1]
y = [x, [x], {"a":x}]
import pdb; pdb.set_trace()
Чтобы увидеть, что ссылается на переменную x, вызовите функцию objgraph.show_backref():
(pdb) import objgraph
(pdb) objgraph.show_backref([x], filename="/tmp/backrefs.png")
На выводе Вы получите PNG изображение, сохранённое в/tmp/backrefs.png:
back refrences
Квадрат внизу с красными буквами это и есть интересующий нас объект. Мы можем увидеть, что на него ссылается x один раз и список y три раза. Если x is вызывает утечки памяти, можно воспользоваться этим методом чтобы найти эти забытые ссылки.
Ещё раз: objgraph позволяет нам:
  • посмотреть N популярных объектов в памяти вашей программы
  • посмотреть какие объекты были удалены и добавлены за какой-то период времени
  • посмотреть все ссылки на заданный объект в нашем скрипте

Эффективность против точности

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

Источник

Автор: Ishayahu Lastov

Релиз CFFI 0.2

Вышел релиз CFFI 0.2 (а вскоре ожидается и 1.0). CFFI это способ вызова C из Python. Этот релиз касается только CPython 2.6 или 2.7. Поддержка PyPy будет в ветке ffi-backend, но она ещё не реализована.Поддержку CPython 3.x сделать легко, но нужна для этого помощь.
Пакет доступен на bitbucket и хорошо документирован. Кроме того Вы можете просто установить его из python package index: pip install cffi
В этом релизе:
  • Многочисленные небольшие изменения и поддержка большего количества Си-измов
  • Большая новость: поддержка installing packages которые используют ffi.verify() на машина без компилятора C. Возможно это снимает последнее ограничение, которое удерживало людей от использования CFFI.
  • Некоторые небольшие изменения:
    • отображение между 'wchar_t' и Python юникодом
    • введение ffi.NULL
    • возможно более ясное API для ffi.new(): т.е. для того, чтобы выделить единичный int и получить указатель на него, используйте ffi.new(«int *») вместо старого ffi.new(«int»)
    • и, конечно, множество маленьких исправлений
  • CFFI использует pkg-config при наличии для собственной установки. Это помогает определить место libffi на современных Linux. Поддержка Mac OS/X тоже доступна (смотрите детальные инструкции по установке). Win32 должен работать из коробки. Win64 реально ещё не тестирован.

    Автор: Ishayahu Lastov

    Сериал "Python PDF" – Обзор metaPDF (Перевод)

    Исследуя библиотеки для работы с PDF на Python, я наткнулся на ещё один маленький проект с названием metaPDF. Судя по указанному на сайте этого проекта, metaPDF это лёгкая библиотека для Python, оптимизированная для получения и вставки метаданных, по сути это легковесная обёртка вокруг замечательной библиотеки pyPdf. Я не уверен в том, что эта библиотека будет полена с учётом возможности работы с самой библиотекой pyPdf, так что давайте посмотрим на неё в деле.

    Получение и использование metaPDF

    Процесс установки metaPDF достаточно прост, особенно если использовать easy_install или pip. После этого давайте запустим маленький скрипт для того, чтобы посмотреть, как он работает. Вот пример, основанный на информации с github:
    from metapdf import MetaPdfReader
     
    pdfOne = r'C:UsersmdriscollDocumentsreportlab-userguide.pdf'
    x = MetaPdfReader()
    metadata = x.read_metadata(open(pdfOne, 'rb'))
    print metadata
    Я запускаю скрипт к руководству по Reportlab. Обратите внимание, что в оригинале есть опечатка — там используется “read” для открытия файла. Я полагаю, что он не будет работать, пока Вы сам не откроете файл. В любом случае, вот результат работы этого скрипта:
    {'/ModDate': u'D:20120629155504', '/CreationDate': u'D:20120629155504', '/Producer': u'GPL Ghostscript 8.15', '/Title': u'reportlab-userguide.pdf', '/Creator': u'Adobe Acrobat 10.1.3', '/Author': u'mdriscoll'}
    Я правда не знаю, каким образом изменилось поле «автор» в документе, но я определённо им не являюсь. Так же я не понимаю, откуда в названии ключей берутся прямые слешы. Судя по исходникам, это всё, что может делать эта библиотека. Разочарованы?  Может быть, если мы проявим внимание к этой библиотеке, автор добавит ей функциональности…

    Автор: Ishayahu Lastov