空集的真值
Truth value of empty set
我对 Python 集的真值感兴趣,比如 {'a', 'b'}
,或者空集 set()
(这与空字典 {}
不同) ).特别是,当且仅当集合 my_set
为空时,我想知道 bool(my_set)
是否为 False
。
忽略原始类型(例如数字)以及用户定义的类型,https://docs.python.org/3/library/stdtypes.html#truth 表示:
The following values are considered false:
- [...]
- any empty sequence, for example,
''
, ()
, []
.
- any empty mapping, for example,
{}
.
- [...]
All other values are considered true
根据https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range,集合不是序列(它是无序的,其元素没有索引等):
There are three basic sequence types: lists, tuples, and range objects.
并且,根据 https://docs.python.org/3/library/stdtypes.html#mapping-types-dict,
There is currently only one standard mapping type, the dictionary.
因此,据我了解,set 类型不是可以成为 False
的类型。但是,当我尝试时,bool(set())
的计算结果为 False
.
问题:
- 这是文档问题,还是我弄错了什么?
- 空集是唯一一个真值为
False
的集合吗?
查看 CPython 的源代码后,我猜想这是一个文档错误,但是,它可能依赖于实现,因此在 [=39= 上提出一个很好的问题] 错误跟踪器。
具体来说,object.c定义某项的真值如下:
int
PyObject_IsTrue(PyObject *v)
{
Py_ssize_t res;
if (v == Py_True)
return 1;
if (v == Py_False)
return 0;
if (v == Py_None)
return 0;
else if (v->ob_type->tp_as_number != NULL &&
v->ob_type->tp_as_number->nb_bool != NULL)
res = (*v->ob_type->tp_as_number->nb_bool)(v);
else if (v->ob_type->tp_as_mapping != NULL &&
v->ob_type->tp_as_mapping->mp_length != NULL)
res = (*v->ob_type->tp_as_mapping->mp_length)(v);
else if (v->ob_type->tp_as_sequence != NULL &&
v->ob_type->tp_as_sequence->sq_length != NULL)
res = (*v->ob_type->tp_as_sequence->sq_length)(v);
else
return 1;
/* if it is negative, it should be either -1 or -2 */
return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int);
}
我们可以清楚地看到,如果值不是布尔类型、None、序列或映射类型,则 value is value 将始终为 true,这将需要 tp_as_sequence 或 tp_as_mapping待设置。
幸运的是,查看 setobject.c 表明集合确实实现了 tp_as_sequence,这表明文档似乎不正确。
PyTypeObject PySet_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"set", /* tp_name */
sizeof(PySetObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)set_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)set_repr, /* tp_repr */
&set_as_number, /* tp_as_number */
&set_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
/* ellipsed lines */
};
Dicts也实现了tp_as_sequence,所以看起来虽然不是sequence类型,但是sequence-like,足够真实了。
在我看来,文档应该澄清这一点:类映射类型或类序列类型的真实性取决于它们的长度。
Edit 正如 user2357112 正确指出的那样,tp_as_sequence
和 tp_as_mapping
并不意味着类型是序列或映射。比如dict实现了tp_as_sequence
,list实现了tp_as_mapping
.
这部分文档写得不好,或者更确切地说,维护得不好。以下子句:
instances of user-defined classes, if the class defines a __bool__()
or __len__()
method, when that method returns the integer zero or bool value False.
真正适用于所有 类,用户自定义与否,包括set
,dict
,甚至是中列出的类型所有其他子句(所有这些子句都定义了 __bool__
或 __len__
)。 (在 Python 2 中,尽管没有 __len__
或 Python 2 的 __bool__
等价物,但 None
是假的,但那个例外是 gone since Python 3.3。 )
我说维护不善是因为此部分至少自 Python 1.4 以来几乎没有变化,也许更早。它已针对添加 False
和删除单独的 int/long 类型进行了更新,但未针对 type/class 统一或集合的引入进行更新。
在编写引用子句时,用户定义的 类 和内置类型确实表现不同,我认为内置类型实际上没有 __bool__
或 __len__
当时。
__bool__
states that this method is called for truth value testing and if it is not defined then __len__
的文档被评估:
Called to implement truth value testing and the built-in operation bool(); [...] When this method is not defined, __len__()
is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither __len__()
nor __bool__()
, all its instances are considered true.
这适用于任何 Python 对象。我们可以看到 set
没有定义方法 __bool__
:
>>> set.__bool__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'set' has no attribute '__bool__'
所以真值测试回到 __len__
:
>>> set.__len__
<slot wrapper '__len__' of 'set' objects>
因此只有一个空集(零长度)被认为是错误的。
文档中 truth value testing 的部分在这方面并不完整。
我对 Python 集的真值感兴趣,比如 {'a', 'b'}
,或者空集 set()
(这与空字典 {}
不同) ).特别是,当且仅当集合 my_set
为空时,我想知道 bool(my_set)
是否为 False
。
忽略原始类型(例如数字)以及用户定义的类型,https://docs.python.org/3/library/stdtypes.html#truth 表示:
The following values are considered false:
- [...]
- any empty sequence, for example,
''
,()
,[]
.- any empty mapping, for example,
{}
.- [...]
All other values are considered true
根据https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range,集合不是序列(它是无序的,其元素没有索引等):
There are three basic sequence types: lists, tuples, and range objects.
并且,根据 https://docs.python.org/3/library/stdtypes.html#mapping-types-dict,
There is currently only one standard mapping type, the dictionary.
因此,据我了解,set 类型不是可以成为 False
的类型。但是,当我尝试时,bool(set())
的计算结果为 False
.
问题:
- 这是文档问题,还是我弄错了什么?
- 空集是唯一一个真值为
False
的集合吗?
查看 CPython 的源代码后,我猜想这是一个文档错误,但是,它可能依赖于实现,因此在 [=39= 上提出一个很好的问题] 错误跟踪器。
具体来说,object.c定义某项的真值如下:
int
PyObject_IsTrue(PyObject *v)
{
Py_ssize_t res;
if (v == Py_True)
return 1;
if (v == Py_False)
return 0;
if (v == Py_None)
return 0;
else if (v->ob_type->tp_as_number != NULL &&
v->ob_type->tp_as_number->nb_bool != NULL)
res = (*v->ob_type->tp_as_number->nb_bool)(v);
else if (v->ob_type->tp_as_mapping != NULL &&
v->ob_type->tp_as_mapping->mp_length != NULL)
res = (*v->ob_type->tp_as_mapping->mp_length)(v);
else if (v->ob_type->tp_as_sequence != NULL &&
v->ob_type->tp_as_sequence->sq_length != NULL)
res = (*v->ob_type->tp_as_sequence->sq_length)(v);
else
return 1;
/* if it is negative, it should be either -1 or -2 */
return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int);
}
我们可以清楚地看到,如果值不是布尔类型、None、序列或映射类型,则 value is value 将始终为 true,这将需要 tp_as_sequence 或 tp_as_mapping待设置。
幸运的是,查看 setobject.c 表明集合确实实现了 tp_as_sequence,这表明文档似乎不正确。
PyTypeObject PySet_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"set", /* tp_name */
sizeof(PySetObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)set_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)set_repr, /* tp_repr */
&set_as_number, /* tp_as_number */
&set_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
/* ellipsed lines */
};
Dicts也实现了tp_as_sequence,所以看起来虽然不是sequence类型,但是sequence-like,足够真实了。
在我看来,文档应该澄清这一点:类映射类型或类序列类型的真实性取决于它们的长度。
Edit 正如 user2357112 正确指出的那样,tp_as_sequence
和 tp_as_mapping
并不意味着类型是序列或映射。比如dict实现了tp_as_sequence
,list实现了tp_as_mapping
.
这部分文档写得不好,或者更确切地说,维护得不好。以下子句:
instances of user-defined classes, if the class defines a
__bool__()
or__len__()
method, when that method returns the integer zero or bool value False.
真正适用于所有 类,用户自定义与否,包括set
,dict
,甚至是中列出的类型所有其他子句(所有这些子句都定义了 __bool__
或 __len__
)。 (在 Python 2 中,尽管没有 __len__
或 Python 2 的 __bool__
等价物,但 None
是假的,但那个例外是 gone since Python 3.3。 )
我说维护不善是因为此部分至少自 Python 1.4 以来几乎没有变化,也许更早。它已针对添加 False
和删除单独的 int/long 类型进行了更新,但未针对 type/class 统一或集合的引入进行更新。
在编写引用子句时,用户定义的 类 和内置类型确实表现不同,我认为内置类型实际上没有 __bool__
或 __len__
当时。
__bool__
states that this method is called for truth value testing and if it is not defined then __len__
的文档被评估:
Called to implement truth value testing and the built-in operation bool(); [...] When this method is not defined,
__len__()
is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither__len__()
nor__bool__()
, all its instances are considered true.
这适用于任何 Python 对象。我们可以看到 set
没有定义方法 __bool__
:
>>> set.__bool__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'set' has no attribute '__bool__'
所以真值测试回到 __len__
:
>>> set.__len__
<slot wrapper '__len__' of 'set' objects>
因此只有一个空集(零长度)被认为是错误的。
文档中 truth value testing 的部分在这方面并不完整。