关于python:在Redis中使用正确的类型获取值

Getting values with the right type in Redis

我在python应用程序中使用redis来存储诸如计数器和时间戳列表之类的简单值,但是尝试获取一个计数器并将其与数字进行比较时遇到了问题。

如果我做:

1
2
3
4
import redis
...
myserver = redis.Redis("localhost")
myserver.set('counter', 5)

然后尝试像这样获得该值:

1
2
if myserver.get('counter') < 10:
     myserver.incr('counter')

然后在if语句中遇到类型错误,因为我正在比较'5'<10,这意味着我正在存储一个整数值并获取一个字符串(可以认为是另一个值)。

我的问题是:这应该像那样工作吗? 我的意思是它是一个非常基本的类型,我知道是否必须解析对象但可以解析一个int? 看来我做错了。

我缺少任何配置吗?

有什么方法可以使redis返回正确的类型,而不总是返回字符串吗?
我之所以这样说是因为列表和日期时间甚至浮点值都相同。

我使用的redis-py客户端会出现问题,而不是redis本身吗?


从技术上讲,您需要自己进行维护。

但是,请看一下此链接,尤其是他们的README部分中的引用解析器和响应回调的部分,也许您可??以使用它。问题是这是否对您来说是一个过度的杀伤力。


正如@favoretti所说,响应回调将达到目的。它一点也不复杂,只需一行就可以了。

1
2
3
4
5
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,它返回一个字符串列表,而不是一个字符串,因此您需要构造一个更全面的回调函数:

1
2
3
4
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相同。


看起来这就是redis存储数据的方式:

1
2
3
4
5
6
7
8
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客户端以推断数据类型。


尽管利用set_response_callback对于简单的数据类型来说是很好的选择,但如果您想知道最快,最简单的方法来存储字典,列表,元组之类的东西,并保留python本地数据类型,它们可能会也可能不会包含–我建议使用python内置的pickle库:

1
2
3
4
5
6
7
8
9
10
11
# 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样板,并为您提供一个干净的接口,用于在Redis之间存储和检索本机python数据类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"""Redis cache."""
import pickle
import redis

redis_host = redis.Redis()


class PythonNativeRedisClient(object):
   """A simple redis client for storing and retrieving native python datatypes."""

    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()

用法:

1
2
3
4
5
6
>>> from some_module import redis_client
>>> to_store = {'a': 1, 'b': 'A string!', 'c': [1, True, False, 14.4]}
>>> redis_client.set('TestKey', to_store)
True
>>> retrieve = redis_client.get('TestKey')
{'a': 1, 'b': 'A string!', 'c': [1, True, False, 14.4]}

您可以将True设置为True

1
redis.StrictRedis(host="localhost", port=6379, db=0, decode_responses=True)


这是我的测试。两个redis连接:一个返回int类型,另一个返回float

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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'>