Драйвер редиса возвращает `Redis (Either Reply a)`. При операции `HGET` возвращает, соответственно, `Redis (Either Reply (Maybe ByteString))`, типа значения по ключу может и не быть. Программист для упрощения пишет сначала специальную функцию `redis'`, которая заворачивает ответ редиса в EitherT-based монаду, чтоб соединять подобные вычисления и возвращать первую неудачу: ``` newtype Redis' a = Redis' (EitherT Redis.Reply Redis.Redis a) deriving (MonadIO) instance Monad Redis' where return = Redis' . EitherT . return . Right (Redis' m) >>= f = Redis' $ m >>= \rv -> unwrapRedis' $ f rv redis' = Redis' . EitherT ``` Таким образом, теперь, если вы получаете ответ `Redis (Either Reply a)`, вы его преобразуете в ``` Redis' (EitherT Reply Redis a) ``` и можете соединять подобные вычисления в do-блоке типа: ``` do res <- redis' $ Redis.hget foo bar res2 <- redis' $ Redis.hget baz zab ``` и вычисление остановится на первом возврате ошибки. Далее. Программисту необходимо по кучке значений сделать `HGET` и вернуть это как-то внутри кортежа, поскольку мы внутри новой монады `Redis'` -- завернуть результат в неё. В случае, если значения хоть по одному ключу не существует, хочется вернуть `Nothing` для всех. Потому создаётся новая монада: ``` newtype HashFields a = HashFields (MaybeT Redis' a) deriving (Functor, Monad) instance Applicative HashFields where pure = return (<*>) = ap ``` описывающая вычисления типа `Redis'`, которые могут вернуть неудачу. Пишется новая функция ``` hashField = HashFields . MaybeT . redis' ``` способная завернуть результат неудачи в новую монаду, которая умеет останавливаться на первой неудаче. Также пишется специальная функция для `HGET`: ``` getField :: ByteString -- ^ Key -> ByteString -- ^ Hash field name -> HashFields String getField key field = fmap toString $ hashField $ Redis.hget key field ``` Также напишем функцию, которая "запустит" наше вычисление: ``` getRedisFields :: forall a. HashFields a -> Redis' (Maybe a) getRedisFields (HashFields f) = runMaybeT f ``` И теперь лёгким движением руки мы можем сделать что-то вроде: ``` getRedisFields ((,,,,,) <$> getField k "foo" <*> getField k "bar" <*> getField k "baz" <*> getField k "zab" <*> getField k "rab" <*> getField k "oof") ``` Вопрос: вам не кажется это "слишком"? Я пока еще не настолько просто манипулирую типами в голове, чтоб ощутить всю ситуацию, хорошо хоть в целом могу медленно прости по шагам по коду, но есть ощущение, что что-то здесь не так.
На первый взгляд ну очень хороша. На второй взгляд стало немного страшно, т.к. на первый взгляд простую ситуацию вида class A(object): def foo(self): pass class B(A): def foo(self): pass b = B() b.foo разрулило неправильно. При переходе на b.foo пошло сразу на A.foo почему-то. Короче, всё равно придется внимательно следить, видимо.
In [1]: a = [b] = [3] In [2]: a Out[2]: [3] In [3]: b Out[3]: 3
В баше очень интересные кавычки. Одинарные кавычки, к примеру, сжирают всё, не давая ничего заэскейпить. То есть одинарные внутри одинарных вставить не получится. Также есть двойные кавычки, в которых бэкслеш \ является обычным символом во всех случаях, кроме как если после него стоит один из символов (‘$’, ‘`’, ‘\’ или ‘!’), тут он, всё же, эскейпит. Удивительное решение сделать зависимость экранирования от контекста, ну и ладно. Bash: kost@k-bx:~$ echo "tfoo" tfoo kost@k-bx:~$ echo "\tfoo" \tfoo kost@k-bx:~$ echo "\\tfoo" \tfoo А теперь, по ошибке забыв что у меня zsh, узрел, что там они тоже решили действовать своим, особенным способом. Я не читал документацию, но из экспериментов так и не понял принцип работы: ~ ➤ echo "tfoo" tfoo ~ ➤ echo "\tfoo" foo ~ ➤ echo "\\tfoo" foo ~ ➤ echo "\\\tfoo" \tfoo ~ ➤ echo "\\\\tfoo" \tfoo Очень сложно со строками :) p.s.: напоминаю, что "привычные" строки можно получить при помощи использования $'строка'
tip of the day: export PIP_DOWNLOAD_CACHE=$HOME/.pip_download_cache Эта строчка сделает так, что pip будет качать все пакеты в эту директорию, а в случае, если там уже есть пакет -- не качать его, а брать оттуда. То есть в новом virtualenv теперь вам не будет больно сделать pip install -U ipython. Интересно, почему это не по-умолчанию.
Итого, сейчас наиболее "правильная" команда для запуска тестов у меня такая: time python manage.py test --settings settings_test --failfast --failed
Exercise 2.6. In case representing pairs as procedures wasn't mind-boggling enough, consider that, in a language that can manipulate procedures, we can get by without numbers (at least insofar as nonnegative integers are concerned) by implementing 0 and the operation of adding 1 as (define zero (lambda (f) (lambda (x) x))) (define (add-1 n) (lambda (f) (lambda (x) (f ((n f) x))))) This representation is known as Church numerals, after its inventor, Alonzo Church, the logician who invented the calculus. Define one and two directly (not in terms of zero and add-1). (Hint: Use substitution to evaluate (add-1 zero)). Give a direct definition of the addition procedure + (not in terms of repeated application of add-1). Для ленивых вот ответ: (define zero (lambda (f) (lambda (x) x))) (define one (lambda (f) (lambda (x) (f x)))) (define two (lambda (f) (lambda (x) (f (f x)))))
Сволочи. Они взяли и реализовали cons/car/cdr через замыкания. То есть я и сам мог это сделать, а не догадался. (define (cons x y) (define (dispatch m) (cond ((= m 0) x) ((= m 1) y) (else (error "Argument not 0 or 1 -- CONS" m)))) dispatch) (define (car z) (z 0)) (define (cdr z) (z 1))
>>> from django.db.models import Q ... ImportError: Settings cannot be imported, because environment variable DJANGO_SETTINGS_MODULE is undefined. неправильно это. ой неправильно.
вот так чуть лучше будет (global-set-key [?\C-.] 'tabbar-forward-tab) (global-set-key [?\C-,] 'tabbar-backward-tab)
Текущий XMPPWSGI: def application(environ): return [u"response", u"strings"] типа пользователь пишет что-то, xmppwsgi-сервер создает environ {'XMPP_JID': 'v.pup@ya.ru', 'MESSAGE': 'пишу боту'} веб-сервер вызывает эту функцию и передает environ то, что получит в ответ -- это ответ Васе, который он запостит. А теперь сама проблема: если мы пишем свой juick -- когда вася пишет, к примеру, пост, надо ему ответить "message posted", а еще его подписчикам ответить "vasya posted: 'ttttt'". Но сами ответы в джаббер должен делать xmppwsgi-сервак (мы ничего не должны знать о самой посылке, сообщений, мы только формируем что и кому, как в http-wsgi тебе все равно какой wsgi-сервер будет отдавать тело ответа), а приложение должно только формировать поток сообщений вида [["кому", "что"]...]. Вопрос, собственно. какой из вариантов? Первый: def application(environ): return [[u"response", u"message posted"], [('one@ya.ru', 'vasya posted shit'), ('two@ya.ru', 'vasya posted shit'),]] Второй: def application(environ, message_queue): message_queue.append(('one@ya.ru', 'vasya posted shit')) message_queue.append(('two@ya.ru', 'vasya posted shit')) return [u"response", u"message posted"] типа по ссылке list обычный ему давать. Плюсы первого -- очевидно. Минусы -- перелопатить весь код и всегда возвращать два аргумента (хотя есть вариант, чтоб понимать и один и два аргумента).
Кто бы мог подумать. def test_xmppwsgi_simple(self): environ = {'MESSAGE': 'PING', 'XMPP_JID': 'k.bx@ya.ru'} app = xmppflask.XmppFlask(__name__) @app.route(u'PING') def ping(): return u'PONG' rv = app(environ) self.assertEquals(list(rv), ['PONG']) (xmpp)kost@kost-narwhal:~/workspace/xmppflask$ nosetests . ---------------------------------------------------------------------- Ran 1 test in 0.222s OK
Прикольно functools.partial http://docs.python.org/library/functools.html#functools.partial Это типа фигня, при помощи которой можно делать что-то вроде лямбд, только для уже готовых функций, да еще и переписывает поверху аргументы. К примеру: >>> from functools import partial ... def a(b, c, d, e): ... print b,c,d,e ... b = partial(a, b=10, c=11, d=12) ... b(d=14, e=13) 10 11 14 13 Мне нравится)
посчитать кол-во строк в питон-файлах typeset -i sum=0; for i in $(find . -name "*.py"); do sum=$(( sum + $(wc -l < "${i}") )); done; echo $sum;
(упс, еще раз, потому что %m а не %M :-) удобно забекапить pg_dump'ом много баз сразу одним махом в красивые имена скачать бесплатно for DBNAME in db1 db2 db3 db4 db5; do pg_dump --host db --port 5432 --username postgres --format custom --blobs --verbose --file $DBNAME-$(date +%Y)-$(date +%m)-$(date +%d).backup $DBNAME; done
закрыть все неактивные бранчи for item in $(hg branches | grep inactive | awk '{print $1}' ); do echo "closing branch ${item}"; hg up $item; hg ci --close-branch -m "close branch ${item}"; done;
Отгадка: во 2-м питоне объекты сравниваются по _именам_ их типов, то есть получаем type(int).__name__ < type(str).__name__ или вроде того. А в 3-м уже все будет ок http://docs.python.org/release/3.0.1/whatsnew/3.0.html#ordering-comparisons
Для тех, кто не понял: >>> 3 < '3' True
………,………………..,……,………….,……,…………………… …,,..,………………………,……:,,::,,:,.,,,,:,.,,………,…,..,,…. ..,..,,…..,……2illl▓▓███████████▓▓▓▓▓▓██▓………,..,,.. ,…,..,…,.k▓█▓▓▓▓lBEnjjBllllllBjzv░1JTHtq8tL;..,…nll█▓…….,…,, ..,……v▓█M:..,::,,:;1JBt;,…,….q:……:vFll▓▓llEL…..█▓,……,… ,…,…..█,,,…░;……..;x,…….,:,,..,,:,,……..,░,,,…..█lM…….,, …..,:n█▓…,..Lii███▓B1……..tj▓▓▓█████lM.,,……..j█▓;…… …W██▓w,,..,Y▓▓█████M…k██▓▓▓█▓lll▓▓v▓▓▓▓▓;.▓█▓… ..#ll█::vn▓▓▓▓g;J:..,.t█▓…………….,n▓▓glll▓▓▓▓Wl▓▓.wll█.. …zi█;:..,..,t▓7▓▓░2ll█▓:……,.X▓Bq..,………Bll▓▓█▓.El█3.F█lll …..▓█1t;3█▓.,…Wl██,..,..f▓▓Mi█W,rB▓▓▓█lB;..▓█lBt█lK.:;█.. ….,.f█▓W███▓▓7:..▒▓█M..tjBl▓██▓Bjzr▒█▓Bj██jY.,..,h█▓2.. …..,..j█.jll█Bl▓▒ll█fjllllll█llBIjj█6;:..▒▓▓▓████,l█▓……Bl█▓..,.. ,……U█.;r█████▓▓▓█▓▓█▓▓█████Bj..C██W…..yi█;:……. ……..j█…█████████████▓lM:.k▓M..f▓█W,…,..lll█8:…..,… ,……N▓,..B██▓▓.█M1▓▓.,..j█….,;8l██▓W,…..░ll██r:…..,,..,, ……r▓▓…..▒ll▓▓▓██▓█▓▓██▓▓▓▓881xtll▓▓██▓I….,..,,..,… ……I█▓.6;,.░;,,,..,..;r░::v7::,..;YTn;xtll▓▓█▓▓n……….,,..,….. …..w█▓..,JjlgglllBjkT:,..,………,,.,W▓▓▓▓H……………,,..,..,,.. ,……U█▓r….,……,……,;rll▓l█▓▓▓▓t,..,……….,..,,..,..,,.,,…. ………▒ll███▓▓▓▓███▓▓▓BBj::….,……,…..,,..,..,,..,..,,.,,… ,…,..,………….,…,……,……,..,…,..,……,……,…,……,……..
class SimpleTest(TestCase): def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. """ self.failUnlessEqual(1 + 1, 2)