【问题标题】:Booleans have two possible values. Are there types that have three possible values? [duplicate]布尔值有两个可能的值。是否存在具有三个可能值的类型? [复制]
【发布时间】:2012-03-19 01:24:23
【问题描述】:

可能重复:
What's the best way to implement an 'enum' in Python?

我正在编写一个函数,理想情况下,我希望返回以下三种状态之一:“是”、“否”和“不知道”。

  1. 是否有任何编程语言的类型具有三个且只有三个状态?类似于布尔值,但具有三种状态而不是两种状态?

  2. 在没有这种类型的语言(如 Python)中,表示这种类型的最佳类型是什么?

    目前我想我会选择一个整数(0 表示“否”,1 表示“不知道”,2 表示“是”),但也许有更好的方法?整数似乎有点“幻数”。

    我可以返回 TrueFalseNone,但在大多数比较情况下,None 的计算结果为 False,这似乎有点容易出错。

【问题讨论】:

  • 这可能是相关的:stackoverflow.com/questions/36932/…
  • 类似于这个问题,不是吗? stackoverflow.com/questions/36932/…
  • 1 和 2 在布尔上下文中都是 True 无论如何。除非您计划实现一个在 __bool__() 中为“可能”值引发 ValueError 的类,否则无论如何使用普通比较,您都会陷入困境。
  • 三值逻辑比简单的枚举要复杂得多。
  • 这正是 Python Tribool 数据类型的用途。

标签: python logic boolean


【解决方案1】:

在 Python 中,我会使用包含这三个值之一的包装器对象来做到这一点;我会使用TrueFalseNone。由于具有三个可能值的类布尔对象的隐含真实性值是有问题的,我们将通过完全禁止来解决这个问题(在__nonzero__() 或Python 3 中引发异常@987654327 @),因此要求始终使用in==!= 显式进行比较。我们将实现相等作为标识,以便仅匹配特定的单例值 TrueFalseNone

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

用法:

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

使用Tristate 对象时,您必须明确指定要匹配的值,即foo == True or bar != None。您也可以使用foo in (False, None) 匹配多个值(当然in 两个值与!= 单个值相同)。如果您希望能够对这些对象执行其他逻辑操作,您可以将它们实现为方法,或者可能通过覆盖某些运算符(但遗憾的是,逻辑 notandor 不是可覆盖,尽管有 a proposal 来添加)。

另请注意,您不能在 Python 中覆盖 id(),例如Tristate(None) is NoneFalse;这两个对象实际上是不同的。由于好的 Python 风格是在与单例比较时使用is,这是不幸的,但也是不可避免的。

2016 年 4 月 27 日编辑:添加了将一个 Tristate 对象与另一个对象进行比较的支持。

【讨论】:

  • @PaulD.Waite 我想问为什么不接受,但很清楚为什么。
  • @Marcin:当然,对不起,如果我造成了任何冒犯——这个答案(如果我理解正确的话)与你的基本相同,只是带有示例代码和布尔比较的讨论。我认为额外的细节使它值得接受。
  • 更新了一些 Python 3 细节。感谢您接受我的回答,我确实没想到!
  • 如果它只有一个问题,好吧,这对于我在 10 分钟内完成的一些代码来说还不错,并且只对...进行了最少的测试:-)
  • 如需更完整的 PyPI 实现,请查看Python tribool module。 (我会将此作为答案发布,但不允许。)
【解决方案2】:

这称为Ternary logic 或三值逻辑。正如其他答案所暗示的,您可以实现一个类:

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

我自己,我可能会寻求您的解决方案(TrueFalseNone),但我理解您的担忧。

【讨论】:

    【解决方案3】:

    在 false = 0、true = 1、unknown = 2 时存在与 None 问题相似的问题(unknown 实际上也不是 true,但如果你不小心,它会评估为 True)。

    我想出了一个老生常谈的方法来获得至少接近你想要的东西,我想。它至少会为您提供一些将在 if/else 和其他布尔 eval 实例中以三元方式进行评估的东西。

    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)
    

    用法:

    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'
    

    您可以制作 Yes、No 和 Unknown 伪单例,让您稍微改进 eval。当您知道您的值将是是或否时,您仍然可以进行简单的 if 检查,但是如果您尝试在 Unknown 上执行直接 bool()(即 if x),您会得到一个 TypeError。不过,这将使您的代码更加明确,因为每次检查三元类型的值时,您都必须在代码中定义您希望在该条件的上下文中如何处理未知,所以这将是一个加号.

    编辑: 我想到了一个需要较少特殊处理但灵活性较低的替代方案。改成上面这样:

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

    用法:

    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'
    

    这样做的好处是允许 nonzero 像 Unknown 中的规范要求那样工作,但它的缺点是从实例化中固定 Unknown 的 eval 并且不再允许 Unknown 成为伪单例。

    【讨论】:

    • __nonzero__ 必须返回 boolint -- 不允许无。
    • @Ethan Furman,因此我多次指出这不符合规范。以及为什么我在底部提供了一个替代方案。你有新的信息要补充吗?
    • 啊——请仔细阅读您的帖子。如果您要在值未知时引发异常,最好定义自己的异常并引发它,而不是让它看起来像一个错误。对于三元逻辑的完整实现,您可以查看我的答案中的链接。 (如果您想在示例中使用自定义异常类,或者其他任何看起来不像错误的东西,我会很乐意改变我的投票。)
    • 我看了你下面的评论,但可以这么说。问题是 OP 希望能够对值本身进行直接布尔评估。 is Thruthis Falsthis Unknown 是 OP 希望避免的检查; Thruth、Falsth 和 Unknown 在没有第三个值的情况下仍将评估为 True 或 False。不过,我会更改答案以引发新的异常。
    • 引发异常肯定是一个有效的选择。对于我的用例,模式是:如果值为Truth,则执行某些操作(对待False/Unknown 相同);如果值为Falsth,则执行某些操作(对待True/Unknown 相同);如果Unknown 应该被区别对待,则使用if/elif/else
    【解决方案4】:
    1. 至少一种语言有这个:C#:int? num = null;,现在num实际上是Nullable<Int32>(问号是“句法糖”) 见:http://msdn.microsoft.com/en-us/library/1t3y8s4s%28v=vs.80%29.aspxhttp://en.wikipedia.org/wiki/Nullable_type
    2. 不了解 Python,但这取决于您的偏好:可用性(enumclass)与效率(4 位字段)

    【讨论】:

      【解决方案5】:

      它通常被称为“枚举”,在该名称的 C 构造之后。

      您可以轻松创建自己的类,其对象必须使用集合中的值进行初始化,并且您可以相应地创建适当的比较、相等和真值函数。

      【讨论】:

      • 三元逻辑与枚举不同。
      • @EthanFurman:确实如此,但我认为不相关。
      【解决方案6】:

      我的dbf module 中有一个三值逻辑类。

      它将False/True/Unknown 实现为单例值Falsth/Truth/Unknown。它实现了所有比较运算符,还允许与 Python 单例False/True/NoneNone 表示未知)进行比较。与Unknown 值的任何比较都会导致Unknown,并且尝试在if 语句(或其他bool 上下文)中隐式使用Unknown 值将引发TypeError,尽管TruthFalsth 可以在布尔上下文中使用,Unknown 可以直接比较。

      由于无法覆盖andornot 行为,因此该类型会覆盖位运算符&|~

      它还实现了__index__Unknown 的值为2

      例子:

      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."
      

      【讨论】:

        【解决方案7】:

        没有这样的内置类型。并且枚举也不支持作为一种类型。但是,您可以将常量用作:

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

        【讨论】:

        • 请注意,这与枚举并不完全相同。
        • 对于类似枚举的实现,您可以参考stackoverflow.com/a/1695250
        • 这与 C 枚举的不同之处与您的示例完全相同。
        • 我从来没有说过我的是一个枚举。我说你可以这样实现你的逻辑。
        【解决方案8】:

        我不太会 Python 编程,但您能否在 不知道 的情况下返回 None。呼叫者必须检查“是”、“否”或无,但至少他们会知道您不知道。

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

        【讨论】:

          【解决方案9】:

          我从未见过类似于 boolean 的类型具有两个以上的值。毕竟“布尔”意味着该函数在“布尔域”上运行,而“布尔域”又恰好有 2 个值。

          也就是说,使用整数、枚举甚至字符串是完全可以的。在 python 中,您可以创建一个仅包含变量 YES、NO、MAYBE 的模块,然后从那里导入到其他地方。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-03-27
            • 1970-01-01
            • 2020-11-18
            • 1970-01-01
            • 2013-05-09
            • 1970-01-01
            • 2017-05-19
            • 2012-07-01
            相关资源
            最近更新 更多