1、介绍
property指的是键和值都为字符串str类型的一组数据。properties则是property的容器,可以包含任意多个property。
(1)业务场景
在诸多业务场景中,存在使用properties数据的需求。
- 配置文件
- 请求和响应头部
- url的直接参数
- 请求体部
- cookies
(2)实现
为了方便处理properties数据,定义list[list[str, str]]结构进行存储和操作。在此基础上,编写函数和类,能够显著的节俭代码,进行复用
- api/PropertiesModule,定义对list[list[str, str]]数据的操作函数
- api/PropertiesClasses,定义properties的相关类
之所以不直接使用list或dict进行处理,大概以下几点:
- dict要求键是不可重复的,会自动去重。但是类似响应头部set-cookie字段是需要允许键重复的
- list的copy是浅复制,但需求一般是深复制。所以需要定义函数和方法
- dict和list都不强调元素是str类型,而properties函数和类都强调键和值都为str类型
2、api/PropertiesModule
"""
解析函数,将字符串s解析为properties结构,比如'a=1&b=2'解析为[['a','1'],['b','2']]
@s: str类型,待解析文本
@sep1: str类型,属性值对与属性值对之间的间隔
@sep2: str类型,属性值对内名称和值的间隔
@strip_flag: bool类型,是否对解析后各组的名称和值,去除前后的空白符。为True表示去除
@return: list[list[str,str]]类型
如果s,sep1,sep2任一值为空字符串,返回[]
如果根据sep1分隔后的项不包含sep2,则该项相当于不存在
"""
def parse(s: str, sep1: str, sep2: str, strip_flag: bool = False):
"""parse(s, sep1, sep2, strip_flag=False)->list[list[str, str]]"""
# 返回对象
result = []
# 如果任一值存在空字符串,直接结束
if s == '' or sep1 == '' or sep2 == '':
return result
items = s.split(sep1)
for i in range(len(items)):
# 存在sep2则按第一个进行分割,否则相当于不存在
if sep2 in items[i]:
split = items[i].split(sep2, maxsplit=1)
if strip_flag:
split[0] = split[0].strip()
split[1] = split[1].strip()
result.append([split[0], split[1]])
return result
"""
格式化字符串 比如[['a','1'],['b','2']]拼接为'a=1&b=2'
@arr: list[list[str, str]]类型,任意多组的键值对
@sep1: str类型,属性值对与属性值对之间的间隔
@sep2: str类型,属性值对内名称和值的间隔
@return: str类型
如果arr的长度为0,则返回空字符串''
"""
def toString(arr: list, sep1: str, sep2: str):
"""toString(arr, sep1, sep2)->str"""
result = ''
if len(arr) > 0:
result = arr[0][0] + sep2 + arr[0][1]
for i in range(1, len(arr)):
result = result + sep1 + arr[i][0] + sep2 + arr[i][1]
return result
"""
克隆属性值对,深层克隆,只是克隆值
@arr: list[list[str, str]]类型,任意多组的键值对
@return: list[list[str,str]]类型
"""
def clone(arr: list):
"""clone(arr) -> list[list[str, str]]"""
# list的copy是浅克隆,但是这里list的元素都是list,所以不能直接用
result = []
for i in range(len(arr)):
result.append([arr[i][0], arr[i][1]])
return result
"""
判断是否存在某键,如果存在匹配则返回True,否则返回False
@arr: list[list[str, str]]类型,任意多组的键值对
@name: str类型,键名
@ignoreCase: bool类型,是否忽视大小写匹配。为True表示忽略
@return: bool类型
"""
def hasName(arr: list, name: str, ignore_case: bool):
"""hasName(arr, name, ignore_case) -> bool"""
for item in arr:
if item[0] == name or (ignore_case and item[0].lower() == name.lower()):
return True
return False
"""
获取指定键名第一次匹配所对应的值
@arr: list[list[str, str]]类型,任意多组的键值对
@name: str类型,键名
@ignoreCase: bool类型,是否忽视大小写匹配。为True表示忽略
@default: 默认值为None,表示如果指定键在arr中不存在时所返回的值。也可以指定为其它值,比如空字符串
@return: 如果匹配则返回值,没有匹配项则返回default,默认是None
"""
def getFirstValue(arr: list, name: str, ignore_case: bool, default=None):
"""getFirstValue(arr, name, ignore_case, default=None) -> str/None"""
for item in arr:
if item[0] == name or (ignore_case and item[0].lower() == name.lower()):
return item[1]
return default
"""
获取指定键名匹配所对应的值的全部
@arr: list[list[str, str]]类型,任意多组的键值对
@name: str类型,键名
@ignoreCase: bool类型,是否忽视大小写匹配。为True表示忽略
@return: list[str]类型
"""
def getValue(arr: list, name: str, ignore_case: bool):
"""getValue(arr, name, ignore_case) -> list[str]"""
result = list()
for item in arr:
if item[0] == name or (ignore_case and item[0].lower() == name.lower()):
result.append(item[1])
return result
"""
移除指定键名匹配所对应的全部项
@arr: list[list[str, str]]类型,任意多组的键值对
@name: str类型,键名
@ignoreCase: bool类型,是否忽视大小写匹配。为True表示忽略
@return: 直接在对象上操作,无返回
"""
def remove(arr: list, name: str, ignore_case: bool):
"""remove(arr, name, ignore_case)"""
i = 0
while i < len(arr):
if arr[i][0] == name or (ignore_case and arr[i][0].lower() == name.lower()):
del arr[i]
# continue很重要,匹配移除当前索引的元素,然后再判断当前索引
continue
i = i + 1
"""
更新指定键名匹配所对应的全部项
@arr: list[list[str, str]]类型,任意多组的键值对
@name: str类型,待匹配键名
@value: str类型,待更新的值
@ignoreCase: bool类型,是否忽视大小写匹配。为True表示忽略
@return: 直接在对象上操作,无返回
"""
def update(arr: list, name: str, value: str, ignore_case: bool):
for item in arr:
if item[0] == name or (ignore_case and item[0].lower() == name.lower()):
item[1] = value
"""
在指定集合的末尾追加项
@arr: list[list[str, str]]类型,任意多组的键值对
@name: str类型,待追加项的键名
@value: str类型,待追加项的值
@return: 直接在对象上操作,无返回
"""
def add(arr: list, name: str, value: str):
arr.append([name, value])
"""
获取键名列表
@arr: list[list[str, str]]类型,任意多组的键值对
@return list[str]类型
"""
def getNames(arr: list):
"""getNames(arr) -> list[str]"""
result = []
for i in range(len(arr)):
result.append(arr[i][0])
return result
"""
获取指定索引的项
@arr: list[list[str, str]]类型,任意多组的键值对
@index int类型,指定索引
@return list[str, str]
"""
def get(arr: list, index: int):
"""get(arr, index) -> list[str, str]"""
return arr[index]
"""
获取指定索引项的键名
@arr: list[list[str, str]]类型,任意多组的键值对
@index int类型,指定索引
@return str类型
"""
def get_name(arr: list, index: int):
"""get(arr, index) -> str"""
return arr[index][0]
"""
获取指定索引项的值
@arr: list[list[str, str]]类型,任意多组的键值对
@index int类型,指定索引
@return str类型
"""
def get_value(arr: list, index: int):
"""get(arr, index) -> str"""
return arr[index][1]
"""
基于索引删除某一项元素
@arr: list[list[str, str]]类型,任意多组的键值对
@index: int类型
@return: 直接在对象上操作,无返回
"""
def remove_index(arr: list, index: int):
del arr[index]
"""
更新指定索引项
@arr: list[list[str, str]]类型,任意多组的键值对
@index: int类型,待匹配的项索引
@name: str类型,待更新的键名
@value: str类型,待更新的值
@return: 直接在对象上操作,无返回
"""
def update_index(arr: list, index: int, name: str, value: str):
arr[index][0] = name
arr[index][1] = value