空值和哨兵,如(但不限于)None、False和True
项目描述
帮助定义与Python内置值(如None、False和True)平行但不同的“空”值和哨兵。
None 是一个很好的 哨兵值,也是 空对象模式 的经典实现。
但有时你需要多个“空”值来表示空的不同方面。“什么都没有”在逻辑上与“未定义”、“禁止”、“数据结束”和其他类型的“空”不同。
nulltype 帮助您以不超载 None(或 False、0、{}、[]、"" 或其他可能的“这里什么也没有!”值)的方式轻松表示空的不同方面。它有助于创建具有特定意义的标识符,例如 Passthrough、Prohibited 和 Undefined。
如果您需要非 True 的真值哨兵,它也会帮助您做到这一点。并且将以易于消费、现成、全面测试的方式完成。
用法
from nulltype import NullType Void = NullType('Void') # following just to show it's working assert bool(Void) == False assert len(EmpVoidty) == 0 assert list(Void) == [] assert Void.some_attribute is Empty assert Void[22] is Nothing assert Void("hey", 12) is Empty
您可以根据喜好创建任意数量的自定义空值。为了方便起见,已导出几个默认值,Empty、Null 和 Nothing。这样,如果您不想创建自己的,可以轻松导入预定义的空值。
from nulltype import Empty
无物之力
在解析数据或遍历可能存在或不存在的数据结构时,可选的空类型特别有用。这在处理如 REST API 返回的数据时很常见。
例如,谷歌 Gmail API 的文档 建议以下代码
threads = gmail_service.users().threads().list(userId='me').execute() if threads['threads']: for thread in threads['threads']: print 'Thread ID: %s' % (thread['id'])
这里有很多操作只是为了避免有问题的不定引用。如果您已经定义了 Nothing 空类型,代码会更短(并避免额外的非常短暂的变量)
results = gmail_service.users().threads().list(userId='me').execute() for thread in results.get('threads', Nothing): print 'Thread ID: %s' % (thread['id'])
三行与四行可能看起来不是很大的优势,但随着任务复杂性的增加,其价值会增加。许多这样的“如果它在那里,那么…”结构在处理 API 结果、XML 解析树和其他基本嵌套信息源时都深深嵌套。在每个嵌套级别上节省一个守卫条件会迅速累加。
虽然您几乎可以在标准 Python 中做到这一点,但与 Nothing 不同,None 不可迭代。您可能会使用空列表 [](或等效的全局变量,如 EMPTYLIST)作为 get 方法的替代值。然而,根据许多解析器和 API 的文档,这种用法在当今 Python 社区中并不普遍。EMPTYLIST 方法也仅适用于返回列表的例程,而“尽管去拿吧”的 nulltype 模型对于更长的访问链也很有用。
results.get("payload", Nothing).get("headers", Nothing)
将返回正确的对象,如果没有则返回 Nothing。如果您尝试测试它(例如,使用 if 或逻辑表达式)或遍历它(例如,使用 for),它将表现得像空列表或 False - 什么在特定上下文中最有用。无论您是在迭代、索引、解引用、调用还是以其他方式访问它,NullType 都不会受到影响。
Nothing 不是什么都没有。它是一种将简化您的代码的东西。
通用哨兵和特殊值
虽然 nulltype 经常用于定义新的“空”值类型,但实际上它更通用。除了不同的“空”形式之外,NullType 实例也是良好的通用哨兵或特殊值。而不是旧的
class MySentinelClass(object): pass
使用
MySentinel = NullType('MySentinel')
这为您提供了具有已知真值属性和更佳打印表示的值。
>>> print MySentinelClass # fugly <class '__main__.MySentinelClass'> >>> print MySentinel # just right MySentinel
如果需要使用一个真实值(而非假值或空值)作为哨兵值,请使用NonNullType,它是NullType的伴侣,在几乎相同的方式下工作,但评估结果为真。
from nulltype import NonNullType Full = NonNullType('Full') assert bool(Full) is True assert len(Full) == 1 assert list(Full) == [Full] assert Full.some_attribute is Full assert Full[22] is Full assert Full("hey", 12) is Full
经验表明,nullish哨兵通常是足够且更好的选择。即使是NonNullType的“所有内容都折叠到相同的值”特性也赋予它一种某种程度上的null-like或至少是非反应性的性质。但如果你确实需要一个真-like哨兵,这里就有。
独特性
NullType实例旨在是单例,每个程序只有一个。它们几乎是这样的,尽管技术上可以有多个NullType实例,这使得它更像是一个多例模式。
目前并没有强制执行每个单例的独特性,这使得它更像是一种使用约定而非严格的法律。即使是最小的关注,这个问题在0%的时间里出现。
注释
已成功打包并针对所有晚期版本的Python进行了测试:2.6、2.7、3.3、3.4、3.5、3.6和3.7预发布版,以及PyPy和PyPy3的最新构建。
查看CHANGES.yml以获取完整的变更日志。
使用pytest、pytest-cov、coverage和tox进行自动多版本测试。使用Travis-CI进行持续集成测试。使用pyroma进行打包代码审查。
类似的模块包括sentinels和null。在这些模块中,我更喜欢sentinels,因为它显然是为Python 3准备的,包括pickle机制。noattr是一个新的替代品。
要使用空值Empty来简化JSON和其他数据格式的解析,请查看items
作者Jonathan Eunice或@jeunice在Twitter上欢迎您的评论和建议。
安装
要安装或升级到最新版本
pip install -U nulltype
您可能需要使用sudo前缀来授权在Unix、Linux和macOS上安装。在没有超级用户权限的环境中,您可能想使用pip的--user选项,仅安装给单个用户,而不是系统范围。在具有多个Python版本的系统上,您可能还需要使用特定的pip3或pip2命令,而不是默认的pip。作为备份,将pip作为Python模块运行可以在pip版本作为独立命令表现不佳的复杂情况下拯救您的理智。
python3.6 -m pip install -U nulltype
测试
要运行模块测试,请使用以下命令之一
tox # normal run - speed optimized tox -e py27 # run for a specific version only (e.g. py27, py34) tox -c toxcov.ini # run full coverage tests
项目详情
下载文件
下载适用于您的平台文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。