Получение значений с правильным типом в Redis
Я использую redis в своем приложении python для хранения простых значений, таких как счетчики и списки штампов времени, но пытаясь получить счетчик и сравнивая его с номером, я столкнулся с проблемой.
Если я это сделаю:
import redis
...
myserver = redis.Redis("localhost")
myserver.set('counter', 5)
а затем попытайтесь получить это значение следующим образом:
if myserver.get('counter') < 10:
myserver.incr('counter')
то я получаю ошибку типа в выражении if, потому что я сравниваю '5' < 10, что означает Я сохраняю целочисленное значение и получаю строку (которую можно рассматривать как другое значение).
Мой вопрос: это должно работать так? Я имею в виду его очень простой тип, я понимаю, если мне нужно разбирать объекты, но int? Кажется, что я делаю что-то неправильно.
Есть ли какая-либо конфигурация, которую мне не хватает?
Есть ли способ сделать redis возвращать правильный тип, а не всегда строку?
Я говорю это, потому что это то же самое для списков и дат или даже значений с плавающей запятой.
Может ли это быть проблемой с клиентом redis-py, который я использую, и не перерисовываю себя?
Ответы
Ответ 1
С технической точки зрения, вам нужно позаботиться об этом самостоятельно.
Однако посмотрите эту ссылку, особенно в части их README, которая ссылается на парсеры и ответные обратные вызовы, возможно, что-то, что вы можете использовать. Вопрос в том, будет ли это лишним для вас или нет.
Ответ 2
Как сказал @favoretti, ответные обратные вызовы будут делать трюк. Это совсем не сложно, только одна строка, и все позаботятся.
In [2]: import redis
In [3]: r = redis.Redis()
In [10]: r.set_response_callback('HGET', float)
In [11]: r.hget('myhash', 'field0')
Out[11]: 4.6
для hmget
, он возвращает список строк, а не одну строку, поэтому вам нужно построить немного более полную функцию обратного вызова:
In [12]: r.set_response_callback('HMGET', lambda l: [float(i) for i in l])
In [13]: r.hmget('myhash', 'field0')
Out[13]: [4.6]
для hgetall
.
Ответ 3
Похоже, что redis сохраняет свои данные:
redis 127.0.0.1:6379> set counter 5
OK
redis 127.0.0.1:6379> type counter
string
redis 127.0.0.1:6379> incr counter
(integer) 6
redis 127.0.0.1:6379> type counter
string
Если вы действительно этого захотите, возможно, вы можете обезвредить клиенту redis-py
для вывода типов данных.
Ответ 4
Вы можете установить decode_respone как True
redis.StrictRedis(host="localhost", port=6379, db=0, decode_responses=True)
Ответ 5
Вот мой тест. Два redis-соединения: один возвращает тип int, другой float
import redis
age_field = redis.Redis()
age_field.set_response_callback('HGET', int)
age_field.hget('foo', 'age')
# OUT: 40
a =age_field.hget('foo', 'age')
type(a)
# OUT: <type 'int'>
gpa_field = redis.Redis()
gpa_field.set_response_callback('HGET', float)
gpa_field.hget('foo', 'gpa')
# OUT: 2.5
b = gpa_field.hget('foo', 'gpa')
type(b)
# OUT: <type 'float'>
Ответ 6
Хотя использование set_response_callback
отлично подходит для простых типов данных, если вы задаетесь вопросом о самом быстром и простом set_response_callback
хранения таких вещей, как словари, списки, кортежи - и сохранении нативных типов данных Python, которые они могут или не могут содержать - Я рекомендую использовать встроенную в Python библиотеку pickle
:
# Imports and simplified client setup
>>> import pickle
>>> import redis
>>> client = redis.Redis()
# Store a dictionary
>>> to_store = {'a': 1, 'b': 'A string!', 'c': [1, True, False, 14.4]}
>>> client.set('TestKey', pickle.dumps(to_store))
True
# Retrieve the dictionary you just stored.
>>> retrieved = pickle.loads(client.get('TestKey'))
{'a': 1, 'b': 'A string!', 'c': [1, True, False, 14.4]}
Вот простой клиент, который уменьшит шаблон pickle
в приведенном выше примере и предоставит вам чистый интерфейс для хранения и извлечения собственных типов данных Python в Redis:
"""Redis cache."""
import pickle
import redis
redis_host = redis.Redis()
class PythonNativeRedisClient(object):
"""
A redis cache client.
Uses pickle for setting and getting so native datatypes are preserved.
"""
def __init__(self, redis_host=redis_host):
"""Initialize client."""
self.client = redis_host
def set(self, key, value, **kwargs):
"""Store a value in Redis."""
return self.client.set(key, pickle.dumps(value), **kwargs)
def get(self, key):
"""Retrieve a value from Redis."""
val = self.client.get(key)
if val:
return pickle.loads(val)
return None
redis_client = PythonNativeRedisClient()
Использование:
>>> from some_module import redis_client
>>> to_store = {'a': 1, 'b': 'A string!', 'c': [1, True, False, 14.4]}
>>> client.set('TestKey', to_store)
True
>>> retrieve = client.get('TestKey')
{'a': 1, 'b': 'A string!', 'c': [1, True, False, 14.4]}