Python money formatting # Форматирование вывода денег # moneyfmt -
Поиском находится плохо. Чтобы лучше искалось, решил запостить ссылку :) (thanks to Vorushin)
django-bricks # Generic Django application for storing content blocks in models
Это специальный пост для тех, кто не попробовал ещё 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. Роман Ворушин и Анатолий Востряков программируют на питоне.
А это вам не хухры мухры.
:)
Mercurial # Руководство # Manual # Tutorial # How to
Нашел грабли :) Надо будет сделать баг-репорт, но пока - просто workaround.
Грабли - если view задекорирован и в нем возникает ошибка, то джанга не всегда правильно обрабатывает traceback - дебажный вывод не показывает, точнее слетает в самом дебажном выводе :)
Пример:
from decorators import render_to
@render_to('main.html')
def main_view(request):
# тут что-то пошло не так
raise Exception('AAA!!!')
return context
Так вот, чтобы дебажный вывод работал правильно (как обычно), render_to должен быть объявлен:
Если render_to импортится из того же package, что и view, то дебажный вывод сходит с ума и не может правильно определить, в каком файле произошла ошибка.
То есть, вот так - сломается: есть views.py, где лежат views, и рядом decorators.py, в котором объявлен render_to.
Работает - так (main_view должен быть не в myproject.utils :)
from myproject.utils.decorators import render_to # ...
def safe_int(val):
try:
return int(val)
except (TypeError, ValueError):
return 0
Пожалуй, алмазный сниппет :)
Нужен, если на входе ожидается непредсказуемое значение, но на выходе требуется получить значение в виде int во что бы то ни стало, так, чтобы программа не ломалась. Если требуется, чтобы ломалась и громко сообщала о неладном - сниппет тогда не нужен, конечно.
Иногда функция может выполняться дольше чем хочется, и нужно установить таймаут на выполнение функции - чтобы выполнение просто оборвалось, если время вышло.
Я нашел такое решение: 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
Для круглых углов в HTML нужно просто использовать свойство “border-radius” :) Кто не поддерживает - сам виноват, зато будет простой, красивый и легкий CSS.
.round-corner {
border:1px solid #a00;
border-radius: 15px;
}
Хохо! У меня круглые углы.
Синтаксис предложен W3C для CSS3: http://www.w3.org/TR/2005/WD-css3-background-20050216/#the-border-radius
Одна из моих любимых библиотек - “django-treebeard”. В ней есть набор реализаций основных алгоритмов для хранения древовидной структуры в базе данных. Автор живет в Перу :)
Ещё один декоратор по мотивам… :)
Вот “декоратор”:
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, которая может нам где-нибудь ещё пригодиться :)
Завел репозиторий для всех подряд сниппетов:
https://bitbucket.org/rudi/rudi/src/
Так приятно сделан битбакет, прямо большой плюс в сторону mercurial :)
Выложил аппликейшн 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 - прилагается к проекту, как положено :)
Javascript # jsMath # Библиотека для вставки формул в HTML # Math formulas in HTML -
Не совсем питон, конечно :) Но полезная штука для веба, а питон с вебом близко дружат :)
Мне нравится Markdown, но не нравится, что он не переносит строки автоматически.
В этом, наверняка, есть смысл :) Но это поведение анти-интуитивно, мне удалось это проверить на неискушенных знаниями пользователях.
Сделал расширение “autobr”, которое включает авто-перенос, выложил здесь: http://github.com/rudyryk/markdown-flavours/
Кстати, такую модификацию я встречал на нескольких сайтах, на том же GitHub, но исходников мне не попадалось.
UPD 25 Dec 2011. Новая ссылка на репозиторий: https://github.com/05bit/python-mdxflavours
Inspired by http://vorushin.ru/blog/26-decorators-python :)
Например, если у меня есть view для удаления чего-нибудь - скажем, файла:
def remove_file(request, id):
#...
И мне нужно показать страницу с подтверждением удаления - простую “да / нет”. Я написал декоратор, который может работать вот так:
def confirm_context(request, id):
context = {}
# ...
return RequestContext(request, context)
@confirm_required('confirm_remove.html', confirm_context)
def remove_file(request, id):
#...
Оригинальный view при этом не модифицируется, что радует.
Теперь при вызове view проверяется POST-запрос на наличие в нем ключа “__confirm__”, либо другого - это определяется необязательным параметром декоратора “key”. Если ключа нет - показывается страница подтверждения. Если есть - вызывается оригинальный view.
В шаблон подтверждения передается контекст, созданный функцией “confirm_context”. Это обычная функция, которая принимает те же параметры, что и изначальный view.
Собственно, код декоратора:
def confirm_required(template_name, context_creator, key='__confirm__'):
def decorator(func):
def inner(request, *args, **kwargs):
if request.POST.has_key(key):
return func(request, *args, **kwargs)
else:
context = context_creator and \
context_creator(request, *args, **kwargs) \
or RequestContext(request)
return render_to_response(template_name, context)
return wraps(func)(inner)
return decorator
ПС. Надо будет подправить верстку блога, а то сниппеты не влезают нормально по ширине.