除了ID生成器之外,计数器也是构建应用程序时必不可少的组件之一,如对于网站的访客数量、用户执行某个操作的次数、某首歌或者某个视频的播放量、论坛帖子的回复数量等,记录这些信息都需要用到计数器。实际上,计数器在互联网中几乎无处不在,因此如何简单、高效地实现计数器一直都是构建应用程序时经常会遇到的一个问题。
代码清单2-7展示了一个计数器实现,这个程序把计数器的值存储在一个字符串键里面,并通过INCRBY命令和DECRBY命令对计数器的值执行加法操作和减法操作,在需要时,用户还可以通过调用GETSET方法来清零计数器并取得清零之前的旧值。
代码清单2-7 使用字符串键实现的计数器:/string/counter.py
class Counter:
def __init__(self, client, key):
self.client = client
self.key = key
def increase(self, n=1):
"""
将计数器的值加上n,然后返回计数器当前的值。
如果用户没有显式地指定n,那么将计数器的值加上1
"""
return self.client.incr(self.key, n)
def decrease(self, n=1):
"""
将计数器的值减去n,然后返回计数器当前的值。
如果用户没有显式地指定n,那么将计数器的值减去1
"""
return self.client.decr(self.key, n)
def get(self):
"""
返回计数器当前的值
"""
# 尝试获取计数器当前的值
value = self.client.get(self.key)
# 如果计数器并不存在,那么返回0作为计数器的默认值
if value is None:
return 0
else:
# 因为redis-py的get()方法返回的是字符串值,所以这里需要使用int()函数将字
# 符串格式的数字转换为真正的数字类型,比如将"10"转换为10
return int(value)
def reset(self):
"""
清零计数器,并返回计数器在被清零之前的值
"""
old_value = self.client.getset(self.key, 0)
# 如果计数器之前并不存在,那么返回0作为它的旧值
if old_value is None:
return 0
else:
# 与redis-py的get()方法一样,getset()方法返回的也是字符串值,所以程序在
# 将计数器的旧值返回给调用者之前,需要先将它转换成真正的数字
return int(old_value)
在这个程序中,increase()方法和decrease()方法在定义时都使用了Python的参数默认值特性:
def increase(self, n=1):
def decrease(self, n=1):
以上定义表明,如果用户直接以无参数的方式调用increase()或者decrease(),那么参数n的值将会被设置为1。
在设置了参数n之后,increase()方法和decrease()方法会分别调用INCRBY命令和DECRBY命令,根据参数n的值,对给定的键执行加法或减法操作:
# increase()方法
return self.client.incr(self.key, n)
# decrease()方法
return self.client.decr(self.key, n)
注意,increase()方法在内部调用的是incr()方法而不是incrby()方法,并且decrease()方法在内部调用的也是decr()方法而不是decrby()方法,这是因为在redis-py客户端中,INCR命令和INCRBY命令都是由incr()方法负责执行的:
如果用户在调用incr()方法时没有给定增量,那么incr()方法就默认用户指定的增量为1,并执行INCR命令。
如果用户在调用incr()方法时给定了增量,那么incr()方法就会执行INCRBY命令,并根据给定的增量执行加法操作。
decr()方法的情况也与此类似,只是被调用的命令变成了DECR命令和DECRBY命令。
以下代码展示了这个计数器的使用方法:
>>> from redis import Redis
>>> from counter import Counter
>>> client = Redis(decode_responses=True)
>>> counter = Counter(client, "counter::page_view")
>>> counter.increase() # 将计数器的值加上1
1
>>> counter.increase() # 将计数器的值加上1
2
>>> counter.increase(10) # 将计数器的值加上10
12
>>> counter.decrease() # 将计数器的值减去1
11
>>> counter.decrease(5) # 将计数器的值减去5
6
>>> counter.reset() # 重置计数器,并返回旧值
6
>>> counter.get() # 返回计数器当前的值
0