Архив метки: Python

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/

Автор: Евгений Курочкин

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/

Автор: Евгений Курочкин

Строки символов Unicode в Python 2 и Python 3

Винсент: Знаешь, что самое забавное в Европе?
Джулс: Что?
Винсент: Такие маленькие отличия. Там вроде все то же самое, что и здесь, но чуть-чуть отличается.

«Криминальное чтиво»

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

В Python 2В Python 3
строковый литерал получает тип strстроковый литерал получает тип str
тип str представляет собой строку байтовтип str представляет собой строку символов Unicode
для представления строки символов Unicode используется тип unicodeдля представления строки байтов используется тип bytes
для представления строки unicode в нужной кодировке используется метод unicode.encode(кодировка), возвращающий строку байтов strдля представления строки str в нужной кодировке используется метод str.encode(кодировка), возвращающий строку байтов bytes
для преобразования строки байтов str в строку unicode используется метод str.decode(кодировка), возвращающий строку unicodeдля преобразования строки байтов bytes в строку str используется метод bytes.decode(кодировка), возвращающий строку str
системная кодировка по умолчанию, она же кодировка исходных файлов по умолчанию, ASCIIсистемная кодировка по умолчанию, она же кодировка исходных файлов по умолчанию, UTF-8
при записи в файл строки по умолчанию преобразуются в системную кодировку по умолчаниюпри записи в файл строки по умолчанию преобразуются в кодировку, определяемую пользовательскими настройками
идентификаторы в программе содержат только символы ASCII, не могут использовать никаких других букв, кроме латинскихидентификаторы в программе содержат символы Unicode, могут использовать буквы других алфавитов, кроме латинского

Мои эксперименты я ставлю под ОС Windows 7 в стандартной консоли.

Для разминки, несколько манипуляций со строковыми литералами и переменными в Python 2:


>>> s = 'Hello'
>>> s
'Hello'
>>> type(s)

>>> u = s.decode('ascii')
>>> u
u'Hello'
>>> type(u)


И в Python 3:


>>> s = 'Hello'
>>> s
'Hello'
>>> type(s)

>>> b = s.encode('ascii')
>>> b
b'Hello'
>>> type(b)


Теперь посмотрим на кодировки, используемые в Python 2 и Python 3 по умолчанию:

  • Системная кодировка по умолчанию (system default encoding), возвращается функцией sys.getdefaultencoding(). Это кодировка исходных текстов Python по умолчанию, она же используется по умолчанию для кодирования и декодирования строк unicode.
  • Кодировки стандартных потоков ввода, вывода и ошибок — файловых объектов, атрибуты sys.stdin.encoding, sys.stdout.encoding и sys.stderr.encoding, соответственно. Строки символов Unicode, посылаемые в канал вывода, преобразуются в соответствующую кодировку (или в системную кодировку по умолчанию, если атрибут encoding is None).
  • Кодировка имен файлов в файловой системе, возвращается функцией sys.getfilesystemencoding(). Имена файлов, представленные в программе на Python как строки символов Unicode, преобразуются в эту кодировку для взаимодействия с файловой системой (или в системную кодировку по умолчанию, если sys.getfilesystemencoding() возвращает None).
  • Кодировка для текстовых данных, заданная пользовательскими настройками, возвращается функцией locale.getpreferredencoding(False).

Кодировки в Python 2:


>>> import sys, locale
>>> sys.getdefaultencoding()
'ascii'
>>> sys.stdin.encoding
'cp866'
>>> sys.stdout.encoding
'cp866'
>>> sys.stderr.encoding
'cp866'
>>> sys.getfilesystemencoding()
'mbcs'
>>> locale.getpreferredencoding(False)
'cp1251'

Кодировки в Python 3:


>>> import sys, locale
>>> sys.getdefaultencoding()
'utf-8'
>>> sys.stdin.encoding
'cp866'
>>> sys.stdout.encoding
'cp866'
>>> sys.stderr.encoding
'cp866'
>>> sys.getfilesystemencoding()
'mbcs'
>>> locale.getpreferredencoding(False)
'cp1251'

Как видим, системной кодировкой по умолчанию для Pyt
hon 2 является ascii, а для Python 3 — utf-8. И это единственное обнаруженное различие.

Нам, русским, очень «повезло» с обилием кириллических кодировок в Windows. Работая в консоли Windows, по умолчанию мы имеем дело с кириллической кодировкой cp866. Работая с текстовым файлом в Блокноте, по умолчанию мы работаем в кириллической кодировке cp1251. Имена файлов, использующие русские символы, в файловой системе Windows представлены в кодировке mbcs (multi-byte character set), — это двухбайтовая кодировка, которая позволяет представить подмножество символов Unicode (UTF-16?).

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

Фрагмент интерактивного сеанса Python 2:


>>> u = u'Привет world'
>>> type(u)

>>> u
u'u041fu0440u0438u0432u0435u0442 world'
>>> print u, u.encode('cp866')
Привет world Привет world

Что я только что сделал?

Команды вводятся через стандартный входной поток, использующий кодировку cp866 (sys.stdin.encoding). Таким образом, в первом предложении присваивания литерал u'Привет world' преобразуется в строку unicode из кодировки cp866 и полученная строка unicode присваивается переменной u. Далее я проверил тип и значение переменной u и увидел, что русские буквы представлены двухбайтовыми кодами Unicode. Наконец, предложение print посылает в стандартный выходной поток данную строку unicode и строку str, полученную преобразованием строки u в кодировку cp866. Результат вывода обеих строк одинаков, поскольку строка unicode неявно преобразуется при выводе в стандартый выходной поток в кодировку cp866 (sys.stdout.encoding).

В Python 3 получим такой результат (предлагаю интерпретировать его самостоятельно):


>>> s = 'Привет world'
>>> type(s)

>>> s
'Привет world'
>>> print(s, s.encode('cp866'))
Привет world b'x8fxe0xa8xa2xa5xe2 world'

Идем дальше.

В Python 2 для использования в скриптах не-ASCII символов нужно явно указывать кодировку исходного файла, поскольку системная кодировка по умолчанию, ascii, подразумевает использование в файле только символов ASCII. А в Python 3 исходный файл по умолчанию содержит символы Unicode в кодировке utf-8, что позволяет использовать в нем практически любые символы без явного указания кодировки.

Файл hello.py в кодировке UTF-8 для Python 2:


# -*- coding: utf-8 -*-

s = u'Привет world!'
print type(s), s, s.encode('cp866')

Выполню его в консоли Windows:


C:_sandbox> c:Python27python.exe hello.py
Привет world! Привет world!

Файл hello3.py в кодировке UTF-8 для Python 3:


# нас устраивает кодировка по умолчанию utf-8

s = 'Привет world!'
print(type(s), s, s.encode('cp866'))

Выполню его в консоли Windows:


C:_sandbox> c:Python33python.exe hello3.py
Привет world! b'x8fxe0xa8xa2xa5xe2 world!'

Разница с работой скрипта hello.py в том, что теперь к кодировке стандартного потока вывода приводится значение типа str, и строка байтов bytes не интерпретируется как строка читабельных символов.

Следующий скрипт helloname.py демонстрирует ввод и вывод кириллических символов в Python 2, используя для их хранения строки unicode:


# -*- coding: utf-8 -*-

import sys

def uraw_input(prompt):
return unicode(raw_input(prompt.encode(sys.stdout.encoding)), sys.stdin.encoding)

name = uraw_input(u'Привет! Ваше имя? ')
print u'Привет, %s!' % name

Выполню скрипт в консоли Windows:


C:_devGOLD>c:Python27python.exe helloname.py
Привет! Ваше имя? Андрей
Привет, Андрей!

Посмотрим теперь, что происходит при записи строк символов Unicode в файл. Если в Python 2 явно не преобразовывать выводимые в файл строки unicode в строки str с нужной кодировкой, то получим ошибку.


# -*- coding: UTF-8 -*-

with open('hello.txt', 'w') as f:
print 'File encoding:', f.encoding
f.write(u'Привет world!')

Выполню скрипт в консоли Windows:


C:_sandbox> c:Python27python.exe hellofile.py
File encoding: None
Traceback (most recent call last):
File "hellofile.py", line 5, in
f.write(u'╨Я╤А╨╕╨▓╨╡╤В world!')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

При записи в файл Python 2 пытается преобразовать строку unicode в системную кодировку по умолчанию, ascii, поскольку атрибут encoding открытого нами файла имеет значение None. В кодировке ascii нельзя представить кириллические символы, из-за чего и возникает ошибка.

Во избежание ошибок, при записи строк
unicode
в файл нужно явно приводить их к желаемой кодировке:


# -*- coding: UTF-8 -*-

with open('hello.txt', 'w') as f:
print 'File encoding:', f.encoding
f.write(u'Привет world!'.encode('cp866'))

Выполню исправленный скрипт в консоли Windows:


C:_sandbox> c:Python27python.exe hellofile.py
File encoding: None

C:_sandbox> type hello.txt
Привет world!

Аналогичный эксперимент с Python 3 показывает, что строки пишутся в файл в кодировке, определяемой локалью пользователя!


with open('hello3.txt', 'w') as f:
print('File encoding:', f.encoding)
f.write('Привет world!')

Выполняю скрипт в консоли Windows:


C:_sandbox> c:Python33python.exe hellofile3.py
File encoding: cp1251

Выше мы видели, что на русифицированной Windows функция locale.getpreferredencoding(False) возвращает 'cp1251'. Именно с этой кодировкой открывается новый файл по умолчанию, и, как следствие, к ней приводятся строки, записываемые в этот файл.

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


with open('hello31.txt', 'w') as f:
print('File encoding:', f.encoding)
f.write('Привет 世界!')

Выполняю скрипт в консоли Windows:


C:_sandbox> c:Python33python.exe hellofile31.py
File encoding: cp1251
Traceback (most recent call last):
File "hellofile31.py", line 3, in
f.write('Привет u4e16u754c!')
File "c:Python33libencodingscp1251.py", line 19, in encode
return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode characters in position 7-8: character maps to

Кодировка cp1251 не кодирует кандзи!

Хорошая новость в том, что в Python 3, в отличие от Python 2, при открытии файла можно явно указать кодировку файла. В эту кодировку и будут преобразовываться строки str при записи в файл; из этой кодировки будут преобразовываться в str читаемые из файла строки байтов.

Укажу явно кодировку открываемых файлов в скрипте hello32.py:


with open('hello31.txt', 'w', encoding='utf-8') as f:
print('File encoding:', f.encoding)
f.write('Привет world!')

with open('hello32.txt', 'w', encoding='utf-8') as f:
print('File encoding:', f.encoding)
f.write('Привет 世界!')

with open('hello31.txt', encoding='utf-8') as f:
print('File encoding:', f.encoding)
print(f.read())

Выполняю скрипт в консоли Windows:


C:_sandbox> c:Python33python.exe hellofile32.py
File encoding: utf-8
File encoding: utf-8
File encoding: utf-8
Привет world!

Как видим, скрипт пишет в файл и читает из файла строки в кодировке utf-8.

В заключение, экзотический пример кода. Вследствие того, что в Python 3 системной кодировкой по умолчанию является utf-8, в Python 3 можно использовать в идентификаторах не только латиницу, но и другие символы Unicode:


>>> def привет_5_раз(имя):
... for i in range(5):
... print('Привет,', имя)
...
>>> привет_5_раз('Медвет')
Привет, Медвет
Привет, Медвет
Привет, Медвет
Привет, Медвет
Привет, Медвет

Сопровождать такой код и вносить в него изменения интернациональной команде разработчиков будет проблематично!

Проделанные сравнительные эксперименты не дали мне достаточно оснований, чтобы решительно встать на одну из сторон в священной войне между защитниками Python 2 и энтузиастами Python 3 :). Хотя Unicode-ориентированность Python 3 и то, как это сказывается на прикладном программировании, мне нравится.

Автор: Andrei Trofimov
Дата публикации: 2014-07-10T17:55:00.000+11:00

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

Автор: Евгений Курочкин

Часть 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

Автор: Евгений Курочкин