Los booleanos tienen dos valores posibles. ¿Hay tipos que tienen tres valores posibles? [duplicar]


Posible Duplicado:
¿Cuál es la mejor manera de implementar una' enumeración ' en Python?

Estoy escribiendo una función que, idealmente, me gustaría devolver uno de los tres estados: "sí", "no" y "no sé".

  1. ¿Algún lenguaje de programación tiene un tipo que tiene tres, y solo tres estados? ¿Como un booleano, pero con tres estados en lugar de dos?

  2. En lenguajes que no tienen tal tipo (como Python), el mejor tipo para representar esto?

    Actualmente creo que voy a ir con un entero (0 para "no", 1 para "no sé" y 2 para "sí"), pero tal vez hay una mejor manera? Los enteros parecen un poco "número mágico".

    Podría volver True, False o None, pero como None evaluaría a False en la mayoría de los contextos de comparación, parece un poco maduro para errores.

Author: Community, 2012-02-29

9 answers

En Python lo haría con un objeto wrapper que contenga uno de esos tres valores; usaría True, False, y None. Dado que el valor de truthiness implícito de un objeto booleano con tres valores posibles es problemático, lo resolveremos mediante que no se permita completamente (planteando una excepción en __nonzero__(), o en Python 3, __bool__()), por lo que se requiere que las comparaciones siempre se hagan explícitamente, in, ==, o !=. Implementaremos la igualdad como identidad para que solo los valores de singleton True, False, y None coinciden.

class Tristate(object):

    def __init__(self, value=None):
       if any(value is v for v in (True, False, None)):
          self.value = value
       else:
           raise ValueError("Tristate value must be True, False, or None")

    def __eq__(self, other):
       return (self.value is other.value if isinstance(other, Tristate)
               else self.value is other)

    def __ne__(self, other):
       return not self == other

    def __nonzero__(self):   # Python 3: __bool__()
       raise TypeError("Tristate object may not be used as a Boolean")

    def __str__(self):
        return str(self.value)

    def __repr__(self):
        return "Tristate(%s)" % self.value

Uso:

t = Tristate(True)
t == True           # True
t != False          # True
t in (True, False)  # True
bool(t)             # Exception!
if t: print "woo"   # Exception!

Al usar objetos Tristate, debe especificar explícitamente qué valores coincidirán, es decir, foo == True or bar != None. También puede hacer foo in (False, None) para hacer coincidir varios valores (aunque, por supuesto, in dos valores es lo contrario de != con un solo valor). Si hay otras operaciones lógicas que desea poder realizar con estos objetos, podría implementarlas como métodos, o posiblemente anulando ciertos operadores (tristemente, sin embargo, lógico not, and, y or no son anulables, aunque hay una propuesta para agregar eso).

También tenga en cuenta que no puede reemplazar id() en Python, por lo que, por ejemplo, Tristate(None) is None es False; los dos objetos son de hecho diferentes. Dado que un buen estilo Python es usar is cuando se compara con singletons, esto es desafortunado, pero inevitable.

Editar 4/27/16: Se agregó soporte para comparar un objeto Tristate con otro.

 26
Author: kindall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-26 17:44:33

Esto se llama Lógica ternaria o Lógica de Tres valores. Como sugieren otras respuestas, puedes implementar una clase:

class Ternary:
    FALSE = 0
    TRUE = 1
    UNKNOWN = 2

Yo mismo, probablemente iría por su solución (True, False, None) pero entiendo su preocupación.

 8
Author: Linus Gustav Larsson Thiel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 14:52:35

El problema paralelo a Ninguno existe con false = 0, true = 1, unknown = 2 (unknown no es realmente verdadero tampoco, pero se evaluará a Verdadero si no tiene cuidado).

Se me ocurrió una forma hackish de conseguir algo que al menos se aproxime a lo que quieres, creo. Al menos te dará algo que evaluará de manera trinaria en if/else y otras instancias de evaluación booleanas.

class Yes(object):
    def __nonzero__(self):
        return True

class No(object):
    def __nonzero__(self):
        return False

class Unknown(object):
    def __nonzero__(self):
        raise ValueError('Unknown typed values do not evaluate to True/False.  Try using Ternary.eval().')

class Ternary(object):
    def __init__(self, yes, no, unknown):
        setattr(self, yes, Yes())
        setattr(self, no, No())
        setattr(self, unknown, Unknown())
    @staticmethod
    def eval(value, unknown_eval):
        if isinstance(value, Unknown):
            return unknown_eval
        return bool(value)

Uso:

t = Ternary('yes', 'no', 'unknown')
# Do stuff to assign ternary value to x
if Ternary.eval(x, True):
    print 'x is yes or unknown'
if Ternary.eval(x, False):
    print 'x is yes only'

Usted podría hacer Sí, No, y pseudo-singletons Desconocidos lo que te permitiría refinar un poco la evaluación. Todavía puedes hacer comprobaciones simples si sabes que tu valor va a ser sí o no, pero si tratas de hacer una bool () recta (es decir, si x) en Desconocido obtendrás un error de tipo. Sin embargo, esto haría que su código sea más explícito, ya que cada vez que verifique un valor del tipo trinario, tendría que definir en su código cómo desea que se trate a unknown en el contexto de ese condicional, por lo que sería un plus.

Editar: Pensé en un alternativa que requeriría un manejo menos especial pero menos flexible. Por encima de esto:

class Unknown(object):
    def __init__(self, eval):
        self._eval = eval
    def __nonzero__(self):
        return self._eval

class Ternary(object):
    def __init__(self, yes, no, unknown, unknown_eval):
        setattr(self, yes, Yes())
        setattr(self, no, No())
        setattr(self, unknown, Unknown(unknown_eval))

Uso:

t1 = Ternary('yes', 'no', 'unknown', True)
t2 = Ternary('yes', 'no', 'unknown', False)
# Do stuff to assign ternary values to x1 and x2
if x1:
    print 'x is yes or unknown'
if x2:
    print 'x is yes only'

Esto tiene el beneficio de permitir que otro distinto de cero funcione como requiere la especificación en Unknown, pero tiene la desventaja de tener la evaluación de Unknown grabada en piedra desde la instanciación y de no permitir que Unknown sea un pseudo-singleton.

 6
Author: Silas Ray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 18:14:48
  1. Al menos un idioma tiene esto: C#: int? num = null;, ahora {[2] } es en realidad Nullable<Int32> (el signo de interrogación es un " azúcar sintáctico") ver: http://msdn.microsoft.com/en-us/library/1t3y8s4s%28v=vs.80%29.aspx y http://en.wikipedia.org/wiki/Nullable_type
  2. No conoce Python, pero depende de su preferencia: usabilidad (enum o class) vs. eficiencia (campos de 4 bits)
 3
Author: Tar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 14:53:51

Generalmente se conoce como 'enum', después de la construcción C de ese nombre.

Puede crear fácilmente su propia clase, cuyos objetos deben inicializarse con valores de un conjunto, y puede crear las funciones de comparación, igualdad y verdad apropiadas en consecuencia.

 2
Author: Marcin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 14:47:22

Tengo una clase lógica de 3 valores en mi módulo dbf.

Implementa False/True/Unknown como singleton valores Falsth/Truth/Unknown. Implementa todos los operadores de comparación, y también permite la comparación con los singletons de Python False/True/None (None tomado como desconocido). Cualquier comparación con un valor Unknown resulta en Unknown, y un intento de usar implícitamente un valor Unknown en una instrucción if (u otro contexto bool) generará un TypeError, aunque Truth y Falsth se puede usar en contextos booleanos, y Unknown se puede comparar directamente.

Porque no es posible anular el and, or, y not comportamiento el tipo anula los operadores bitwise&, |, y ~.

También implementa __index__ con Unknown que tiene el valor de 2.

Ejemplo:

from dbf import Logical, Truth, Falsth, Unknown

middle_name = raw_input("What is the middle name? ['?' if unknown] ").strip()
if middle_name == '?':
    middle_name = ''
    middle_exists = Unknown
elif middle_name:
    middle_exists = Truth
else:
    middle_exists = Falsth
.
.
.
if middle_exists is Unknown:
    print "The middle name is unknown."
elif middle_exists:
    print "The middle name is %s." % middle_name
else:
    print "This person does not have a middle name."
 1
Author: Ethan Furman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-01 17:10:26

No hay tales tipos incorporados. Y enum tampoco son compatibles como tipo. Sin embargo, puede usar constante como:

class Option:
    NO = 0
    DONT_KNOW = 1
    YES = 2

reply = Option.YES
 0
Author: Husain Basrawala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 14:50:55

No soy mucho de una programación de Python, pero podría devolver Ninguno en el caso No sé. La persona que llama tendría que comprobar si "Sí", " No " o Ninguno, pero al menos sabrían que usted no lo sabe.

Http://boodebr.org/main/python/tourist/none-empty-nothing

 0
Author: Ninju Bohra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 14:52:23

Nunca vi un tipo similar al booleano que tuviera más de dos valores. Después de todo" booleano "significa que la función opera sobre" dominio booleano", que a su vez tiene exactamente 2 valores.

Dicho esto, está perfectamente bien usar enteros, enum o incluso cadenas. En python puede crear un módulo que contenga solo variables SÍ, NO, TAL VEZ e importar desde allí a cualquier otro lugar.

 0
Author: Ecir Hana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-29 14:52:49