34

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

如果我做:

import redis
...
myserver = redis.Redis("localhost")
myserver.set('counter', 5)

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

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

然后我在 if 语句中得到一个类型错误,因为我正在比较 '5' < 10,这意味着我正在存储一个整数值并获得一个字符串 1(可以被视为不同的值)。

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

有没有我缺少的配置?

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

这可能是我正在使用的 redis-py 客户端而不是 redis 本身的问题吗?

4

8 回答 8

19

从技术上讲,您需要自己处理。

但是,看看这个链接,特别是在他们的 README 中提到解析器和响应回调的部分,也许这是你可以使用的东西。问题是这对你来说是否是矫枉过正。

于 2012-10-25T02:30:19.960 回答
16

正如@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

for 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.

于 2013-07-13T23:48:53.067 回答
7

虽然利用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上述示例中的样板文件,并为您提供一个干净的接口,用于在 Redis 中存储和检索本机 python 数据类型:

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

用法:

>>> 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]}
于 2018-12-12T21:16:42.753 回答
6

您可以将 decode_respone 设置为 True

redis.StrictRedis(host="localhost", port=6379, db=0, decode_responses=True)
于 2018-09-20T19:10:59.283 回答
5

看起来这就是 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客户端来推断数据类型。

于 2012-10-25T03:01:49.037 回答
2

根据需要,我们有 3 种方式将数据存储到 Redis

细绳

  • 所有数据都以字节形式存储和读回。
  • 这是 Redis 使用的原生方式方法。
  • 我们可以redis-cli用来检查和操作数据
  • 但是,数据可能无法有效存储。例如12345,使用至少 5 个字节存储为字符串,即使它可以用 2 字节无符号短表示。
  • 您可以限制使用 Redis 提供的各种数据结构。
  • Redis 中存储的数据可以跨不同版本的 Python 和编程语言移植。

泡菜

  • pickle所有 Python 数据在存储到 Redis 之前都使用库转换为二进制数据。
  • 数据必须使用pickle库转换回 Python 数据类型才能使用。
  • 它可以对 Redis 不提供的复杂和动态数据结构进行建模。
  • 一旦数据被转换回 Python 数据类型,我们就可以使用强大的 Python 来操作数据。
  • 它在 Redis 中使用更多内存,pickle包括元数据、输出中的引用。
  • 不能redis-cli用于操作数据,例如INCR.
  • Redis 中存储的数据不能跨不同版本的 Python 和编程语言移植。

结构

  • struct所有 Python 数据在存储到 Redis 之前都使用库转换为二进制数据。
  • 数据必须使用struct库转换回 Python 数据类型才能使用。
  • 它可以对 Redis 不提供的复杂和静态数据结构进行建模。
  • 一旦数据被转换回 Python 数据类型,我们就可以使用强大的 Python 来操作数据。
  • 它使用这三种方法中最少的内存
  • 不能redis-cli用于操作数据,例如INCR.
  • Redis 中存储的数据可以跨不同版本的 Python 和编程语言移植。
from redis import Redis
import sys
import pickle
import struct

redis = Redis()

# Data
d = [1234, 3.1415, b'Good', True]
k = ['int', 'float', 'byte', 'bool']

## Using String
print(f'String\n{"_"*50}')
# Need to convert True to string or int or float
d2 = [1234, 3.1415, b'Good', int(True)]
redis.hset('k0', mapping=dict(zip(k,d2)))
print(redis.hmget('k0', *k))
print()


## Using Pickle
print(f'Pickle\n{"_"*50}')
packed = pickle.dumps(d)
print(f'Pickled data occupies {sys.getsizeof(packed)} bytes')
print(packed)
redis.set('k1', packed)
print(f'{pickle.loads(redis.get("k1"))}')
print()


## Using Struct
print(f'Struct\n{"_"*50}')
s = struct.Struct('H f 10s ?')
print(f'The structure occupies {s.size} bytes.')
packed = s.pack(*d)
print(f'{packed}')
redis.set('k2', s.pack(*d))
print(f'{s.unpack(redis.get("k2"))}')

预期输出:

String
__________________________________________________
[b'1234', b'3.1415', b'Good', b'1']

Pickle
__________________________________________________
Pickled data occupies 69 bytes
b'\x80\x04\x95\x19\x00\x00\x00\x00\x00\x00\x00]\x94(M\xd2\x04G@\t!\xca\xc0\x83\x12oC\x04Good\x94\x88e.'
[1234, 3.1415, b'Good', True]

Struct
__________________________________________________
The structure occupies 19 bytes.
b'\xd2\x04\x00\x00V\x0eI@Good\x00\x00\x00\x00\x00\x00\x01'
(1234, 3.1414999961853027, b'Good\x00\x00\x00\x00\x00\x00', True)
于 2020-08-09T17:35:33.277 回答
1

这是我的测试。两个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'>
于 2013-12-06T22:01:47.193 回答
0

一种解决方案可能是使用 ajson.loads()返回intfloat作为正确的类型,而不是str.

import json
import redis

redis = redis.from_url("redis://************")

def get(key, decode=True):
    """set decode to False when value stored as a string"""
    value = redis.get(key)
    if not decode:
        return value
    if value is not None:
        try:
            return json.loads(value)
        except json.decoder.JSONDecodeError:
            # not containing a JSON document
            return value

redis.set("foo", 2)
redis.set("bar", 2.75)

get("foo")
# 2
get("bar")
# 2.75
于 2021-03-06T17:37:57.397 回答