Skip to content

工具契约、执行循环与副作用:练习答案 ​

练习解答 ​

以下是完整离线文件,保存为answer.py后运行python answer.py;先建立与正文一致的初始状态,再验证变体。

使用正文定义与最终状态:

python
import json
orders={};stock={'A':3}
def reserve(principal,key,args):
    if principal!='alice':raise PermissionError('forbidden')
    if set(args)!={'sku','quantity'} or args['sku']!='A':raise ValueError('bad args')
    n=args['quantity']
    if type(n) is not int or n<=0:raise ValueError('bad quantity')
    fingerprint=json.dumps(args,sort_keys=True)
    cache_key=(principal,key)
    if cache_key in orders:
        old=orders[cache_key]
        if old['fingerprint']!=fingerprint:raise ValueError('idempotency conflict')
        return old['result']
    if stock['A']<n:raise ValueError('insufficient stock')
    stock['A']-=n
    result={'reserved':n,'remaining':stock['A']}
    orders[cache_key]={'fingerprint':fingerprint,'result':result}
    return result
first=reserve('alice','order-1',{'sku':'A','quantity':2})
assert reserve('alice','order-1',{'sku':'A','quantity':2})==first
assert stock['A']==1
try:reserve('alice','order-1',{'sku':'A','quantity':1})
except ValueError as e:assert str(e)=='idempotency conflict'
else:raise AssertionError('conflicting replay accepted')
print('one reservation',stock)

try:reserve('bob','order-1',{'sku':'A','quantity':2})
except PermissionError:pass
else:raise AssertionError('unauthorized replay')
try:reserve('alice','order-2',{'sku':'A','quantity':2})
except ValueError as e:assert str(e)=='insufficient stock'
else:raise AssertionError('oversold')
assert stock['A']==1
print('failure preserves inventory')

失败路径也要验证状态未变,只断言“抛了异常”无法证明没有先扣库存后失败。

用心记录每一步成长