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

Машинное обучение. Начало.

Предисловие

Итак, начнем цикл статей про машинное обучение) В основном он будет основан на материале из различных книг, но основная идея цикла — это подача!) А подавать будем, попытавшись написать реальный проектик и попрактиковавшись в различных интересных штукенциях)

В поисках данных

Итак, нам нужны данные — много данных!) И желательно интересных. Не знаю, как вы, но я долго думать не стал и воспользовался сайтом kinopoisk.ru, надеюсь сильно бить не будут, ведь мы будем взращивать их будущие кадры(т.е. Вас!)))

Начнем с конфигурации. В разных книгах примеры данных хранятся во всяких rss, plain files и прочей не современной чепухе) Мы то с Вами живем в то время, когда даже слово sql стыдно произносить в приличном обществе, без приставки no!) Но мы произнесем — PostgreSQL! Начнем с установки(ищем, где ищется)) а закончим созданием таблички votes.

CREATE TABLE votes
(
id serial NOT NULL, --уникальный идентификатор
user_id integer NOT NULL, --id пользователя с сайта
film_id integer NOT NULL, --id фильма с сайта
vote smallint NOT NULL, --оценка фильму пользователем от 1 до 10
CONSTRAINT vote_id PRIMARY KEY (id)
)

Объяснять столбцы не буду!!!))) Если все получилось, давайте добавим данных. Бить меня не нужно, можно только корректировать, я для Вас написал следующее чудо, запускаем и…не ждем, а приступаем к работе, он и сам справится, за несколько лет)) У меня на момент написания этих строк обработано 9000 пользователей из как минимум 4722023 (спалился:)

import re, psycopg2
from lxml.html import parse
from lxml.cssselect import CSSSelector

def get_user_voites():

user_start_num = 200
user_end_num = 4722023

try:
conn=psycopg2.connect("dbname='postgres' user='postgres' password='120789' host='localhost'")
cur = conn.cursor()
page_num_sel = CSSSelector('div.pagesFromTo')

for user_num in xrange(user_start_num, user_end_num):

vote_film_list = []
loop_bool = True
page_num = 1

while loop_bool:
try:
page = parse('http://www.kinopoisk.ru/user/%s/votes/list/ord/date/page/%s/' % (user_num, page_num))

vote_count_div = int(page_num_sel(page)[0].text.split()[-1])

items = page.xpath("//div[contains(@class, 'item')]")
for item in items:
try:
film_div = item.find("div[@class='info']").find("div[@class='nameRus']").find("a")

vote_num = int(item.find("div[@class='num']").text)
film_id = re.search('film/(d)+/$', film_div.values()[0]).group(0)[5:-1]
film_name = film_div.text
vote = item.find("div[@class='vote']").text

if vote and film_id:
vote_film_list.append({'vote': vote, 'user_id': user_num, 'film_id': film_id})
except:
pass

if vote_num <= vote_count_div:
page_num += 1

print vote_num, page_num, vote_count_div, len(vote_film_list)
cur.executemany("""INSERT INTO votes(user_id, film_id, vote) VALUES (%(user_id)s, %(film_id)s, %(vote)s)""", vote_film_list)
conn.commit()
vote_film_list = []

except Exception, e:
print e
loop_bool = False


except Exception, e:
print "I am unable to connect to the database.", e

finally:
if conn:
conn.close()

Автор: Pavel Petropavlov

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/

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

Часть 4. py.test debug

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

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

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

Строки символов 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

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

Часть 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 — выполнить лишь заданный тест

—maxfail= — задает максимальное число фейлов, тест удобен при дебаге или если заранее известно что результат тестов будет один и тотже
-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:Python27python.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

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