今さらPython3 (60) - Redis その2

この本の第8章をまだ続けてます。Redisの続きから。

入門 Python 3

入門 Python 3

Redis - リスト

>>> conn.lpush('zoo', 'bear')
1
>>> conn.lpush('zoo', 'alligator', 'duck')
3

これだけ見ると、さっきのset/getたちと違いがよく分からない。lpushの最初のlがおそらくListを指していて、第2引数以降のものを追加していると考えれば良いのかな。

>>> conn.linsert('zoo', 'before', 'bear', 'beaver')
4
>>> conn.linsert('zoo', 'after', 'bear', 'cassowary')
5

なんか美しくないなぁ。zooのbearの手前(before)にbeaverって入れてくれという意味と、後ろ(after)にcassowaryを入れてくれという意味なんだろうけど。

>>> conn.lset('zoo', 2, 'marmoset')
True
>>> conn.rpush('zoo', 'yak')
6
>>> conn.lindex('zoo', 3)
b'bear'
>>> conn.lindex('zoo', 0)
b'duck'
>>> conn.lindex('zoo', 1)
b'alligator'
>>> conn.lindex('zoo', 2)
b'marmoset'
>>> conn.lindex('zoo', 4)
b'cassowary'
>>> conn.lindex('zoo', 5)
b'yak'
>>> conn.lindex('zoo', 6)
>>> 

lset()は、指定したindexのところの値を置き換える、rpush()は右側から追加する。lindex()で値を拾うと。

>>> conn.lrange('zoo', 0, -1)
[b'duck', b'alligator', b'marmoset', b'bear', b'cassowary', b'yak']
>>> conn.lrange('zoo', 0, 2)
[b'duck', b'alligator', b'marmoset']

1つ1つチマチマ出すのかよと思ったら、lrange()が用意されていましたね。

>>> conn.ltrim('zoo', 1, 4)
True
>>> conn.lrange('zoo', 0, -1)
[b'alligator', b'marmoset', b'bear', b'cassowary']
>>> 

ltrim()で、必要な部分のみ残して、いらない部分が切り落とされます。

ものすごい駆け足感あるけど次へ。

ハッシュ

ハッシュはPythonの辞書と違うけど、文字列しか格納できないそうだ。(リストも文字列のみ格納)

>>> conn.hmset('song', {'do':'a deer', 're':'about a deer'})
True
>>> conn.hset('song', 'mi', 'a note to follow re')
1
>>> conn.hget('song', 'mi')
b'a note to follow re'
>>> conn.hmget('song', 're', 'do')
[b'about a deer', b'a deer']
>>> conn.hkeys('song')
[b're', b'do', b'mi']
>>> conn.hvals('song')
[b'about a deer', b'a deer', b'a note to follow re']
>>> conn.hlen('song')
3
>>> conn.hgetall('song')
{b're': b'about a deer', b'mi': b'a note to follow re', b'do': b'a deer'}
>>> conn.hsetnx('song', 'fa', 'a note that rhymes with la')
1
>>> 

出来損ないのドレミの歌みたいな感じの辞書(ではなくハッシュ)が作成されましたね。h:ハッシュ、m:multipleと考えれば、これまでのものと規則性は似ているように見える。

集合(セット)

>>> conn.sadd('zoo', 'duck', 'goat', 'turkey')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/redis/client.py", line 1494, in sadd
    return self.execute_command('SADD', name, *values)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/redis/client.py", line 573, in execute_command
    return self.parse_response(connection, command_name, **options)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/redis/client.py", line 585, in parse_response
    response = connection.read_response()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/redis/connection.py", line 582, in read_response
    raise response
redis.exceptions.ResponseError: WRONGTYPE Operation against a key holding the wrong kind of value
>||

いきなり怒られたと思ったら、zooという名前をすでに使用済みだからだったみたい。zoo2に変更して再トライ。

>|python|
>>> conn.sadd('zoo2', 'duck', 'goat', 'turkey')
3
>>> conn.scard('zoo2')
3
>>> conn.smembers('zoo2')
{b'duck', b'goat', b'turkey'}
>>> conn.srem('zoo2', 'turky')
0

scard()で要素数を数えて、smembers()で一覧を出す。srem()はremoveで要素を消すってところですね。

>>> conn.sadd('better_zoo', 'tiger', 'wolf', 'duck')
3
>>> conn.sinter('zoo2', 'better_zoo')
{b'duck'}

もう1つbetter_zooというセットを作って、共通の積集合を得るのがsinter()ですねと。

>>> conn.sinterstore('fowl_zoo', 'zoo2', 'better_zoo')
1
>>> conn.smembers('fowl_zoo')
{b'duck'}
>>> 

中身を確認。duckがかぶってますね。

>>> conn.sunionstore('fabulous_zoo', 'zoo2', 'better_zoo')
5
>>> conn.smembers('fabulous_zoo')
{b'duck', b'turkey', b'wolf', b'tiger', b'goat'}

ファッッッ!!!消したはずの七面鳥が。。。と思ったら、さっきsrem()したときにturkyというのを消してる。そして空振りだったので、戻り値値がゼロになってるに気づかずにここまで進んで来ちゃったんだね。何はともあれ、sunionstore()を使って和集合をfabulous_zooに入れたので、消したつもりの七面鳥含めて5つエントリがあるのが分かる。

>>> conn.sdiff('zoo2', 'better_zoo')
{b'goat', b'turkey'}
>>> conn.srem('zoo2', 'turkey')
1
>>> conn.sdiff('zoo2', 'better_zoo')
{b'goat'}
>>> conn.sdiffstore('zoo_sale', 'zoo2', 'better_zoo')
1
>>> conn.smembers('zoo_sale')
{b'goat'}
>>> 

今度はzoo2にいて、better_zooにいない動物を探しますと。sdiff()で出すんだけど、さっき消しそびれた七面鳥がまだいるので、クリスマスに食べちゃった事※にして(srem)、山羊(goat)だけになるように結果を合わせておき、その上でsdiffstoreでzoo_saleに入れてあげました。山羊は売られる運命なんですね。。。

※この記事を書いている今日が12/24なので、こんな事を言ってしまいました。

ソート済集合(セット)

>>> import time
>>> now = time.time()
>>> now
1450945634.176036
>>> conn.zadd('logins', 'smeagol', now)
1
>>> conn.zadd('logins', 'sauron', now+(5*60))
1
>>> conn.zadd('logins', 'bilbo', now+(5*60*2))
1
>>> conn.zadd('logins', 'treebeard', now+(24*60*60))
1

よそ見していて、2時間後の来客のハズが10分後になってしまったけど、問題ないはずなので気にせずに続ける。

>>> conn.zrank('logins', 'bilbo')
2
>>> conn.zscore('logins', 'bilbo')
1450946234.176036
>>> time.localtime(conn.zscore('logins', 'bilbo'))
time.struct_time(tm_year=2015, tm_mon=12, tm_mday=24, tm_hour=17, tm_min=37, tm_sec=14, tm_wday=3, tm_yday=358, tm_isdst=0)
>>>

bilboが来たのが2番目(zrank())で、来た時刻というのが2015/12/24 17:37だというのが分かりますねと。

>>> conn.zrange('logins', 0, -1)
[b'smeagol', b'sauron', b'bilbo', b'treebeard']
>>> conn.zrange('logins', 0, -1, withscores=True)
[(b'smeagol', 1450945634.176036), (b'sauron', 1450945934.176036), (b'bilbo', 1450946234.176036), (b'treebeard', 1451032034.176036)]
>>> 

なんか時間が分からないと気持ち悪いやんけと格闘すること10分。

>>> for name, score in conn.zrange('logins', 0, -1, withscores=True):
...     print(name.decode('utf-8'), time.strftime('%Y/%m/%d %H:%M', time.localtime(score)))
... 
smeagol 2015/12/24 17:27
sauron 2015/12/24 17:32
bilbo 2015/12/24 17:37
treebeard 2015/12/25 17:27
>>>

(つづく)