• python redis 方法大全


    redis连接

    1,安装redis

    pip install redis

    实例:

    import redis
    from datetime import datetime
    r
    = redis.Redis(host='localhost', port=6379, db=15,decode_responses=True) r.set('name', 'lvye') print(r['name']) print(r.get('name')) print(type(r.get('name')))

    lvye
    lvye
    <class 'str'>

    decode_responses=True,写入的键值对中的value为str类型,不加这个参数写入的则为字节类型。

    2、连接池

    redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。

    默认,每个Redis实例都会维护一个自己的连接池。

    可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池

    import redis    
    
    pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)   # host是redis主机,需要redis服务端和客户端都起着 redis默认端口是6379
    r = redis.Redis(connection_pool=pool)
    r.set('gender', 'male')     # key是"gender" value是"male" 将键值对存入redis缓存
    print(r.get('gender'))      # gender 取出键male对应的值

    3,string redis基本命令

    1,在Redis中设置值,默认,不存在则创建,存在则修改
      

    set(name, value, ex=None, px=None, nx=False, xx=False)

       参数:

    • ex,过期时间(秒)
    • px,过期时间(毫秒)
    • nx,如果设置为True,则只有name不存在时,当前set操作才执行
    • xx,如果设置为True,则只有name存在时,当前set操作才执行

    2,setnx(name, value)
    设置值,只有name不存在时,执行设置操作(添加)

    print(r.setnx('fruit1', 'banana'))  # fruit1不存在,输出为True

    白话解释,不存在则直接创建新的,存在则返回false,不可以修改。

    3,setex(name, value, time)

    参数: time,期时间(数字秒 或 timedelta对象)

    r.setex("fruit2", "orange", 5)
    time.sleep(5)
    print(r.get('fruit2'))  # 5秒后,取值就从orange变成None

    4.psetex(name, time_ms, value)

    time_ms,过期时间(数字毫秒 或 timedelta对象)

    r.psetex("fruit3", 5000, "apple")
    time.sleep(5)
    print(r.get('fruit3'))  

    5,批量设置值  mset(*args, **kwargs)

    import redis
    r = redis.Redis(host='localhost', port=6379, db=15,decode_responses=True)
    r.mget({'k1': 'v1', 'k2': 'v2'})
    r.mset(k1="v1", k2="v2") # 这里k1 和k2 不能带引号 一次设置对个键值对
    print(r.mget("k1", "k2"))   # 一次取出多个键对应的值
    #['v1', 'v2']
    print(r.mget("k1"))
    #['v1']

    6,批量获取值,

    print(r.mget('k1', 'k2'))
    print(r.mget(['k1', 'k2']))

    7,设置新值并获取原来的值

    r.set('name','yang')
    s = r.getset('name','zhenyu')
    print(s)

    #yang

    8,返回name对应值的字节长度(一个汉字3个字节)

    s = r.strlen('name')
    print(s)

    9,ncr(self, name, amount=1)

    r.set("visit:12306:totals", 34634)
    r.incr("visit:12306:totals")
    r.incr("visit:12306:totals")
    print(r.get("visit:12306:totals"))
    解析: 设初始值,不指定amount 默认为1 ,上面最后输出 34636
     
    应用场景 – 页面点击数
    假定我们对一系列页面需要记录点击次数。
    例如论坛的每个帖子都要记录点击次数,而点击次数比回帖的次数的多得多。如果使用关系数据库来存储点击,可能存在大量的行级锁争用。
    所以,点击数的增加使用redis的INCR命令最好不过了。 
    当redis服务器启动时,可以从关系数据库读入点击数的初始值(12306这个页面被访问了34634次)
     
    10,incrbyfloat(self, name, amount=1.0)
     
    自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
    r.set("foo1", "123.0")
    r.set("foo2", "221.0")
    print(r.mget("foo1", "foo2"))
    r.incrbyfloat("foo1", amount=2.0)
    r.incrbyfloat("foo2", amount=3.0)
    print(r.mget("foo1", "foo2"))
    
    ['123.0', '221.0']
    ['125', '224']

    11,decr(self, name, amount=1)

    自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。

    r.decr("foo5", amount=3) # 递减3
    r.decr("foo6", amount=1) # 递减1
    print(r.mget("foo5", "foo6"))
    #['-3', '-1']

    12,append(key, value)

    在redis name对应的值后面追加内容

    r.append('name','yang')
    print(r.get('name'))

    #zhenyuyang

     4,hash redis基本命令 

    1.hset(name, key, value)

    单个增加--修改(单个取出)--没有就新增,有的话就修改

    r.hset("hash1", "k1", "v1")
    r.hset("hash1", "k2", "v2")
    
    print(r.hkeys("hash1")) # 取hash中所有的key
    #['k1', 'k2']
    print(r.hget("hash1", "k1"))    # 单个取hash的key对应的值
    # v1
    print(r.hmget("hash1", "k1", "k2"))  # 多个取hash的key对应的值
    # ['v1', 'v2']


    r.hsetnx("hash1", "k3", "v3") # 只能新建
    print(r.hget("hash1", "k3"))
    #  这个命令只能新建,不会覆盖原来的值

    hget(name,key)

    在name对应的hash中获取根据key获取value

    hmget(name, keys, *args)
    在name对应的hash中获取多个key的值

     2,hmset(name, mapping)

    批量插入数据,

    参数:
    name,redis的name
    mapping,字典,如:{'k1':'v1', 'k2': 'v2'}

    r.hmset("hash2", {"k2": "v2", "k3": "v3"})
    print(r.hmget('hash2','k2','k3'))
    #['v2', 'v3']

     3,hgetall(name)

    获取所有的键值对

    r.hmset("hash2", {"k2": "v2", "k3": "v3"})
    print(r.hgetall('hash2'))
    #{'k2': 'v2', 'k3': 'v3'}

    4,hlen(name)

    获取键值对长度,说白了就是计算几个字典而已

    r.hmset("hash2", {"k2": "v2", "k3": "v3"})
    print(r.hgetall('hash2'))
    #{'k2': 'v2', 'k3': 'v3'}
    print(r.hlen('hash2'))
    #2

    5,hkeys(name)

    获取所有的键,返回列表

    r.hmset("hash2", {"k2": "v2", "k3": "v3"})
    print(r.hkeys('hash2'))
    #['k2', 'k3']

    6,hvals(name)

    获取所有的值,返回列表

    r.hmset("hash2", {"k2": "v2", "k3": "v3"})
    print(r.hvals('hash2'))
    #['v2', 'v3']

    7,hexists(name, key)
    判断成员是否存在(类似字典的in)

    r.hmset("hash2", {"k2": "v2", "k3": "v3"})
    s = r.hexists('hash2','k2')
    print(s)
    #True

    8,hdel(name,*keys)

    删除键值对

    #{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
    r.hdel("hash1", "k1")    # 删除一个键值对
    print(r.hgetall("hash1"))
    #{'k2': 'v222', 'k3': 'v3', 'k11': 'v1'}

    9,hincrby(name, key, amount=1)

    自增name对应的hash中的指定key的值,不存在则创建key=amount

    r.hset("hash1", "k3", 123)
    r.hincrby("hash1", "k3", amount=-1)
    print(r.hgetall("hash1"))
    #{'k3': '122'}
    r.hincrby("hash1", "k4", amount=1)  # 不存在的话,value默认就是1
    print(r.hgetall("hash1"))
    #{'k3': '122', 'k4': '1'}

    10,hincrbyfloat(name, key, amount=1.0)

    自增自减浮点数(将key对应的value--浮点数 自增1.0或者2.0,或者别的浮点数 负数就是自减)

    r.hset("hash1", "k5", "1.0")
    r.hincrbyfloat("hash1", "k5", amount=-1.0)    # 已经存在,递减-1.0
    print(r.hgetall("hash1"))
    #{'k5': '0'}
    r.hincrbyfloat("hash1", "k6", amount=-1.0)    # 不存在,value初始值是-1.0 每次递减1.0
    print(r.hgetall("hash1"))
    #{'k5': '0', 'k6': '-1'}

    11,hscan_iter(name, match=None, count=None)

    利用yield封装hscan创建生成器,实现分批去redis中获取数据

    参数:
    match,匹配指定key,默认None 表示所有的key
    count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数

    print(r.hgetall('hash1'))
    #{'k5': '0', 'k6': '-1'}
    for item in r.hscan_iter('hash1'):
        print(item)
    #('k5', '0')
    #('k6', '-1')   #f返回键值对元祖
    5,list redis基本命令 

     1,lpush(name,values)

    增加(类似于list的append,只是这里是从左边新增加)--没有就新建

    r = redis.Redis(host='localhost', port=6379, db=15,decode_responses=True)
    r.lpush("list1", 11, 22, 33)
    print(r.lrange('list1', 0, -1))
    #['33', '22', '11']

    扩展:

    r.rpush("list2", 11, 22, 33)      # 表示从右向左操作
    print(r.llen("list2"))         # 列表长度
    print(r.lrange("list2", 0, 3))    # 切片取出值,范围是索引号0-3

    2,lpushx(name,value)

    往已经有的name的列表的左边添加元素,没有的话无法创建

    r.lpushx("list1", 11)  #该list1必须存在,否则无法插入

    3,rpushx(name,value)

    往已经有的name的列表的右边添加元素,没有的话无法创建

    print(r.lrange('list1',0,-1))
    #['1100', '11', '33', '22', '11', '33', '22', '11', '1100']
    r.rpushx("list1", 1100)   #增加
    print(r.lrange('list1',0,-1))
    #['1100', '11', '33', '22', '11', '33', '22', '11', '1100', '1100']

    4,linsert(name, where, refvalue, value))

    在name对应的列表的某一个值前或后插入一个新值
    参数:
    name,redis的name
    where,BEFORE或AFTER
    refvalue,标杆值,即:在它前后插入数据
    value,要插入的数据

    r.linsert("list1", "before", "11", "00")     #意思是在11 之前插入00

    5,r.lset(name, index, value)

    对name对应的list中的某一个索引位置重新赋值
    参数:
    name,redis的name
    index,list的索引位置
    value,要设置的值

    print(r.lrange('list1',0,-1))
    #['1100', '00', '11', '33', '22', '11', '33', '22', '11', '1100', '1100']
    r.lset('list1',1,'222')     #对下标1的值改为222
    print(r.lrange('list1',0,-1))
    #['1100', '222', '11', '33', '22', '11', '33', '22', '11', '1100', '1100']

    6,r.lrem(name, value, num)

    在name对应的list中删除指定的值

    参数:
    name,redis的name
    value,要删除的值
    num, 

    num=0,删除列表中所有的指定值;
    num=2,从前到后,删除2个; num=1,从前到后,删除左边第1个
    num=-2,从后向前,删除2个

    print(r.lrange("list1", 0, -1))
    #['1100', '222', '11', '33', '22', '11', '33', '22', '11', '1100', '1100']
    r.lrem('list1','1100',4)  #左边出现的11,后面指定删除数量
    print(r.lrange("list1", 0, -1))
    #['1100', '222', '33', '22', '11', '33', '22', '11', '1100', '1100']

    7,lpop(name)

    在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素

    s = r.lpop('list1')  #从左边删除第一个,并返回
    s = r.rpop('list1')  #从右边删除第一个,并返回

    8,ltrim(name, start, end)

    在name对应的列表中移除没有在start-end索引之间的值
    参数:
    name,redis的name
    start,索引的起始位置
    end,索引结束位置

    print(r.lrange("list1", 0, -1))
    #['33', '22', '11', '22', '33', '22']
    r.ltrim('list1',0,2)  #下标0-2也就是3个数,不在这个范围的删除
    print(r.lrange("list1", 0, -1))
    #['33', '22', '11']

    9,lindex(name, index)

    在name对应的列表中根据索引获取列表元素

    print(r.lrange("list1", 0, -1))
    #['33', '22', '11']
    print(r.lindex('list1',0))
    #33

    10,rpoplpush(src, dst)

    移动 元素从一个列表移动到另外一个列表

    # list1 ['33', '22']
    # list2 ['11', '5', '4', '3', '2', '1']
    r.rpoplpush('list1','list2') #可以看出从第一个列表最右边一个移到第二个列表的最左侧 print(r.lrange("list1", 0, -1)) print(r.lrange("list2", 0, -1)) #['33'] #['22', '11', '5', '4', '3', '2', '1']

    11,brpoplpush(src, dst, timeout=0)

    移动 元素从一个列表移动到另外一个列表 可以设置超时

    参数:
    src,取出并要移除元素的列表对应的name
    dst,要插入元素的列表对应的name
    timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞

    r.brpoplpush('list1','list2' , timeout=0)
    print(r.lrange("list1", 0, -1))
    print(r.lrange("list2", 0, -1))
    #['33']
    6,set redis基本命令

    1,sadd(name,values)

    name对应的集合中添加元素

    r.sadd("set1", 33, 44, 55, 66)  # 往集合中添加元素
    print(r.scard("set1"))  # 集合的长度是4
    print(r.smembers("set1"))   # 获取集合中所有的成员
    #{'33', '55', '44', '66'}

    2.获取元素个数 类似于 len

    scard(name)

    获取 name 对应的集合中元素个数

    3.获取集合中所有的成员

    smembers(name)

    获取name对应的集合的所有成员

    4, sscan('set1')

    获取集合中所有的成员--元组形式

    r.sadd("set1", 33, 44, 55, 66)  # 往集合中添加元素
    print(r.sscan('set1'))
    #(0, ['33', '44', '55', '66'])

    5,sscan_iter()

    获取集合中所有的成员--迭代器的方式

    for i in r.sscan_iter('set1'):
        print(i)
    '''
    33
    44
    55
    66
    '''

    6,sismember(name, value)

    判断是否是集合的成员 类似in

    print(r.smembers("set2"))
    #{'55', '33', '66', '44'}
    print(r.sismember('set2', '33'))
    True

    7,smove(src, dst, value)

    将某个成员从一个集合中移动到另外一个集合

    print(r.smembers("set2"))
    #{'55', '66', '33', '44'}
    print(r.smembers("set3"))
    #{'11', '22', '33'}
    r.smove("set2", "set3", '44')    #移动
    
    print(r.smembers("set2"))
    #{'66', '55', '33'}
    print(r.smembers("set3"))
    #{'44', '11', '22', '33'}

    8,spop(name)

    从集合移除一个成员,并将其返回,说明一下,集合是无序的,所有是随机删除的

    print(r.smembers("set2"))
    s = r.spop('set2')
    print(s)
    print(r.smembers("set2"))
    '''
    {'66', '55', '33'}
    66
    {'55', '33'}
    '''

    ’8,srem(name, values)

    删除--指定值删除

    print(r.smembers("set2"))
    r.srem('set2','55')
    print(r.smembers("set2"))
    '''
    {'33', '55'}
    {'33'}
    '''
    7,redis基本命令 有序set
    • Set操作,Set集合就是不允许重复的列表,本身是无序的
    • 有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,
    • 所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。

    1,zadd()

    向有序集合添加元素

    r.zadd("zset1", n1=11, n2=22)
    r.zadd("zset2", 'm1', 22, 'm2', 44)
    print(r.zcard("zset1")) # 集合长度
    print(r.zcard("zset2")) # 集合长度
    print(r.zrange("zset1", 0, -1))   # 获取有序集合中所有元素
    print(r.zrange("zset2", 0, -1, withscores=True))   # 获取有序集合中所有元素和分数
    
    2
    2
    ['n1', 'n2']
    [('m1', 22.0), ('m2', 44.0)]

    2.获取有序集合元素个数 类似于len

    zcard(name)
    print(r.zcard("zset1")) # 集合长度

    获取name对应的有序集合元素的数量

    3.r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)

    按照索引范围获取name对应的有序集合的元素
    参数:
    name,redis的name
    start,有序集合索引起始位置(非分数)
    end,有序集合索引结束位置(非分数)
    desc,排序规则,默认按照分数从小到大排序
    withscores,是否获取元素的分数,默认只获取元素的值
    score_cast_func,对分数进行数据转换的函数

    4,zrevrange(name, start, end, withscores=False, score_cast_func=float)

    同zrange,集合是从大到小排序的

    r.zadd("zset1", n1=11, n2=22)
    print(r.zrevrange("zset1", 0, -1)) # 只获取元素,不显示分数 print(r.zrevrange("zset1", 0, -1, withscores=True)) # 获取有序集合中所有元素和分数,分数倒序 ''' ['n2', 'n1'] [('n2', 22.0), ('n1', 11.0)] '''

    5,zscan_iter(name, match=None, count=None,score_cast_func=float)

    获取所有元素--迭代器

    r.zadd("zset1", n1=11, n2=22)
    for i in r.zscan_iter("zset1"): # 遍历迭代器
        print(i)
    '''
    ('n1', 11.0)
    ('n2', 22.0)
    '''

    6,zcount(name, min, max)

    获取name对应的有序集合中分数 在 [min,max] 之间的个数

    r.zadd("zset1", n1=11, n2=22)
    print(r.zrange("zset1", 0, -1, withscores=True))
    print(r.zcount("zset1", 11, 22))
    '''
    [('n1', 11.0), ('n2', 22.0)]
    2
    '''

    7,自增name对应的有序集合的 name 对应的分数

    zincrby(name, value, amount)

    r.zadd("zset1", n1=11, n2=22)
    r.zincrby("zset1", "n2", amount=2)    # 每次将n2的分数自增2
    print(r.zrange("zset1", 0, -1, withscores=True))
    #[('n1', 11.0), ('n2', 24.0)]

    8,zrank(name, value)

    获取值的索引号

    r.zadd("zset1", n1=11, n2=22)
    print(r.zrank("zset1", "n1"))   # n1的索引号是0 这里按照分数顺序(从小到大)
    #0
    print(r.zrank("zset1", "n2"))   # n2的索引号是1
    #1
    print(r.zrevrank("zset1", "n1"))    # n1的索引号是1 这里安照分数倒序(从大到小)
    #1

    9,zrem(name, values)

    删除name对应的有序集合中值是values的成员

    r.zadd("zset1", n1=11, n2=22)
    r.zrem("zset1", "n1")   # 删除有序集合中的元素n1 删除单个
    print(r.zrange("zset1", 0, -1))
    #['n2']

    10,zremrangebyrank(name, min, max)

    删除--根据排行范围删除,按照索引号来删除

    r.zadd("zset1", n1=11, n2=22,n3=33,n4=55)
    r.zremrangebyrank('zset1',0,2)   #删除这个区间的元素,包括2
    print(r.zrange("zset1", 0, -1))
    #['n4']   

    11,zremrangebyscore(name, min, max)

    根据分数范围删除

    r.zadd("zset1", n1=11, n2=22,n3=33,n4=55)
    r.zremrangebyscore('zset1',0,22)   #删除这个区间的分数,包括22
    print(r.zrange("zset1", 0, -1))
    #['n3', 'n4']

    12,zscore(name, value)

    获取值对应的分数

    r.zadd("zset1", n1=11, n2=22,n3=33,n4=55)
    print(r.zscore('zset1', 'n1'))  
    #11.0
    8、其他常用操作

    1.删除
    delete(*names)
    根据删除redis中的任意数据类型(string、hash、list、set、有序set)

    r.delete("list2")  # 删除key为gender的键值对

    2.检查名字是否存在
    exists(name)
    检测redis的name是否存在,存在就是True,False 不存在

    print(r.exists('set1'))

    3.模糊匹配

    keys(pattern='')
    更多:
    KEYS * 匹配数据库中所有 key 。
    KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
    KEYS hllo 匹配 hllo 和 heeeeello 等。
    KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
    4.设置超时时间
    expire(name ,time)
    为某个redis的某个name设置超时时间
    r.lpush("list5", 11, 22)
    r.expire("list5", time=3)
    print(r.lrange("list5", 0, -1))
    time.sleep(3)
    print(r.lrange("list5", 0, -1))

    5.重命名
    rename(src, dst)
    对redis的name重命名

    r.lpush("list5", 11, 22)
    r.rename("list5", "list5-1")

    6.随机获取name
    randomkey()
    随机获取一个redis的name(不删除)

    print(r.randomkey())

    7.获取类型
    type(name)
    获取name对应值的类型

    print(r.type("set1"))
    print(r.type("hash2"))

    8.查看所有元素

    scan(cursor=0, match=None, count=None)

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

    9.查看所有元素--迭代器
    scan_iter(match=None, count=None)

    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)

    other 方法

    print(r.get('name'))    # 查询key为name的值
    r.delete("gender")  # 删除key为gender的键值对
    print(r.keys()) # 查询所有的Key
    print(r.dbsize())   # 当前redis包含多少条数据
    r.save()    # 执行"检查点"操作,将数据写回磁盘。保存时阻塞
    # r.flushdb()        # 清空r中的所有数据

    9,管道(pipeline)


    redis默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,
    如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。

    管道(pipeline)是redis在提供单个请求中缓冲多条服务器命令的基类的子类。它通过减少服务器-客户端之间反复的TCP数据库包,从而大大提高了执行批量命令的功能。

    import redis
    import time
    
    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 = r.pipeline() # 创建一个管道
    
    pipe.set('name', 'jack')
    pipe.set('role', 'sb')
    pipe.sadd('faz', 'baz')
    pipe.incr('num')    # 如果num不存在则vaule为1,如果存在,则value自增1
    pipe.execute()
    
    print(r.get("name"))
    print(r.get("role"))
    print(r.get("num"))

    管道的命令可以写在一起,如:

    pipe.set('hello', 'redis').sadd('faz', 'baz').incr('num').execute()
    print(r.get("name"))
    print(r.get("role"))
    print(r.get("num"))
     
    10,用户浏览信息缓存:
     
    先把商品id保存
     conn = get_redis_connection('history')
     # 2、判断goodid是否保存过,保存过删除
     conn.lrem('history_%s' % user.id, 0, validated_data['goodid'])
     # 3、写入goodid
     conn.lpush('history_%s' % user.id, validated_data['goodid'])
     # 4、控制保存数量
     conn.ltrim('history_%s' % user.id, 0, 4)

    然后redis里取出来

      # 1、获取用户对象
      user = request.user
      # 2、查询redis获取goodid
      conn = get_redis_connection('history')
      goodlists = conn.lrange('history_%s' % user.id, 0, 5)  # [3,1,2]
      # 3、根据sku_id查询商品对象
      goods = GoodInfo.objects.filter(id__in=goodlists)
      # 4、序列化返回商品信息
      ser =self.get_serializer(goods,many=True)


    写了一天好累,部分 参考简书上:https://www.jianshu.com/p/2639549bedc8 ,更多详细信息,可以参考这个网址



  • 相关阅读:
    Java的字符串及格式化输入输出
    Java的数据类型与类型转换
    java基本程序
    svn基础入门
    github基础入门笔记
    git基础入门笔记
    linux基础入门笔记
    二、FreeMarker 模版开发指南 第二章 数值和类型
    【CodeForces】[599B]Spongebob and Joke
    【CodeForces】[612B]HDD is Outdated Technology
  • 原文地址:https://www.cnblogs.com/lvye001/p/10254016.html
Copyright © 2020-2023  润新知