您现在的位置是: 网站首页 >Python Python

Python操作Redis语法string、hash、list、set使用方法

admin2019年3月21日 11:26 Python | Redis 370人已围观

# Redis连接 > redis提供两个类`Redis`和`StrictRedis`用于实现Redis的命令,`StrictRedis`用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。 > redis连接实例是线程安全的,可以直接将redis连接实例设置为一个全局变量,直接使用。如果需要另一个Redis实例(or Redis数据库)时,就需要重新创建redis连接实例来获取一个新的连接。同理,python的redis没有实现select命令。 连接redis,加上`decode_responses=True`,写入的键值对中的`value`为`str`类型,不加这个参数写入的则为字节类型。 ```python >>> import redis # host是redis主机,需要redis服务端和客户端都启动 redis默认端口是6379 >>> r = redis.Redis(host="localhost", port=6379, decode_responses=True) # key是name,value是LR,将键值对写入redis缓存,写入成功后返回True >>> r.set("name", "LR") True # 取出键name的值 >>> print(r["name"]) LR >>> print(r.get('name')) LR >>> print(type(r.get('name'))) <class 'str'> ``` # 连接池 > redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。 可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池 ```python >>> pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True) >>> r = redis.Redis(connection_pool=pool) >>> r.set('gender', 'male') True >>> print(r.get('gender')) male ``` # redis基本命令 string ## set(name, value, ex=None, px=None, nx=False, xx=False) 在Redis中设置值,默认,不存在则创建,存在则修改 参数: * ex,过期时间(秒) * px,过期时间(毫秒) * nx,如果设置为`True`,则只有name不存在时,当前set操作才执行 * xx,如果设置为`True`,则只有name存在时,当前set操作才执行 ## ex,过期时间秒 过期时间(秒) 这里过期时间是10秒,10秒后键food的值就变成None ```python >>> r.set('food', 'rice', ex=10) True >>> print(r.get('food')) rice >>> print(r.get('food')) None ``` ## px,过期时间毫秒 过期时间(毫秒) 这里过期时间是2000毫秒,2000毫秒后,键food的值就变成None ```python >>> r.set('food', 'rice', px=2000) True >>> print(r.get('food')) None ``` ## nx,name不存在时新建 如果设置为True,则只有name不存在时,当前set操作才执行 (新建),如果name存在,则也不执行更新 ```python # name不存在时新建 >>> r.set('fruit', 'apple', nx=True) True # name存在时不再更新 >>> r.set('fruit', 'banana', nx=True) >>> >>> ret = r.set('fruit', 'banana', nx=True) >>> ret >>> print(ret) None ``` ## xx,name存在时修改 如果设置为True,则只有name存在时,当前set操作才执行 (修改),如果name不存在,则也不进行创建 ```python # name存在时进行修改 >>> r.set('fruit', 'banana', xx=True) True >>> r.get('fruit') 'banana' # name不存在时不再创建 >>> ret = r.set('non', 'test', xx=True) >>> print(ret) None >>> r.get('non') ``` ## get(name),获取name的值 ```python >>> r.get('fruit') ``` ## setnx(name, value),name不存在时新建 设置值,只有name不存在时,执行设置操作(添加) ```python # name不存在时新建 >>> r.setnx('fruit1', 'banana') True >>> r.get('fruit1') 'banana' # name存在时不变 >>> r.setnx('fruit1', 'apple') False >>> r.get('fruit1') 'banana' ``` ## setex(name, value, time),以过期时间秒新建 参数: time,过期时间(数字秒 或 timedelta对象) ```python # 设置过期时间5s。5s后见fruit2的值变为None >>> r.setex('fruit2', 'orange', 5) True >>> print(r.get('fruit2')) None ``` ## psetex(name, time_ms, value),以过期时间毫秒新建 参数: time_ms,过期时间(数字毫秒 或 timedelta对象) ```python # 设置5000ms后,键fruit3的值清空 >>> r.psetex('fruit3', 5000, 'apple') True >>> print(r.get('fruit3')) None ``` ## mset(*args, **kwargs),批量设置值 批量设置值,参数为字典的键值对或赋值形式 ```python >>> r.mset({'k1': 'v1', 'k2': 'v2'}) True >>> r.mset(k3='v3', k4='v4') True >>> r.get('k2') 'v2' >>> r.get('k3') 'v3' ``` ## mget(keys, *args),批量获取值 批量获取,参数为获取的name,或获取的name列表 ```python >>> r.mget('k1', 'k2') ['v1', 'v2'] >>> r.mget(['k2', 'k3', 'k4']) ['v2', 'v3', 'v4'] >>> r.mget(['k2', 'k3', 'k4', 'k5']) ['v2', 'v3', 'v4', None] ``` ## getset(name, value),设置新值并获取原来的值 设置新值并获取原来的值 ```python >>> r.get('fruit') 'banana' >>> r.getset('fruit', 'apple') 'banana' >>> r.get('fruit') 'apple' ``` ## getrange(key, start, end),切片操作获取值 获取子序列(根据字节获取,非字符) 参数: - name,Redis 的 name - start,起始位置(字节) - end,结束位置(字节) **一个汉字3个字节 1个字母一个字节 每个字节8bit** ```python >>> r.set('name1', '这是中文') True # 取索引号是0-2 前3位的字节 这 切片操作 (一个汉字3个字节 1个字母一个字节 每个字节8bit) >>> r.getrange('name1', 0, 2) '这' >>> r.getrange('name1', 0, 3) # 引发异常:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe6 in position 3: unexpected end of data >>> r.getrange('name1', 0, 5) '这是' # 取所有字节 >>> r.getrange('name1', 0, -1) '这是中文' >>> r.set('name2', 'This is English') True >>> r.getrange('name2', 0, 4) 'This ' >>> r.getrange('name2', 0, 8) 'This is E' >>> r.getrange('name2', 0, -1) 'This is English' ``` ## setrange(name, offset, value),指定位置替换字符串 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加) 参数: - offset,字符串的索引,字节(一个汉字三个字节) - value,要设置的值 ```python >>> r.getrange('name2', 0, -1) 'This is English' >>> >>> >>> # 从索引号是2的地方开始替换成at,也就死This编程That >>> r.setrange('name2', 2, 'at') 15 >>> r.get('name2') 'That is English' ``` ## setbit(name, offset, value),设置二进制位的值 对name对应值的二进制表示的位进行操作,替换为0或1 参数: - name,redis的name - offset,位的索引(将值变换成二进制后再进行索引) - value,值只能是 1 或 0 注:如果在Redis中有一个对应: n1 = "foo", 那么字符串foo的二进制表示为:01100110 01101111 01101111 所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1, 那么最终二进制则变成 01100111 01101111 01101111,即:"goo" 对于utf-8,每一个汉字占 3 个字节 对于汉字,for循环时候会按照 字节 迭代,那么在迭代时,将每一个字节转换 十进制数,然后再将十进制数转换成二进制 ## getbit(name, offset),获取二进制某位的值 获取name对应的值的二进制表示中的某位的值 (0或1) ```python >>> r.get('name2') 'That is English' >>> r.getbit('name2', 0) 0 >>> r.getbit('name2', 5) 1 ``` ## bitcount(name, start=None, end=None),获取二进制1出现次数 获取name对应的值的二进制表示中 1 的个数 参数: - name,Redis的name - start 字节起始位置 - end,字节结束位置 ```python >>> r.get('name2') 'That is English' # 表示前2个字节中,1出现的个数 >>> r.bitcount('name2', 0, 1) 6 ``` ## bitop(operation, dest, *keys),多个值做位运算 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值 参数: - operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或) - dest, 新的Redis的name - *names,要查找的Redis的name ```python bitop("AND", 'new_name', 'n1', 'n2', 'n3') # 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中 r.set("foo","1") # 0110001 r.set("foo1","2") # 0110010 print(r.mget("foo","foo1")) # ['goo1', 'baaanew'] print(r.bitop("AND","new","foo","foo1")) # "new" 0 0110000 print(r.mget("foo","foo1","new")) source = "12" for i in source: num = ord(i) print(num) # 打印每个字母字符或者汉字字符对应的ascii码值 f-102-0b100111-01100111 print(bin(num)) # 打印每个10进制ascii码值转换成二进制的值 0b1100110(0b表示二进制) print bin(num).replace('b','') # 将二进制0b1100110替换成01100110 ``` ## strlen(name),获取值的字节长度 返回name对应值的字节长度(一个汉字3个字节) ```python >>> r.get('name2') 'That is English' >>> r.strlen('name2') 15 ``` ## incr(self, name, amount=1),值自增,整型 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。 参数: - name,Redis的name - amount,自增数(必须是整数) 注:同incrby ```python >>> r.set('foo', 6) True >>> r.get('foo') '6' >>> r.incr('foo', amount=1) 7 >>> r.get('foo') '7' ``` 应用场景 – 页面点击数 假定我们对一系列页面需要记录点击次数。例如论坛的每个帖子都要记录点击次数,而点击次数比回帖的次数的多得多。如果使用关系数据库来存储点击,可能存在大量的行级锁争用。所以,点击数的增加使用redis的INCR命令最好不过了。 当redis服务器启动时,可以从关系数据库读入点击数的初始值(12306这个页面被访问了34634次) ```python >>> r.set("visit:12306:totals", 34634) True >>> print(r.get("visit:12306:totals")) 34634 # 每当有一个页面点击,则使用INCR增加点击数即可。 >>> r.incr("visit:12306:totals") 34635 >>> r.incr("visit:12306:totals") 34636 >>> r.incr("visit:12306:totals") 34637 # 页面载入的时候则可直接获取这个值 >>> print(r.get("visit:12306:totals")) 34637 ``` ## incrbyfloat(self, name, amount=1.0),值自增,浮点型 自增name对应的值,当name不存在时,则创建name=amount,否则,则自增。 参数: - name,Redis的name - amount,自增数(浮点型) ```python >>> r.set("foo1", "123.0") True >>> r.set("foo2", "221.0") True >>> print(r.mget("foo1", "foo2")) ['123.0', '221.0'] >>> r.incrbyfloat("foo1", amount=2.0) 125.0 >>> r.incrbyfloat("foo2", amount=3.0) 224.0 >>> print(r.mget("foo1", "foo2")) ['125', '224'] >>> r.incrbyfloat("foo1", amount=2.6) 127.6 >>> print(r.mget("foo1", "foo2")) ['127.59999999999999', '224'] >>> r.incrbyfloat("foo1", amount=1.20) 128.79999999999998 >>> print(r.mget("foo1", "foo2")) ['128.79999999999998', '224'] ``` ## decr(self, name, amount=1),值自减,整型 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。 参数: - name,Redis的name - amount,自减数(整数) ```python >>> r.mset(k1=25, k2=6) True # 递减3 >>> r.decr('k1', amount=3) 22 # 递减10 >>> r.decr('k2', 10) -4 >>> print(r.mget('k1', 'k2')) ['22', '-4'] ``` ## append(key, value),值追加内容 在redis name对应的值后面追加内容 参数: - name, redis的name - value, 要追加的字符串 ```python >>> r.set('s', 'hello') True >>> r.get('s') 'hello' # 追加字符串,返回追加后字符串的总长度 >>> r.append('s', ', world') 12 >>> r.get('s') 'hello, world' ``` # redis基本命令 hash 感觉类似于name = {'k1': 'v1', 'k2': 'v2'}的字典 ## hset(name, key, value),单个增加、修改取出 name对应的hash中设置一个键值对(不存在,则创建;否则,修改) 参数: - name,redis的name - key,name对应的hash中的key - value,name对应的hash中的value 注: `hsetnx(name, key, value)`,当name对应的hash中不存在当前key时则创建(相当于添加) ```python >>> r.hset('hash1', 'k1', 'v1') 1 >>> r.hset('hash1', 'k2', 'v2') 1 # 获取hash中所有的key >>> print(r.hkeys('hash1')) ['k1', 'k2'] # 取名为hash1中的key对应的值 >>> r.hget('hash1', 'k1') 'v1' # 当hash1中存在key,则进行修改 >>> r.hset('hash1', 'k1', 'v111') 0 >>> r.hget('hash1', 'k1') 'v111' # 去在hash1中多个key对应的值,如果不存在则为None >>> r.hmget('hash1', 'k1', 'k2') ['v1', 'v2'] >>> r.hmget('hash1', 'k1', 'k2', 'k3') ['v1', 'v2', None] # 当name对应的hash1中存在key不添加和更新,如果不存在key则进行新建 >>> r.hsetnx('hash1', 'k2', 'v3') 0 >>> r.hsetnx('hash1', 'k3', 'v3') 1 >>> print(r.hget('hash1', 'k2')) v2 ``` ## hmset(name, mapping),批量增加取出 在name对应的hash中批量设置键值对 参数: - name,redis的name - mapping,字典,如:{'k1':'v1', 'k2': 'v2'} `hget(name,key)` 在name对应的hash中获取根据key获取value `hmget(name, keys, *args)` 在name对应的hash中获取多个key的值 参数: - name,reids对应的name - keys,要获取key集合,如:['k1', 'k2', 'k3'] - *args,要获取的key,如:k1,k2,k3 ```python >>> r.hmset('hash2', {'k1':'v1', 'k2': 'v2'}) True >>> r.hget('hash2', 'k2') 'v2' >>> r.hmget('hash2', 'k1', 'k2') ['v1', 'v2'] >>> r.hmget('hash2', ['k1', 'k2']) ['v1', 'v2'] ``` ## hgetall(name),取出所有键值对 获取name对应hash的所有键值 ```python >>> print(r.hgetall('hash1')) {'k1': 'v111', 'k2': 'v2', 'k3': 'v3'} >>> r.hgetall('hash2') {'k1': 'v1', 'k2': 'v2'} ``` ## hlen(name),所有键值对的格式 hash长度 获取name对应的hash中键值对的个数 ```python >>> print(r.hlen('hash1')) 3 >>> print(r.hlen('hash2')) 2 # 不存在的名 >>> print(r.hlen('hash3')) 0 ``` ## hkeys(name),得到所有的keys 获取name对应的hash中所有的key的值,类似于字典的取所有keys ```python >>> print(r.hkeys('hash2')) ['k1', 'k2'] ``` ## hvals(name),得到所有的值 获取name对应的hash中所有的value的值,类似于字典取所有的value ```python >>> print(r.hvals('hash2')) ['v1', 'v2'] ``` ## hexists(name, key),判断成员是否存在 检查name对应的hash是否存在当前传入的key,类似于字典的in,存在返回False,不存在返回True ```python >>> r.hgetall('hash2') {'k1': 'v1', 'k2': 'v2'} >>> r.hexists('hash2', 'k3') False >>> r.hexists('hash2', 'k1') True ``` ## hdel(name,*keys),删除键值对 将name对应的hash中指定key的键值对删除 ```python >>> r.hdel('hash2', 'k1') 1 >>> print(r.hgetall('hash2')) {'k2': 'v2'} ``` ## hincrby(name, key, amount=1),自增自减整数 自增name对应的hash中的指定key的值value--整数 自增1或者2,或者别的整数 负数就是自减,不存在则创建`key=amount` 参数: - name,redis中的name - key, hash对应的key - amount,自增数(整数) ```python >>> r.hset('hash2', 'k3', 123) 1 >>> r.hgetall('hash2') {'k2': 'v2', 'k3': '123'} >>> r.hincrby('hash2', 'k3', amount=-23) 100 >>> r.hgetall('hash2') {'k2': 'v2', 'k3': '100'} >>> # 不存在的key,则key的值就是amount的值 >>> r.hincrby('hash2', 'k4', amount=5) 5 >>> r.hgetall('hash2') {'k2': 'v2', 'k3': '100', 'k4': '5'} ``` ## hincrbyfloat(name, key, amount=1.0),自增自减浮点数 自增name对应的hash中的指定key的值value--浮点数 自增1.0或者2.0,或者别的浮点数 负数就是自减,不存在则创建key=amount 参数: - name,redis中的name - key, hash对应的key - amount,自增数(浮点数) 自增name对应的hash中的指定key的值,不存在则创建key=amount ```python >>> r.hset('hash2', 'k5', 5.6) 1 >>> r.hincrbyfloat('hash2', 'k5', amount=-2.2) 3.4 >>> print(r.hgetall('hash2')) {'k2': 'v2', 'k3': '100', 'k4': '5', 'k5': '3.4'} >>> >>> r.hincrbyfloat('hash2', 'k6', amount=-1.0) -1.0 >>> print(r.hgetall('hash2')) {'k2': 'v2', 'k3': '100', 'k4': '5', 'k5': '3.4', 'k6': '-1'} ``` ## hscan(name, cursor=0, match=None, count=None),取值查看,分片读取 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆 参数: - name,redis的name - cursor,游标(基于游标分批取获取数据) - match,匹配指定key,默认None 表示所有的key - count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数 如: - 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None) - 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None) - ... - 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕 ```python >>> print(r.hscan('hash2')) (0, {'k2': 'v2', 'k3': '100', 'k4': '5', 'k5': '3.4', 'k6': '-1'}) ``` ## hscan_iter(name, match=None, count=None),创建生成器 利用yield封装hscan创建生成器,实现分批去redis中获取数据 参数: - match,匹配指定key,默认None 表示所有的key - count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数 如: ```python >>> for item in r.hscan_iter('hash2'): ... print(item) ... ('k2', 'v2') ('k3', '100') ('k4', '5') ('k5', '3.4') ('k6', '-1') >>> >>> print(r.hscan_iter('hash2')) # 生成器内存地址 <generator object StrictRedis.hscan_iter at 0x000001D3D2377518> ``` # redis基本命令 list ## lpush(name,values),从列表左边添加元素,没列表就新建 在name对应的list中添加元素,没有该name的列表就新建,每个新的元素都添加到列表的最左边,类似于list的append,只是这里是从左边新增加 ```python # 按元素顺序依次从列表左边插入 >>> r.lpush('list1', 11, 22, 33) 3 >>> print(r.lrange('list1', 0, -1)) ['33', '22', '11'] >>> >>> r.lpush('list1', 10) 4 >>> print(r.lrange('list1', 0, -1)) ['10', '33', '22', '11'] >>> >>> # 按元素顺序依次从列表右边插入 >>> r.rpush('list2', 11, 22, 33) 3 >>> print(r.llen('list2')) 3 >>> print(r.lrange('list2', 0, 3)) ['11', '22', '33'] ``` ## lrange(name, start, end),从列表中获取元素 ```python # 获取列表所有元素 >>> print(r.lrange('list1', 0, -1)) ['10', '33', '22', '11'] # 获取索引号为0-3的元素 >>> print(r.lrange('list2', 0, 3)) ['11', '22', '33'] ``` ## rpush(name,values)从列表右边添加元素,没列表就新建 增加(从右边增加)--没有就新建 ```python >>> r.rpush('list2', 44, 55, 66) 6 >>> print(r.llen('list2')) 6 >>> print(r.lrange('list2', 0, -1)) ['11', '22', '33', '44', '55', '66'] ``` ## lpushx(name,value)从列表左边添加元素,没有列表就不创建 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边,往**已经有的name的列表**的左边添加元素,如果没有这个列表,则不会新建列表并添加元素 ```python >>> r.lpushx('list99', 99) 0 >>> print(r.llen('list99')) 0 >>> print(r.lrange('list99', 0, -1)) [] ``` 往已存在的列表中添加元素 ```python >>> r.lpushx('list2', 77) 7 >>> print(r.llen('list2')) 7 # 取所有元素 >>> print(r.lrange('list2', 0, -1)) ['77', '11', '22', '33', '44', '55', '66'] # 取索引号为0到1的元素 >>> print(r.lrange('list2', 0, 1)) ['77', '11'] # 取索引号为0的元素 >>> print(r.lrange('list2', 0, 0)) ['77'] ``` ## rpushx(name,value)从列表右边添加元素,没有列表就不创建 往已经有的name的列表的右边添加元素,没有的话无法创建 ```python >>> r.rpush('list2', 88) 8 >>> print(r.llen('list2')) 8 >>> print(r.lrange('list2', 0, -1)) ['77', '11', '22', '33', '44', '55', '66', '88'] # 取右边最后一个元素 >>> print(r.lrange('list2', -1, -1)) ['88'] >>> ``` ## linsert(name, where, refvalue, value)),列表插入元素 在name对应的列表的某一个值前或后插入一个新值,固定索引号位置插入元素 参数: - name,redis的name - where,BEFORE或AFTER - refvalue,标杆值,即:在它前后插入数据 - value,要插入的数据 ```python >>> print(r.lrange('list2', 0, -1)) ['77', '11', '22', '33', '44', '55', '66', '88'] # 往列表中左边第一个出现的元素22前(before)插入元素99 >>> r.linsert('list2', 'before', 22, 99) 9 >>> print(r.lrange('list2', 0, -1)) ['77', '11', '99', '22', '33', '44', '55', '66', '88'] >>> # 往列表中左边第一个出现的元素33后(after)插入元素111 >>> r.linsert('list2', 'after', 33, 111) 10 >>> print(r.lrange('list2', 0, -1)) ['77', '11', '99', '22', '33', '111', '44', '55', '66', '88'] ``` ## r.lset(name, index, value),列表指定位置修改值 对name对应的list中的某一个索引位置重新赋值,指定索引号进行修改 参数: - name,redis的name - index,list的索引位置 - value,要设置的值 ```python >>> print(r.lrange('list2', 0, -1)) ['77', '11', '99', '22', '33', '111', '44', '55', '66', '88'] >>> r.lset('list2', 3, -99) True >>> print(r.lrange('list2', 0, -1)) ['77', '11', '99', '-99', '33', '111', '44', '55', '66', '88'] ``` ## r.lrem(name, value, num),列表指定值删除元素 在name对应的list中删除指定的值,指定值进行删除,可以指定删除的个数,返回的是实际删除的元素个数。 参数: - name,redis的name - value,要删除的值 - num, num=0,删除列表中所有的指定值; - num=2,从前到后,删除2个; num=1,从前到后,删除左边第1个 - num=-2,从后向前,删除2个 ```python >>> r.rpush('list4', 11, 1, 22, 1, 33, 1, 44, 1, 55, 1, 66, 1) 12 >>> r.lrange('list4', 0, -1) ['11', '1', '22', '1', '33', '1', '44', '1', '55', '1', '66', '1'] >>> # 将列表中左边的1个‘1’删除,最后一个参数表示删除的元素个数 >>> r.lrem('list4', '1', 1) 1 >>> r.lrange('list4', 0, -1) ['11', '22', '1', '33', '1', '44', '1', '55', '1', '66', '1'] >>> # 将列表右边的1个‘1’删除,最后一个参数‘-’表示右边开始,1表示元素个数为1个 >>> r.lrem('list4', '1', -1) 1 >>> r.lrange('list4', 0, -1) ['11', '22', '1', '33', '1', '44', '1', '55', '1', '66'] >>> # 将列表中所有‘1’删除,参数为0表示所有 >>> r.lrem('list4', '1', 0) 4 >>> r.lrange('list4', 0, -1) ['11', '22', '33', '44', '55', '66'] ``` ## lpop(name),列表左边第一个元素移除,返回值 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素 ```python >>> r.lrange('list4', 0, -1) ['11', '22', '33', '44', '55', '66'] # 删除列表最左边的元素,并且返回删除的元素的值 >>> r.lpop('list4') '11' >>> r.lrange('list4', 0, -1) ['22', '33', '44', '55', '66'] ``` ## rpop(name),列表右边第一个元素移除,返回值 从右向左操作,删除第一个元素,返回删除的元素 ```python >>> r.lrange('list4', 0, -1) ['22', '33', '44', '55', '66'] # 删除列表最右边的元素,并且返回删除的元素值 >>> r.rpop('list4') '66' >>> r.lrange('list4', 0, -1) ['22', '33', '44', '55'] ``` ## ltrim(name, start, end),删除索引之外的所有元素 在name对应的列表中移除没有在start-end索引之间的值 参数: - name,redis的name - start,索引的起始位置 - end,索引结束位置 ```python >>> r.lrange('list4', 0, -1) ['22', '33', '44', '55'] # 删除索引号是1-2之外的元素,值为保留的索引号为1-2的元素 >>> r.ltrim('list4', 1, 2) True >>> r.lrange('list4', 0, -1) ['33', '44'] ``` ## lindex(name, index),根据索引号获取值 在name对应的列表中根据索引获取列表元素 ```python >>> r.lrange('list4', 0, -1) ['33', '44'] # 获取索引号为1的元素 >>> r.lindex('list4', 1) '44' ``` ## rpoplpush(src, dst),移动 元素从一个列表移动到另外一个列表 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边 参数: - src,要取数据的列表的name - dst,要添加数据的列表的name ```python >>> r.rpush('list5', 1, 2, 3) 3 >>> r.rpush('list6', 7, 8, 9) 3 # 移动list5最右边的元素,添加到list6最左边的位置 >>> r.rpoplpush('list5', 'list6') '3' >>> r.lrange('list6', 0, -1) ['3', '7', '8', '9'] ``` ## brpoplpush(src, dst, timeout=0),移动 元素从一个列表移动到另外一个列表 可以设置超时 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧 参数: - src,取出并要移除元素的列表对应的name - dst,要插入元素的列表对应的name - timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞 ```python >>> r.lrange('list5', 0, -1) ['1', '2'] >>> r.lrange('list6', 0, -1) ['3', '7', '8', '9'] >>> # timeout参数为可选 >>> r.brpoplpush('list5', 'list6') '2' >>> r.lrange('list5', 0, -1) ['1'] >>> r.lrange('list6', 0, -1) ['2', '3', '7', '8', '9'] >>> # 移动list5最右边的元素添加到list6最左边,设置超时为2s >>> r.brpoplpush('list5', 'list6', timeout=2) '1' # 当list5中没有元素时,程序会阻塞等待5s才会通过 >>> r.brpoplpush('list5', 'list6', timeout=5) ``` ## blpop(keys, timeout),一次从左向右移除多个列表 将多个列表排列,按照从左到右去pop对应列表的元素 参数: - keys,redis的name的集合 - timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞 ```python >>> r.lpush('list11', 1, 2, 3) 3 >>> r.lpush('list12', 4, 5, 6) 3 >>> while True: ... r.blpop(['list11', 'list12'], timeout=2) ... print('list11:', r.lrange('list11', 0, -1), '; list12:', r.lrange('list12', 0, -1)) ... ('list11', '3') list11: ['2', '1'] ; list12: ['6', '5', '4'] ('list11', '2') list11: ['1'] ; list12: ['6', '5', '4'] ('list11', '1') list11: [] ; list12: ['6', '5', '4'] ('list12', '6') list11: [] ; list12: ['5', '4'] ('list12', '5') list11: [] ; list12: ['4'] ('list12', '4') list11: [] ; list12: [] list11: [] ; list12: [] ``` ## brpop(keys, timeout),一次从右向左移除多个列表 将多个列表排列,按照右向左去pop对应列表的元素 同blpop,将多个列表排列,按照从右像左去移除各个列表内的元素 ## 自定义增量迭代 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要: 1. 获取name对应的所有列表 1. 循环列表 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能: ```python def list_iter(name): """ 自定义redis列表增量迭代 :param name: redis中的name,即:迭代name对应的列表 :return: yield 返回 列表元素 """ list_count = r.llen(name) for index in range(list_count): yield r.lindex(name, index) # 使用 for item in list_iter('list2'): # 遍历这个列表 print(item) ``` # redis基本命令 set ## sadd(name,values),新增集合元素 name对应的集合中添加元素 ```python # 往集合里添加元素 >>> r.sadd('set1', 11, 22, 33, 44) 4 # 获取集合的长度 >>> print(r.scard('set1')) 4 # 获取中所有的成员 >>> print(r.smembers('set1')) {'33', '44', '11', '22'} ``` ## scard(name),获取元素个数,类似于len 获取name对应的集合中元素个数 ```python # 获取集合的长度 >>> print(r.scard('set1')) 4 ``` ## smembers(name),获取集合中所有的成员 获取name对应的集合的所有成员 ```python # 获取中所有的成员 >>> print(r.smembers('set1')) {'33', '44', '11', '22'} ``` ## sscan(name, cursor=0, match=None, count=None),元组形式获取元素 获取集合中所有的成员--元组形式 ```python >>> print(r.sscan('set1')) (0, ['11', '22', '33', '44']) ``` ## sscan_iter(name, match=None, count=None),迭代器方式获取元素 获取集合中所有的成员--迭代器的方式,同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大 ```python >>> for i in r.sscan_iter('set1'): ... print(i) ... 11 22 33 44 ``` ## sdiff(keys, *args),获取两集合的差集 在第一个name对应的集合中且不在其他name对应的集合的元素集合 ```python >>> r.sadd('set2', 11, 33, 55) 3 # 获取集合中的所有成员 >>> r.smembers('set1') {'33', '44', '11', '22'} >>> r.smembers('set2') {'33', '55', '11'} # 在集合set1中有,但不在集合set2中的元素 >>> r.sdiff('set1', 'set2') {'44', '22'} >>> # 在集合set2中有,但不在集合set1中的元素 >>> r.sdiff('set2', 'set1') {'55'} ``` ## sdiffstore(dest, keys, *args),获取差集存在新的集合中 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中 ```python # 在集合set1中存在,但不在集合set2中的元素存在set3中 >>> r.sdiffstore('set3', 'set1', 'set2') 2 >>> r.smembers('set3') {'44', '22'} ``` ## sinter(keys, *args),获取集合的交集 获取多个name对应集合的交集 ```python >>> r.smembers('set1') {'33', '44', '11', '22'} >>> r.smembers('set2') {'33', '55', '11'} # 获取set1和set2的交集 >>> print(r.sinter('set1', 'set2')) {'33', '11'} ``` ## sinterstore(dest, keys, *args),获取集合的交集存在新的集合中 获取多一个name对应集合的并集,再将其加入到dest对应的集合中 ```python # 取set1和set2的交集存在新集合set3中 >>> r.sinterstore('set3', 'set1', 'set2') 2 >>> r.smembers('set3') {'33', '11'} ``` ## sunion(keys, *args),获取集合的并集 获取多个name对应的集合的并集 ```python >>> r.smembers('set1') {'33', '44', '11', '22'} >>> r.smembers('set2') {'33', '55', '11'} # 获取集合set1和set2的并集 >>> print(r.sunion('set1', 'set2')) {'22', '33', '55', '44', '11'} ``` ## sunionstore(dest,keys, *args),获取集合的并集存在新集合中 获取多个name对应的集合的并集,并将结果保存到dest对应的集合中 ```python # 获取集合set1和set2的并集,存在set3中 >>> print(r.sunionstore('set3', 'set1', 'set2')) 5 >>> print(r.smembers('set3')) {'22', '33', '55', '44', '11'} ``` ## sismember(name, value),判断是否是集合成员 检查value是否是name对应的集合的成员,结果为True和False,类似in ```python >>> print(r.smembers('set3')) {'22', '33', '55', '44', '11'} >>> # 55是集合set3的成员 >>> print(r.sismember('set3', 55)) True >>> print(r.sismember('set3', 66)) False ``` ## smove(src, dst, value),从一集合将某个成员移动到另一集合 将某个成员从一个集合中移动到另外一个集合 ```python >>> r.smembers('set1') {'22', '33', '44', '11'} >>> r.smembers('set2') {'55', '33', '11'} >>> # 移动目标集合已有的元素,可以移动成功,但目标集合也不会增加 >>> r.smove('set1', 'set2', 11) True >>> r.smembers('set1') {'22', '33', '44'} >>> r.smembers('set2') {'55', '33', '11'} >>> # 移动目标集合没有的元素,源集合删除,目标集合增加 >>> r.smove('set1', 'set2', 22) True >>> r.smembers('set1') {'33', '44'} >>> r.smembers('set2') {'55', '22', '33', '11'} # 移动源集合中不存在的元素,返回False >>> r.smove('set1', 'set2', 99) False ``` ## spop(name),删除,随机删除并且返回被删除值 从集合移除一个成员,并将其返回,说明一下,集合是无序的,所有是随机删除的 ```python >>> r.smembers('set2') {'55', '22', '33', '11'} # 随机删除值 >>> print(r.spop('set2')) 22 >>> r.smembers('set2') {'55', '33', '11'} ``` ## srem(name, values),删除,指定值删除 在name对应的集合中删除某些值 ```python >>> r.smembers('set2') {'22', '33', '44', '11'} # 删除一个元素 >>> print(r.srem('set2', 44)) 1 >>> r.smembers('set2') {'22', '33', '11'} # 指定多个元素删除 >>> print(r.srem('set2', 11, 22)) 2 >>> r.smembers('set2') {'33'} ``` # redis基本命令 有序set Set操作,Set集合就是不允许重复的列表,本身是无序的 有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较, 所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。 ## zadd(name, *args, **kwargs),新增 在name对应的有序集合中添加元素 ```python >>> r.zadd('zset1', n1=11, n3=22) 2 >>> r.zadd('zset2', 'm1', 22, 'm2', 44) 2 # 获取有序集合的长度 >>> print(r.zcard('zset1')) 2 >>> print(r.zcard('zset2')) 2 # 获取集合中的所有元素 >>> print(r.zrange('zset1', 0, -1)) ['n1', 'n3'] >>> print(r.zrange('zset2', 0, -1)) ['m1', 'm2'] # 获取集合中的所有元素和分数,以列表中的元组展示出来 >>> print(r.zrange('zset2', 0, -1, withscores=True)) [('m1', 22.0), ('m2', 44.0)] ``` ## zcard(name),获取有序集合元素个数,类似于len 获取name对应的有序集合元素的数量 ```python # 获取有序集合的长度 >>> print(r.zcard('zset1')) 2 ``` ## zrange( name, start, end, desc=False, withscores=False, score_cast_func=float),获取有序集合的所有元素 按照索引范围获取name对应的有序集合的元素,默认按照分数从小打大排序 参数: * name,redis的name * start,有序集合索引起始位置(非分数) * end,有序集合索引结束位置(非分数) * desc,排序规则,默认按照分数从小到大排序 * withscores,是否获取元素的分数,默认只获取元素的值 * score_cast_func,对分数进行数据转换的函数 ## zrevrange(name, start, end, withscores=False, score_cast_func=float), 从大到小排序 同zrange,集合是**从大到小排序**的 ```python >>> print(r.zrange('zset1', 0, -1)) ['n1', 'n3'] # 集合按照从大到小排序,只获取元素,不显示分数 >>> print(r.zrevrange('zset1', 0, -1)) ['n3', 'n1'] # 获取有序结合中所有元素和分数,按照分数倒序 >>> print(r.zrevrange('zset1', 0, -1, withscores=True)) [('n3', 22.0), ('n1', 11.0)] ``` ## zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float),按照分数范围获取name对应的有序集合的元素 按照分数范围获取name对应的有序集合的元素,**默认从小到大排序** ```python >>> for i in range(1, 30): ... element = 'n' + str(i) ... r.zadd('zset3', element, i) ... 1 1 # ... 1 1 # 在分数是15---25之间,取出符合条件的元素 >>> print(r.zrangebyscore('zset3', 15, 25)) ['n15', 'n16', 'n17', 'n18', 'n19', 'n20', 'n21', 'n22', 'n23', 'n24', 'n25'] # 在分数是10---20之间,取出符合条件的元素(带分数) >>> print(r.zrangebyscore('zset3', 10, 20, withscores=True)) [('n10', 10.0), ('n11', 11.0), ('n12', 12.0), ('n13', 13.0), ('n14', 14.0), ('n15', 15.0), ('n16', 16.0), ('n17', 17.0), ('n18', 18.0), ('n19', 19.0), ('n20', 20.0)] ``` ## zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float),按照分数范围获取有序集合的元素并排序 按照分数范围获取有序集合的元素并排序(**默认从大到小排序**) ```python # 在分数是20---12之间,取出符合条件的元素 按照分数倒序 >>> print(r.zrevrangebyscore('zset3', 20, 12, withscores=True)) [('n20', 20.0), ('n19', 19.0), ('n18', 18.0), ('n17', 17.0), ('n16', 16.0), ('n15', 15.0), ('n14', 14.0), ('n13', 13.0), ('n12', 12.0)] ``` ## zscan(name, cursor=0, match=None, count=None, score_cast_func=float),获取所有元素--默认按照分数顺序排序 获取所有元素--默认按照分数顺序排序 ```python >>> print(r.zscan('zset3')) (0, [('n1', 1.0), ('n2', 2.0), ('n3', 3.0), ('n4', 4.0), ('n5', 5.0), ('n6', 6.0), ('n7', 7.0), ('n8', 8.0), ('n9', 9.0), ('n10', 10.0), ('n11', 11.0), ('n12', 12.0), ('n13', 13.0), ('n14', 14.0), ('n15', 15.0), ('n16', 16.0), ('n17', 17.0), ('n18', 18.0), ('n19', 19.0), ('n20', 20.0), ('n21', 21.0), ('n22', 22.0), ('n23', 23.0), ('n24', 24.0), ('n25', 25.0), ('n26', 26.0), ('n27', 27.0), ('n28', 28.0), ('n29', 29.0)]) ``` ## zscan_iter(name, match=None, count=None,score_cast_func=float),获取所有元素--迭代器 获取所有元素--迭代器 ```python >>> for i in r.zscan_iter('zset3'): ... print(i) ... ('n1', 1.0) ('n2', 2.0) ('n3', 3.0) # 。。。 ('n27', 27.0) ('n28', 28.0) ('n29', 29.0) ``` ## zcount(name, min, max),获取分数区间内元素个数 获取name对应的有序集合中分数 在 [min,max] 之间的个数,包含min和max两个值 ```python >>> print(r.zrange('zset3', 0, -1, withscores=True)) [('n1', 1.0), ('n2', 2.0), ('n3', 3.0), ('n4', 4.0), ('n5', 5.0), ('n6', 6.0), ('n7', 7.0), ('n8', 8.0), ('n9', 9.0), ('n10', 10.0), ('n11', 11.0), ('n12', 12.0), ('n13', 13.0), ('n14', 14.0), ('n15', 15.0), ('n16', 16.0), ('n17', 17.0), ('n18', 18.0), ('n19', 19.0), ('n20', 20.0), ('n21', 21.0), ('n22', 22.0), ('n23', 23.0), ('n24', 24.0), ('n25', 25.0), ('n26', 26.0), ('n27', 27.0), ('n28', 28.0), ('n29', 29.0)] # 获取分数在6和12之间的元素个数,且包含6和12 >>> print(r.zcount('zset3', 6, 12)) 7 ``` ## zincrby(name, value, amount),自增 自增name对应的有序集合的 name 对应的分数 ```python # 每次讲n3的分数自增30,最终会放到有序集合的最后 >>> r.zincrby('zset3', 'n3', amount=30) 33.0 >>> print(r.zrange('zset3', 0, -1, withscores=True)) [('n1', 1.0), ('n2', 2.0), ('n4', 4.0), ('n5', 5.0), ('n6', 6.0), ('n7', 7.0), ('n8', 8.0), ('n9', 9.0), ('n10', 10.0), ('n11', 11.0), ('n12', 12.0), ('n13', 13.0), ('n14', 14.0), ('n15', 15.0), ('n16', 16.0), ('n17', 17.0), ('n18', 18.0), ('n19', 19.0), ('n20', 20.0), ('n21', 21.0), ('n22', 22.0), ('n23', 23.0), ('n24', 24.0), ('n25', 25.0), ('n26', 26.0), ('n27', 27.0), ('n28', 28.0), ('n29', 29.0), ('n3', 33.0)] ``` ## zrank(name, value),获取值的索引号,从小到大排序 获取某个值在 name对应的有序集合中的索引(从 0 开始) ```python >>> print(r.zrank('zset3', 'n6')) 4 # n1的索引号是0 这里按照分数顺序(从小到大) >>> print(r.zrank('zset3', 'n1')) 0 ``` ## zrevrank(name, value),获取值的索引号,从大到小排序 ```python # 按照从大到小排序,共29个元素,最小的n1索引号为28 >>> print(r.zrevrank('zset3', 'n1')) 28 >>> print(r.zrevrank('zset3', 'n3')) 0 ``` ## zrem(name, values),删除,指定值删除 删除name对应的有序集合中值是values的成员 ```python >>> print(r.zrange('zset3', 0, -1)) ['n1', 'n2', 'n4', 'n5', 'n6', 'n7', 'n8', 'n9', 'n10', 'n11', 'n12', 'n13', 'n14', 'n15', 'n16', 'n17', 'n18', 'n19', 'n20', 'n21', 'n22', 'n23', 'n24', 'n25', 'n26', 'n27', 'n28', 'n29', 'n3'] # 删除集合中的元素n3,由于集合不重复,只删除一个就不存在重复的了 >>> r.zrem('zset3', 'n1') 1 >>> print(r.zrange('zset3', 0, -1)) ['n2', 'n4', 'n5', 'n6', 'n7', 'n8', 'n9', 'n10', 'n11', 'n12', 'n13', 'n14', 'n15', 'n16', 'n17', 'n18', 'n19', 'n20', 'n21', 'n22', 'n23', 'n24', 'n25', 'n26', 'n27', 'n28', 'n29', 'n3'] ``` ## zremrangebyrank(name, min, max),删除,根据索引号范围删除 根据排行范围删除,按照索引号来删除 ```python # 删除有序集合中索引号从1---12的元素 >>> r.zremrangebyrank('zset3', 1, 12) 12 >>> print(r.zrange('zset3', 0, -1)) ['n2', 'n16', 'n17', 'n18', 'n19', 'n20', 'n21', 'n22', 'n23', 'n24', 'n25', 'n26', 'n27', 'n28', 'n29', 'n3'] ``` ## zremrangebyscore(name, min, max),删除,根据分数范围删除 根据分数范围删除 ```python # 删除有序集合中分数为18---23的元素 >>> r.zremrangebyscore('zset3', 18, 23) 6 >>> print(r.zrange('zset3', 0, -1)) ['n2', 'n16', 'n17', 'n24', 'n25', 'n26', 'n27', 'n28', 'n29', 'n3'] ``` ## zscore(name, value),获取对应的分数,判断是否为成员 获取name对应有序集合中 value 对应的分数,**如果不是集合的成员,将返回None** ```python # 获取元素n24对应的分数 >>> print(r.zscore('zset3', 'n24')) 24.0 ``` # 其他常用操作 ## delete(*names),删除 根据删除redis中的任意数据类型(string、hash、list、set、有序set) ```python # 从redis中删除名称为zset3的键值对 >>> r.delete('zset3') 1 ``` ## exists(name),检查名字是否存在 检测redis的name是否存在,存在就是True,False 不存在 ```python >>> r.exists('zset3') False >>> r.exists('zset1') True ``` ## keys(pattern=''),模糊匹配 根据模型获取redis的name 更多: * KEYS `*` 匹配数据库中所有 key 。 * KEYS `h?llo` 匹配 hello , hallo 和 hxllo 等。 * KEYS `h*llo` 匹配 hllo 和 heeeeello 等。 * KEYS `h[ae]llo` 匹配 hello 和 hallo ,但不匹配 hillo ```python >>> print(r.keys('foo*')) ['foo2', 'foo', 'foo1'] ``` ## expire(name ,time),设置超时时间 为某个redis的某个name设置超时时间,单位为秒 ```python >>> r.lpush('list22', 11 ,22) 2 # 设置超时时间为10s >>> r.expire('list22', 10) True >>> r.lrange('list22', 0 ,-1) ['22', '11'] # 10s后删除值 >>> r.lrange('list22', 0 ,-1) [] ``` ## rename(src, dst),重命名 对redis的name重命名 ```python >>> r.lpush('list22', 11 ,22) 2 >>> r.lrange('list22', 0 ,-1) ['22', '11'] >>> r.rename('list22', 'list22-new') True >>> r.lrange('list22', 0 ,-1) [] >>> r.lrange('list22-new', 0 ,-1) ['22', '11'] ``` ## randomkey(),随机获取name 随机获取一个redis的name(不删除) ```python >>> print(r.randomkey()) foo >>> print(r.randomkey()) zset1 >>> print(r.randomkey()) fruit ``` ## type(name),获取name的值类型 获取name对应值的类型 ```python >>> print(r.type('zset1')) zset >>> print(r.type('set1')) set # 对于不存在的name,则会返回none >>> print(r.type('list22')) none >>> print(r.type('list22-new')) list ``` ## scan(cursor=0, match=None, count=None),查看所有元素 ```python print(r.hscan("hash2")) print(r.sscan("set3")) print(r.zscan("zset2")) print(r.getrange("foo1", 0, -1)) print(r.lrange("list2", 0, -1)) print(r.smembers("set3")) print(r.zrange("zset3", 0, -1)) print(r.hgetall("hash1")) ``` ## scan_iter(match=None, count=None),查看所有元素,迭代器 ```python for i in r.hscan_iter("hash1"): print(i) for i in r.sscan_iter("set3"): print(i) for i in r.zscan_iter("zset3"): print(i) ``` ## 其他方法 ```python # 查询key为name的值 >>> print(r.get('name')) LR >>> # 删除key为gender的键值对 >>> r.delete('gender') 1 # 查询所有的key >>> print(r.keys()) ['list4', 'list1', 'list10', 'key2', 'list2', 'zset2', 'set3', 'set2', 'set1', 's', 'k1', 'k4', 'hash2', 'fruit1', 'list6', 'zset1', 'key1', 'visit:12306:totals', 'list22-new', 'fruit', 'name', 'k3', 'foo2', 'list3', 'hash1', 'foo', 'foo1', 'k2'] # 查看当前redis包含多少条数据 >>> print(r.dbsize()) 28 # 执行“检查点”操作,将数据写回磁盘,包村时阻塞 >>> r.save() True # 清空r中的所有数据 >>> r.flushdb() True >>> print(r.keys()) [] ``` # 管道(pipeline) redis默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作。 如果想要在一次请求中指定多个命令,则可以使用`pipline`实现一次请求指定多个命令,并且默认情况下一次`pipline` 是原子性操作。 管道(pipeline)是redis在提供单个请求中缓冲多条服务器命令的基类的子类。它通过减少服务器-客户端之间反复的TCP数据库包,从而大大提高了执行批量命令的功能。 ```python >>> import redis >>> pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True) >>> r = redis.Redis(connection_pool=pool) # pipe = r.pipeline(transaction=False) # 默认的情况下,管道里执行的命令可以保证执行的原子性,执行pipe = r.pipeline(transaction=False)可以禁用这一特性。 # pipe = r.pipeline(transaction=True) # 创建一个管道 >>> pipe = redis.Redis().pipeline() ```

很赞哦! (1)

文章交流

  • emoji
0人参与,0条评论

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行552天
  • 系统信息:Linux
  • 后台程序:Python: 3.6.6
  • 网站框架:Django: 2.2.9
  • 文章统计:219 篇
  • 文章评论:37 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 微信公众号:扫描二维码,关注我们
返回
顶部
标题 换行 登录
网站