teoria programacion programa potencia lista entre diferencia convertir conjuntos conjunto python set truthiness

python - programacion - Valor de verdad del conjunto vacío



python set() function (3)

Estoy interesado en el valor de verdad de los conjuntos Python como {''a'', ''b''} , o el conjunto vacío set() (que no es lo mismo que el diccionario vacío {} ). En particular, me gustaría saber si bool(my_set) es False si y solo si el conjunto my_set está vacío.

Ignorando los tipos primitivos (como los numerales) y definidos por el usuario, https://docs.python.org/3/library/stdtypes.html#truth dice:

Los siguientes valores se consideran falsos:

  • [...]
  • cualquier secuencia vacía, por ejemplo, '''' , () , [] .
  • cualquier mapeo vacío, por ejemplo, {} .
  • [...]

Todos los demás valores se consideran verdaderos

Según https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range , un conjunto no es una secuencia (está desordenado, sus elementos no tienen índices, etc. )

Hay tres tipos de secuencia básicos: listas, tuplas y objetos de rango.

Y, de acuerdo con https://docs.python.org/3/library/stdtypes.html#mapping-types-dict ,

Actualmente solo hay un tipo de mapeo estándar, el diccionario .

Entonces, por lo que yo entiendo, el tipo de conjunto no es un tipo que nunca puede ser False . Sin embargo, cuando intento, bool(set()) evalúa como False .

Preguntas:

  • ¿Es esto un problema de documentación, o estoy obteniendo algo mal?
  • ¿El conjunto vacío es el único conjunto cuyo valor de verdad es False ?

Después de mirar el código fuente de CPython, supongo que se trata de un error de documentación, sin embargo, podría ser dependiente de la implementación y, por lo tanto, sería un buen tema para plantear en el rastreador de errores de Python.

Específicamente, object.c define el valor de verdad de un artículo de la siguiente manera:

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); }

Podemos ver claramente que el valor es value sería siempre verdadero si no es un tipo booleano, None, una secuencia o un tipo de mapeo, que requeriría que se establezca tp_as_sequence o tp_as_mapping.

Afortunadamente, mirar setobject.c muestra que los conjuntos implementan tp_as_sequence, lo que sugiere que la documentación parece ser incorrecta.

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 también implementan tp_as_sequence, por lo que parece que, aunque no es un tipo de secuencia, es similar a una secuencia, lo suficiente para ser verdad.

En mi opinión, la documentación debería aclarar esto: los tipos tipo mapeo, o los tipos de secuencia dependerán de verdad de su longitud.

Editar Como user2357112 señala correctamente, tp_as_sequence y tp_as_mapping no significan que el tipo es una secuencia o un mapa. Por ejemplo, dict implementa tp_as_sequence y lista implements tp_as_mapping .


Esa parte de los documentos está mal escrita o, mejor dicho, mal conservada. La siguiente cláusula:

instancias de clases definidas por el usuario, si la clase define un __bool__() o __len__() , cuando ese método devuelve el entero valor cero o bool Falso.

realmente se aplica a todas las clases, definidas por el usuario o no, incluidas set , dict e incluso los tipos enumerados en todas las otras cláusulas (todas las cuales definen __bool__ o __len__ ). (En Python 2, None es falso a pesar de no tener un __len__ o el equivalente de Python 2 de __bool__ , pero esa excepción se ha ido desde Python 3.3 ).

Digo mal mantenido porque esta sección ha sido casi sin cambios desde al menos Python 1.4 , y tal vez antes. Se ha actualizado para la adición de False y la eliminación de tipos int / long independientes, pero no para la unificación de tipo / clase o la introducción de conjuntos.

Cuando se escribió la cláusula citada, las clases definidas por el usuario y los tipos incorporados realmente se comportaban de manera diferente, y no creo que los tipos incorporados realmente tuvieran __bool__ o __len__ en ese momento.


La documentación para __bool__ indica que este método se llama para la prueba del valor de verdad y, si no está definido, se evalúa __len__ :

Llamado para implementar pruebas de valor verdad y la operación incorporada bool (); [...] Cuando este método no está definido, se __len__() , si está definido, y el objeto se considera verdadero si su resultado es distinto de cero. Si una clase no define ni __len__() ni __bool__() , todas sus instancias se consideran verdaderas.

Esto es válido para cualquier objeto de Python. Como podemos ver, set no define un método __bool__ :

>>> set.__bool__ Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object ''set'' has no attribute ''__bool__''

entonces la prueba de verdad recae en __len__ :

>>> set.__len__ <slot wrapper ''__len__'' of ''set'' objects>

Por lo tanto, solo un conjunto vacío (longitud cero) se considera falso.

La parte para la prueba del valor de verdad en la documentación no está completa con respecto a este aspecto.