пятница, 15 августа 2014 г.

exrex генератор данных по регулярным выражениям

Всем привет!

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

API крайне простой
count - возвращет количество возможных вариаций
parse - парсер регулярных выражений
getone - возвращает псевдо-рандомное значение
generate - возвражает генератор со всеми вариантами

Примеры использования

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

>>> print exrex.getone('[A-Z][a-z]{1,10} [A-Z][a-z]{1,10}')
Boris Yeltsin

>>> print exrex.getone('\+[0-9]\([0-9]{3}\) [0-9]{3}-[0-9]{2}-[0-9]{2}')
+5(777) 790-68-69

>>> print [r for r in exrex.generate('|'.join(str(i) for i in range(5)))]
[u'0', u'1', u'2', u'3', u'4']

>>> exrex.count('[0-9]{1}')
10

>>> print exrex.parse('[0-9]{1,100}')
[('max_repeat', (1, 100, [('in', [('range', (48, 57))])]))]

Есть также возможность запуска из консоли

>>> python -m exrex -r [0-9]{2}
31
Возвращает псевдо-рандомное число из 2 цифр

За более подробной информацией в ссылки.


Ссылки

https://github.com/asciimoo/exrex
http://exrex.readthedocs.org/

пятница, 11 июля 2014 г.

python кодировка исходников

Всем привет!

Я думаю каждый из нас ловил что-то подобное:

 File "python_coding_style.py", line 2
SyntaxError: Non-ASCII character '\xd0' in file python_coding_style.py on line 3, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

Проблема в том что интерпретатор не знает в какой кодировке у нас файлы исходников. Проблема решается явным указанием их кодировки:
# coding: utf8

Чаще на практике встречаются следующие варианты

# -*- coding: utf8 -*-
# coding: utf8
# coding=utf8

В PEP-0263 декларируется, что для определения кодировки исходников годится все что подпадет под регулярку coding[:=]\s*([-\w.]+)
Например, #This Python file uses the following encoding: utf-8 !!!

Ссылки

http://legacy.python.org/dev/peps/pep-0263/

четверг, 10 июля 2014 г.

Часть 4. py.test debug

Мы уже рассмотрели основные принципы написание тестов, но один важный момент упустили, а именно дебаг тестов.


Использование print

Часто люди пользующие питон дебажат свои скрипты с помощью использования принтов. По умолчанияю pytest пишет весь stout и stderr. В предыдущем посте эта тема рассматривалась. Для отключения этой особенности нужно использовать параметр --capture или маску -s. 
Посмотрим на небольшой (надуманный) пример:

# use_debug.py
@py.test.mark.parametrize("v", range(1, 5))
def test_parity(v):
    print("Value: %d" % v)
    assert v % 2

>>>py.test use_debug.py -s -v --tb line

============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
plugins: teamcity-messages, rerunfailures
collected 4 items

use_debug.py:40: test_parity[1] Value: 1
PASSED
use_debug.py:40: test_parity[2] Value: 2
FAILED
use_debug.py:40: test_parity[3] Value: 3
PASSED
use_debug.py:40: test_parity[4] Value: 4
FAILED

================================== FAILURES ===================================
c:\JOB\3_TestSolution\Blog\use_debug.py:43: assert (2 % 2)
c:\JOB\3_TestSolution\Blog\use_debug.py:43: assert (4 % 2)
===================== 2 failed, 2 passed in 0.03 seconds ======================

Как видим в консоли напечатались входные значения. 



Автовключение дебага

Для этого предусмотрен флаг --pdb. Если тест пофелился, то py.test прогонит его на тех же данных, но уже с включенным питоновским дебагером pdb.

>>>py.test use_debug.py -s -v --tb line --pdb

platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
plugins: teamcity-messages, rerunfailures
collected 4 items

use_debug.py:64: test_parity[1]
>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>
> c:\job\3_testsolution\blog\use_debug.py(67)test_parity()
-> assert v % 2
(Pdb)

Брейкпоинты


Из коробки py.test предоставляет возможность в коде явно указать брекпоинт.

# use_debug.py
@py.test.mark.parametrize("v", range(1, 5))
def test_parity(v):
    py.test.set_trace()
    assert v % 2

При каджом вызове тестовой функции test_parity в месте устанвки брейкпоинта py.test будет запускать pdb

>>>py.test use_debug.py -s -v --tb line

use_debug.py:62: test_v[1]
>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>
> c:\job\3_testsolution\blog\use_debug.py(65)test_v()
-> assert v % 2
(Pdb)


Ссылки

http://pytest.org/latest/usage.html#dropping-to-pdb-python-debugger-on-failures
http://pytest.org/latest/usage.html#setting-a-breakpoint-aka-set-trace

четверг, 26 июня 2014 г.

python copy/deepcopy

Всем привет!

Продолжаем ломать копья об питоновскую парадигму изменяемых/неизменяемых объектов.
Разберем поверхностное и глубокое копирование словаря.

>>>s = {"str": "1", "list": [1, 2], "dict": {"first": "test1"}}
>>>f = s
>>>f['second'] = '2'

>>>print s

Надеюсь понятно какой вывод будет в консоли. Если нет, то смотрим ссылки.
Для того чтобы не ссылаться на один объект, нужно скопировать словарь s.

Но метод copy обладает неприятной особенностью, он поверхностно копирует словарь.
Смотрим на пример (s из предыдущего примера).

>>>f = s.copy()
>>>f['second'] = '3'
>>>f['dict']['third'] = '3'
>>>print s

{'dict': {'third': '3', 'first': 'test1'}, 'list': [1, 2], 'str': '1'}

Проблема решается использованием модуля copy.

>>>import copy

>>>f = copy.deepcopy(s)
>>>f['second'] = '3'
>>>f['dict']['third'] = '3'
>>>print s

>>>print f

Как работает copy.deepcopy

Начнем с аргументов

deepcopy(x, memo=None, _nil=[])

x - копируемый объект. Копируемым объектом может быть любой стандартный тип данных, а также кастомных объектов.
memo - словарь, в котором будут сопоставлены id созданных объектов и их значения. По-умолчанию None(если не понятно почему, то смотрим в ссылки [3]). При желании можно реализовать собственный "копир", если есть потребность в специфичном поведении копирования.
_nil - вспомогательный атрибут, используемый во время копирования

Пара примеров для понимания работы deepcopy

>>>s = {"i": 1, "s": "2", "l": [1, 2, 3]}
>>>memo = {}
>>>d = copy.deepcopy(s, memo)

>>>print id(s)
44501728

>>>print id(d)
44502304

>>>print d
{'i': 1, 's': '2', 'l': [1, 2, 3]}

>>>print id(memo)
44502016

>>>print memo

{44501728: {'i': 1, 's': '2', 'l': [1, 2, 3]}, 31711104: '2', 31565540: 2, 44502016: [1, 'i', '2', 's', 2, 3, [1, 2, 3], 'l', {'i': 1, 's': '2', 'l': [1, 2, 3]}], 31266408: 'i', 31565552: 1, 31565528: 3, 31268304: 's', 31708488: 'l', 44497976: [1, 2, 3]}

Словарь memo хранит id всех копируемых объектов, их значения., а также свой id.

>>>dt = copy.deepcopy(s, memo)

>>>print id(dt)
44502304

print dt
{'i': 1, 's': '2', 'l': [1, 2, 3]}

Как видим, deepcopy возвращает уже копировавшийся объект d

>>>s1 = {"i": 1, "s": "2", "l": [1, 2, 3]}
>>>dth = copy.deepcopy(s1, memo)
>>>print id(s1)
44516240

>>>print(id(dth))
44517248

>>>print(dth)
{'i': 1, 's': '2', 'l': [1, 2, 3]}

>>>print(id(memo))
44502016

>>>print(memo)
{44501728: {'i': 1, s': '2', 'l': [1, 2, 3]}, 31711104: '2', 44516240: {'i': 1,  's': '2', 'l': [1, 2, 3]}, 31565540: 2, 44502016: [1, 'i', '2', 's', 2, 3, [1, 2, 3], 'l', {'i': 1, 's': '2', 'l': [1, 2, 3]}, [1, 2, 3], {'i': 1, 's': '2', 'l': [1, 2, 3]}], 31266408: 'i', 31565552: 1, 31565528: 3, 44498856: [1, 2, 3], 31268304: 's', 31708488: 'l', 44497976: [1, 2, 3]}

Реализация deepcopy

Сначала происходит инициализация memo, если словарь не задан. Если memo задан тогда проверяется наличие id копируемого объекта, если такой объект уже есть в memo, то происходит его возврат.

d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
    return y

Т.е. если после копирования нужно все время получать новый объект, то тогда поле memo нужно оставлять по-умолчанию (это мы видели на примере выше).

Далее определяется тип копируемого объекта и если он есть в описании стандартных типов, то вызывается функция копирования

copier = _deepcopy_dispatch.get(cls)
if copier:
    y = copier(x, memo)

Например, для словаря вызывается _deepcopy_dict

def _deepcopy_dict(x, memo):
    y = {}
    memo[id(x)] = y
    for key, value in x.iteritems():
        y[deepcopy(key, memo)] = deepcopy(value, memo)
    return y

Код довольно простой, создается новый словарь, в цикле копируются значения. Обратите внимание копирование идет - рекурсивным вызовом deepcopy, сделано это на случай того, если словарь составной и содержит вложенные объекты.
После успешного копирования обновляется словарь memo и вызывается функция _keep_alive, которая записывает в memo, сам копируемый объект, т.е. если вызвать memo[id(memo)], то получим список всех объектов, которые копировались.

Ссылки
[1]https://docs.python.org/2/library/stdtypes.html#dict.copy
[2]https://docs.python.org/2/library/copy.html
[3]http://evgenqa.blogspot.ru/2014/05/blog-post_23.html


serchcode

Для тех кто в танке и не читает хабр прикольный поисковик по коду опен сорс https://searchcode.com/


Нашел 193 результата по запросу putIn и 760 по medveDev.

понедельник, 16 июня 2014 г.

The Architecture of Open Source Applications

http://aosabook.org/en/index.html - неплохие статьи об архитекруре приложений с открытым исходным кодом. git, mercurial, nginx, zeromq и проч. А также есть что почитать для тех кто пишет на питоне - Twisted, SQLAlchemy.

Часть 3. Совсем немного о py.test.exe

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

>>> py.test -h

Команды формирования вывода

-q - уменьшает детализацию информации о выполнении теста

-v - увеличивает детализацию о выполнении теста

--capture - параметр для управлением захвата вывода stdout/stderr. Принимает одно из значений fd|sys|no. Более подробно в ссылках
-s - маска для --capture=no. Параметр удобен, если нужно убрать мусор (например, дебажные принты) из информации о выполнившихся тестах.

--tb - отвечает за детализацию вывода трейса. Одно из long/short/line/native/no

Команды запуска 

-k <test name> - выполнить лишь заданный тест

--maxfail=<fail count> - задает максимальное число фейлов, тест удобен при дебаге или если заранее известно что результат тестов будет один и тотже
-x - маска дял --maxfaile=1

--pdb - стартует питоновский дебагер для отладки ошибок. Удобен для отладки тестов (подробнее в одном из следующих постов о дебаге тестов)

Комманды для настройки

--color (yes|no|auto) - цветной вывод в консоли или нет

Совсем небольшой пример

# use_tool.py
import py.test

@py.test.mark.parametrize('num', [i for i in range(5)])
def test_func(num):
    print('\ntest_func()')
    assert num % 2

def test_fail():
    print('bla, bla, fail')
    assert False

Выполняем в консоли
>>>py.test use_tool.py -k "test_func" -v -s -x --tb=line

============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
plugins: teamcity-messages
collected 6 items

use_tool.py:37: test_func[0]
test_func()
FAILED

================================== FAILURES ===================================
use_tool.py:40: assert (0 % 2)
!!!!!!!!!!!!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!!!!!!!!!!!!
===================== 1 tests deselected by '-ktest_func' =====================
=================== 1 failed, 1 deselected in 0.04 seconds ====================

Ссылки

[1] http://pytest.org/latest/capture.html
[2] http://pytest.org/latest/customize.html?highlight=maxfail
[3] http://pytest.org/latest/usage.html

среда, 11 июня 2014 г.

Часть 2. Фикстуры pytest

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

Что такое test fixtures

В тестировании обычно под этим понимают флоу теста, его контекст.
В py.test предусмотрены следующие типы фикстур: встроенные фикстуры, вспомогательные функции, декораторы


setup/teardown

Часто перед запуском теста нужно провести базовые подготовления. Например, это может быть настройка окружения, запуск тестовых сервисов, подготовка тестовых данных, инициализация соединения с базами и проч. Для выполнения этих операций в py.test входит небольшой набор вспомогательных функций - несколько пар setup/teardown.

setup_module(module)
teardown_module(module)

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

setup_function(function)
teardown_function(function)

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

#fixtures.py
def setup_module(module):
    print('\nsetup_module()')


def teardown_module(module):
    print('\nteardown_module()')


def setup_function(function):
    print('\nsetup_function()')


def teardown_function(function):
    print('\nteardown_function()')


def test_first():
    print('\ntest_first()')


def test_second():
    print('\ntest_second()')


class TestMyClass:
    @classmethod 
    def setup_class(cls):
        print ('\nsetup_class()')

    @classmethod 
    def teardown_class(cls):
        print ('teardown_class()')

    def setup_method(self, method):
        print ('\nsetup_method()')

    def teardown_method(self, method):
        print ('\nteardown_method()')

    def test_first(self):
        print('test_first()')

    def test_second(self):
        print('test_second()')



>>>py.test -s -v fixtures.py

============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
collected 4 items

fixtures.py:17: test_first
setup_module()

setup_function()

test_first()
PASSED
teardown_function()

fixtures.py:21: test_second
setup_function()

test_second()
PASSED
teardown_function()

fixtures.py:40: TestMyClass.test_first
setup_class()

setup_method()
test_first()
PASSED
teardown_method()

fixtures.py:43: TestMyClass.test_second
setup_method()
test_second()
PASSED
teardown_method()
teardown_class()

teardown_module()
========================== 4 passed in 0.02 seconds ===========================


Встроенные фикстуры

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

>>>py.test --fixtures

Для ознакомления в данном посте рассмотри одну из встроенных фикстур tempdir
temdir
Очень полезная вещь, предназначена для работы с темповыми данными. Т.е. в темповой директории можно хранить любые временные файлы. Например, это могут быть логи тестируемых программ или какой-то контекcт теста, который может понадобиться для воспроизведения бага.

Использовать крайне просто

#fixtures.py
def test_file(tmpdir):
    f = tmpdir.mkdir('logs').join('test.log')
    print(tmpdir.strpath)
    f.write('bla bla')

    with open(f.strpath, 'r') as fp:
        print(fp.read())

>>>py.test -s -v fixtures.py
============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
collected 1 items

fixtures.py:161: test_file \temp\pytest-76\test_file0
bla bla
PASSED

========================== 1 passed in 0.03 seconds ===========================

Полезные фикстуры

Для итерации по входным данным можно использовать полезный декоратор py.test.mark.parametrize

#fixtures.py
@py.test.mark.parametrize('num', [i for i in range(2)])
def test(num):
    assert num % 2


py.test -s -v fixtures.py
============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
collected 2 items

fixtures.py:182: test[0] FAILED
fixtures.py:182: test[1] PASSED

================================== FAILURES ===================================
___________________________________ test[0] ___________________________________

num = 0

    @py.test.mark.parametrize('num', [i for i in range(2)])
    def test(num):
>       assert num % 2
E       assert (0 % 2)

fixtures.py:184: AssertionError
===================== 1 failed, 1 passed in 0.02 seconds ======================

Кастомизация

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

Для определения собственных инструментов в py.test предусмотрены 

#fixtures.py
@pytest.fixture(scope="function")
def before(request):
    print('\nbefore()')

    def after():
        print("\nafter()")
    request.addfinalizer(after)


def test_func(before):
    print("\ntest_func()")



>>>py.test -s -v fixtures.py
============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
collected 1 items

fixtures.py:14: test_func
before()

test_func()
PASSED
after()
========================== 1 passed in 0.01 seconds ===========================

Вывод

1. Прежде всего это создано для удобства (как собственно и весь py.test)
2. Сокращает время разработки теста
3. делает тест прозрачнее
4. Уменьшает количество копипаста


Ссылки

http://pytest.org/latest/
http://en.wikipedia.org/wiki/Test_fixture
http://pythontesting.net/

среда, 4 июня 2014 г.

Понимаем UnboundLocalError

Опять же на programmingwats.tumblr.com наткнулся на небольшую особенность питона.

my_str_1 = "1: outside of func"
my_str_2 = "2: outside of func"
def func_1():
    my_str_1 = "1: inside the func"
    my_str_2 = "2: inside the func"
    def func_2():
        print(my_str_1)
        print(my_str_2)
        my_str_1 = "1: inside the class"
    func_2()

func_1()
prints:
Traceback (most recent call last):
  File “<stdin>”, line 1, in <module>
  File “<stdin>”, line 8, in func_1
  File “<stdin>”, line 5, in func_2
UnboundLocalError: local variable ‘my_str_1’ referenced before assignment

В принципе на эту тему написано довольно много постов и погуглив можно спокойно найти объяснение. Но т.к. в свое время эта особенность питона попила у меня много кровушки я решил написать небольшой пост.

В чем причина такого поведения?
Причина заключается в том что питон преобразует переменную my_str_1 в локальную для func_2 и во время вызова функции print получает на вход не инициализированную переменную.
Решения проблемы тривиальные - либо убрать print, либо изменить название переменной, или объявить my_str_1 как глобальную.

Кто хочет более подробно ознакомиться с причинами такого поведения см ссылки, особенно с [2]

Ссылки

[1]https://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value
[2]http://eli.thegreenplace.net/2011/05/15/understanding-unboundlocalerror-in-python/
[3]http://programmingwats.tumblr.com/page/2

вторник, 3 июня 2014 г.

python 2 утечка переменных цикла

Проглядывая programmingwats.tumblr.com нашел небольшую особенность python 2.

Посмотрим на пример:

>>>x = "top"
>>>print(list("a" for x in (1,2)), x)
>>>print(["a" for x in (1,2)], x)

([‘a’, ‘a’], ‘top’)
([‘a’, ‘a’], 2)

Это бага питона, связанная с тем что локальная переменная x, используемая при создании списка становилась доступной в родительском пространстве имен. Думаю понятно чем грозит данная бага.
Данная бага поправлена в python 3.


Ссылки

https://docs.python.org/3/whatsnew/3.0.html#changed-syntax
http://programmingwats.tumblr.com/page/2
http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/tutorials/key_differences_between_python_2_and_3.ipynb?create=1

пятница, 23 мая 2014 г.

изменяемый объект в аргументах

Тема замусолена до невозможности, но не удержался увидев код Request

def func(f, l=[]):
    l.append(f)
    return l

func является потенциально опасной. Смотрим что происходит при её использовании

>>> print(func(1))
[1]
>>> print(func(2))
[1, 2]


l ссылается на один и тот же объект, который все время и изменяется (вспоминаем концепцию python)

Решение очевидное - определить список в функции

Такое решение используется и в популярном фреймворке request

class Request(RequestHooksMixin):
    def __init__(self,
        method=None,
        url=None,
        headers=None,
        files=None,
        data=None,
        params=None,
        auth=None,
        cookies=None,
        hooks=None):

        # Default empty dicts for dict params.
        data = [] if data is None else data
        files = [] if files is None else files
        headers = {} if headers is None else headers
        params = {} if params is None else params
        hooks = {} if hooks is None else hooks

Ссылки

https://github.com/kennethreitz/requests/blob/master/requests/models.py

среда, 21 мая 2014 г.

Часть 1. pytest. Первый взгляд

Каждый девелопер, для которого слова tdd, unittest, ci являются не пустыми, слышал про тестовый фраймворк pytest (он же py.test). По каким-то причинам этот тестовый фраймвор слабо освещен на просторах рунета. В ряде статей постараюсь заполнить этот пробел. Думаю, познакомившись с pytest поближе вы забудете про стандартный unittest.




Преимущества pytest

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

Ставим


pip install -U pytest
Или через easy_install
easy_install -U pytest

Или из пакета
Качаем пакет http://pypi.python.org/pypi/pytest

python pytest/setup.py install

Единственно при оффлайн установке стоит учесть что для работы pytest нужно еще 2 пакета - colorama и py

Пробуем


Забегая в перед скажу что все тестовые функции должны начинаться с префикса test_*.

# tests.py
def test_first_our_passed():
    assert True

def test_first_our_failed():
    assert False

Запустим тест в консоли py.test test.py

============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2
collected 2 items

tests_first.py .F

================================== FAILURES ===================================
____________________________ test_first_our_failed ____________________________

    def test_first_our_failed():
>       assert False
E       assert False

tests_first.py:6: AssertionError
===================== 1 failed, 1 passed in 0.13 seconds ======================

Из коробки получаем довольно информативный вывод


Структура проекта


Есть два устоявшихся принципа по организации тестового проекта
Все тесты находятся в папке test рядом с пакетами проекта

/myproj
    __init__.py
    project.py
/test
    test_func_1.py
    test_func_2.py

Второй вариант - папка с тестами находится в самом пакете проекта

/myproj
    __init__.py
    project.py
    /test
        test_func_1.py
        test_func_2.py

Разница не большая, а скорее дело вкуса.



Структура тестов


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

/myproj
    __init__.py
    project.py

# project.py
def pp(val):
    return val + 1

Первый принцип - все тесты описаны в виде тестовых функциях

def test_positive():
    assert pp(1) == 2

def test_negative():
    assert pp(1) == 1

Второй принцип - все тесты описаны в виде методов тестового класса

class TestProject:
    def test_positive(self):
        assert pp(1) == 2

    def test_negative(self):
        assert pp(1) == 1

На тестах данные принцип практически не сказывается.


Варианты запуска

Из коробки в pytest предусмотрено несколько вариантов запуска тестов.

Через специальную утилиту py.test.exe

py.test.exe test_project.py

В консоли через питон 

python -m "pytest.main('test directory')"

Вызов pytest в коде

if __name__ == '__main__':
    pytest.main('test_project.py')

+ никто не отменял сабпроцесс;)

Ссылки

http://pytest.org



Ельцин и питон

В доке питона по sqlite3 обнаружил Ельцина

import sqlite3

con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("create table people (name_last, age)")

who = "Yeltsin"
age = 72

# This is the qmark style:
cur.execute("insert into people values (?, ?)", (who, age))

# And this is the named style:
cur.execute("select * from people where name_last=:who and age=:age", {"who": who, "age": age})

print cur.fetchone()

Судя по дате создания модуля sqlite - это именно Борис

Ссылка


https://docs.python.org/2/library/sqlite3.html#cursor-objects

вторник, 20 мая 2014 г.

{} vs dict()


В питоне предусмотрена возможность создать словарь двумя способами - через фигурные скобки {} и через конструктор dict(). В чем разница и что лучше использовать в коде?



Код

a = dict(one=1, two=2, three=3)
b = {'one': 1, 'two': 2, 'three': 3}

Пример наглядно демонстрирует что при статическом определении атрибутов количество кода практически одинаковое (скобки больше всего лишь на 2 символа). 


Инициализация

Конструктор словаря предлагает расширенный функционал для инициализации словаря.

d = dict((str(v),v) for v in range(10))
dict(zip(['one', 'two', 'three'], [1, 2, 3]))


Скорость работы

Выполним в консоли следующие выражения

>>python -m timeit "{}"
10000000 loops, best of 3: 0.034 usec per loop

>>python -m timeit "dict()"

10000000 loops, best of 3: 0.127 usec per loop

Почему такая существенная разница в скорости? Все просто, смотрим что выдает диассемблер

>>import dis

>>def d(): t = {}

>>dis.dis(d)


  1           0 BUILD_MAP                0

              3 STORE_FAST               0 (t)
              6 LOAD_CONST               0 (None)

              9 RETURN_VALUE


>>def d(): t = dict()

>>dis.dis(d)

  1           0 LOAD_GLOBAL              0 (dict)

              3 CALL_FUNCTION            0
              6 STORE_FAST               0 (t)
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE


При вызове конструктора dict() выполняется большее количество инструкций питона.

Вывод

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

PS. Все выводы актуальны и для списков


Ссылки

https://docs.python.org/2/library/stdtypes.html#mapping-types-dict
http://stackoverflow.com/questions/6610606/dict-literal-vs-dict-constructor-any-preferred

пятница, 18 апреля 2014 г.

ограничение lambda

Ограничение на использование лямбда-выражений

lambda x, y: assert x==y

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

Можно режить конкретную проблему


def assert_eq(x, y): 
    assert x == y

f = lambda x, y: assert_eq(x, y)

Но особого смысла в этом нет, т.к. мы лямбдой просто вызываем новую функцию assert_eq

Ссылки