You're viewing all posts tagged with python

Преобразование строк из/в Unicode

В дополнение к посту о funny characters.

Преобразование utf-8 в cp866 (в ASCII кодировку)

    >>> u'ы'.encode('cp866')
    '\xeb'

Преобразование cp866 в utf-8

    >>> 'ы'.decode('cp866')
    u'\u044b'

decode - декодирует последовательность байтов в юникод, encode - кодирует юникод в последовательность байтов. Последовательность байтов всегда должна интерпретироваться с учетом кодировки - в примере получаются последовательности байтов в кодировке cp866. :)

PS. А в Python 3 концепция работы с юникодом круче, как обещает документация :)

Cтрока (str) - всегда UTF-8, то есть действительно строка, в универсальной кодировке. А все остальное - последовательности байт bytes, про которые нужно знать, в какой они кодировке, если нужно их перевести в строку.

Опасаться funny characters

Предыстория

Во время написания iКучи я столкнулся с несовершенством мира. У меня появилась такая задача: по ссылке узнать title страницы. Теоретически, задача не сложная, но проявились нюансы, связанные с кодировками.

Чтобы сразу проверять работу парсера, я тестировал все в консоли. И иногда print вылетал с UnicodeEncodeError. Это явление известно как funny characters.

Пример

  • символ ‘ы’ имеет код U+044B
  • символ ‘©’ имеет код U+00A9
    >>> a = u'ы'
    >>> a
    u'\u044b'
    >>> b = u'\u00a9'
    >>> b
    u'\xa9'

Пробуем напечатать:

    >>> print a
    ы
    >>> print b
    ...
    UnicodeEncodeError: 'charmap' codec can't encode character u'\xa9' in position 0: character maps to <undefined>
    >>>

Пробуем сложить сроки и записать в файл:

    >>> f = open('test.txt', 'w')
    >>> f.write((a + b).encode('utf-8'))
    >>> f.close()

Работает!

Выводы

Даже не знаю. Похоже, что этот явление проявляется только в консоли, при печати. В общем, это что-то неведомое, чего стоит опасаться, на всякий случай.

Вот здесь предлагается “вычищать” эти символы из текста: http://code.activestate.com/recipes/546517-accent2htmlcodepy-convert-accents-and-special-char

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

Почитать для знания

http://www.joelonsoftware.com/articles/Unicode.html http://www.stereoplex.com/2009/nov/8/python-unicode-and-unicodedecodeerror/

Контрибьюшн

В библиотеке django-treebeard есть теперь мой небольшой кусочек кода :) Я доволен :)

Кстати, я сначала прицепил к своему сниппету лицензию BSD, но когда Густаво посмотрел код и решил его внедрить к себе, то попросил поменять лицензию на Apache2 чтобы prevent a headaches. Я был конечно не против, но я думал, что эти лицензии разрешают все и совместимы поэтому. Оказывается, не совсем.

Python Goodies

Это специальный пост для тех, кто не попробовал ещё Python в деле :) И особенно специально для Хаафа.

Вот такие плюсы есть у Python по сравнению с чем-нибудь ещё.

0. Painless.

В общем - главное :))) Лаконично и работает.

1. Интерактивный интерпретатор.

То есть, ставишь себе Python, запускаешь в командной строке python - и можно уже играться.

>>> a,b,c = 1,3,4
>>> max(a,b)
3

2. Мощный набор встроенных методов и типов, паттернов, базовых синтаксических конструкций

“Из коробки”, без всяких инклудов-импортов, есть списки, хэш-мэпы, min/max’ы, “декораторы”, лямбда-функции, преобразования кодировок, работа с файлами.

Примеры без экзотики:

def is_str_quoted(var):
    return var[0] == var[-1] and var[0] in ("'", '"')

var[-1] - последний элемент в var. Гениально. Отрицательная индексация - это шедевр! :)

“Нарезка” строк, списков по индексу делается универсальной конструкцией, вот так:

>>> a = 'Little Johny'
>>> 'Big ' + a[7:-1]
'Big John'

Или вот - применить ко всем элементам списка функцию и вернуть список результатов:

>>> add_ly = lambda x: x + 'ly'
>>> map(add_ly, ['rapid', 'kind', 'identical'])
['rapidly', 'kindly', 'identically']

Пояснение: lambda x: x + ‘ly’ - это функция, которая принимает x и возвращает x + ‘ly’, анонимные функции их ещё называют или лямбда-функции.

Очень часто - просто не нужны классы, хватает встроенного типа dict, чтобы представить какой-то объект:

>>> response = {'code': 1, 'message': "Hi, I'm ok."}
>>> print response['code']
1

И вот - функция с произвольным набором параметров:

def show_args(*args, **kwargs):
    times = kwargs['times']
    for i in range(times):
        for a in args:
            print a

>>> show_args('Rabbits', 'like', 'fuel', times=3)
Rabbits
like
fuel
Rabbits
like
fuel
Rabbits
like
fuel

Неименованные параметры - принимаются как список, именованные - как обычный dict (хэш-мэп).

Или вот - объявление класса, конструирование, наследование - ничего лишнего.

class GenericBoss:
    def __init__(self, owner=None):
        self.owner = owner

    def say_what_should_we_do(self):
        print self.owner.say_what_should_we_do()

class BigBoss(GenericBoss):
    def say_what_should_we_do(self):
        print "Work!"

>>> GenericBoss(owner=BigBoss()).say_what_should_we_do()
'Work!'

3. Библиотеки

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

Хорошие - это вот, например:

>>> import feedparser
>>> d = feedparser.parse("http://rudi-kodoblog.05bit.com/rss")

или

from PIL import Image
im = Image.open("bride.jpg")
im.rotate(45).save("bride_rot_45.jpg")

Для GUI, интерактивной графики - поменьше конечно, но нарождается. Есть bind’ы к C/C++ библиотекам. Вот такое даже есть - pythonogre.com.

4. Роман Ворушин и Анатолий Востряков программируют на питоне.

А это вам не хухры мухры.

:)

Django # Грабли с отловом exceptions в декорированных views # Debug output for decorated views fails

Нашел грабли :) Надо будет сделать баг-репорт, но пока - просто workaround.

Грабли - если view задекорирован и в нем возникает ошибка, то джанга не всегда правильно обрабатывает traceback - дебажный вывод не показывает, точнее слетает в самом дебажном выводе :)

Пример:

from decorators import render_to

@render_to('main.html')
def main_view(request):
    # тут что-то пошло не так
    raise Exception('AAA!!!')
    return context

Так вот, чтобы дебажный вывод работал правильно (как обычно), render_to должен быть объявлен:

  • либо в этом же модуле view
  • либо в другом package’е

Если render_to импортится из того же package, что и view, то дебажный вывод сходит с ума и не может правильно определить, в каком файле произошла ошибка.

То есть, вот так - сломается: есть views.py, где лежат views, и рядом decorators.py, в котором объявлен render_to.

Работает - так (main_view должен быть не в myproject.utils :)

from myproject.utils.decorators import render_to

# ...

Python # Преобразование строки или чего-то в число # Safe string or something to int

def safe_int(val):
    try:
        return int(val)
    except (TypeError, ValueError):
        return 0

Пожалуй, алмазный сниппет :)

Нужен, если на входе ожидается непредсказуемое значение, но на выходе требуется получить значение в виде int во что бы то ни стало, так, чтобы программа не ломалась. Если требуется, чтобы ломалась и громко сообщала о неладном - сниппет тогда не нужен, конечно.

Python # Таймаут на выполнение функции # Function timeout

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

Я нашел такое решение: http://code.activestate.com/recipes/473878/

Там - написана такая функция:

def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None)
    # ...

Которую легко использовать, например, так:

def get_feed_index(url, limit=None):
    import feedparser
    data = timeout(feedparser.parse, (url,), timeout_duration=10.0)
    if data:
        # process data
    else:
        raise Exception("Reading feed timeout! URL = %s" % url)

То есть, мы захотели прочитать rss-фид, но feedparser не дает установить таймаут соединения, а ждать бесконечно долго тоже не круто. Поэтому он запускается в обертке timeout, а по таймауту выполнение просто обрывается и возвращается None. Удобно :)

Выложил сниппет: http://bitbucket.org/rudi/rudi/src/tip/python/feed_timeout.py

Django # Декоратор render_html # Decorator

Ещё один декоратор по мотивам… :)

Вот “декоратор”:

def render_html(template_name):
    def wrapper(func):
        @wraps(func)
        def inner(request, *args, **kwargs):
            result = func(request, *args, **kwargs)
            if result.has_key('_redirect'):
                return HttpResponseRedirect(result['_redirect'])
            else:
                return render_to_response(template_name, RequestContext(request, result))
        return inner
    return wrapper

Вот пример его использования:

def page_edit_handler(request, slug):
    page = get_object_or_404(slug=slug)
    if request.method == 'POST':
        form = PageForm(request.POST, instance=page)
        if form.is_valid():
            form.save()
            return {'_redirect': page.get_absolute_url()}
    else:
        form = PageForm(instance=page)
    return {'form': form}
    
page_edit_view = render_html('page_edit.html')(page_edit_handler)

Комментарий к примеру: page_edit_handler выдает обычный dict, который обычно попадает в контекст, и выводится как html. Но иногда нужно сделать редирект, тогда возвращается специальный dict, с ключом ‘_redirect’.

Ключ ‘_redirect’ начинается с подчеркивания - такие ключи не работают в контексте, т.е. {{ _redirect }} выдал бы ошибку. И это - на руку нам, потому что можно использовать этот ключ для наших служебных целей, и никому это не должно помешать.

Решение на первый взгляд - хитрое. Но на самом деле - вполне понятное, и если такой подход использовать в проекте, то вьюхи получаются более компактные, что хорошо. В этом и смысл шаманств :)

И комментарий второй - я специально не использовал в примере синтаксис декоратора! Если делать так, как в примере, то у нас остается “неиспорченная” функция page_edit_handler, которая может нам где-нибудь ещё пригодиться :)

Django # Приложение для определения макросов # Custom macro defenitions

Выложил аппликейшн django_autotext: http://github.com/rudyryk/django_autotext

Оно делает следующее: дает возможность определить произвольный “макрос”, который можно использовать в тексте - вместо макроса будет подставлен текст, сгенерированный функцией.

Как оно задумано и как работает?

Например - хочется чтобы можно было в тексте использовать имя текущего пользователя. Текст хранится в базе данных и редактируется через админку. Определим макрос user_full_name, и будем его использовать так:

Привет, дорогой { user_full_name }!

Определяется макрос следующим образом:

from django_autotext import formatter

def user_full_name(context):
    return context['user'].get_full_name()
formatter.add_macro('user_full_name', user_full_name)

В шаблоне - нужно включить текст внутрь тега-обработчика:

{% load autotext %}
{% autotext %}{{ content }}{% endautotext %}

Собственно, все :) Как установить, примеры, readme - прилагается к проекту, как положено :)

Markdown # Автоматический перенос строк # Auto line breaks

Мне нравится Markdown, но не нравится, что он не переносит строки автоматически.

В этом, наверняка, есть смысл :) Но это поведение анти-интуитивно, мне удалось это проверить на неискушенных знаниями пользователях.

Сделал расширение “autobr”, которое включает авто-перенос, выложил здесь: http://github.com/rudyryk/markdown-flavours/

Кстати, такую модификацию я встречал на нескольких сайтах, на том же GitHub, но исходников мне не попадалось.

UPD 25 Dec 2011. Новая ссылка на репозиторий: https://github.com/05bit/python-mdxflavours