如何检查值是不是在逻辑上是整数?

Posted

技术标签:

【中文标题】如何检查值是不是在逻辑上是整数?【英文标题】:How to check if value is logically integer?如何检查值是否在逻辑上是整数? 【发布时间】:2014-11-15 13:17:00 【问题描述】:

我有一些函数在做数学运算,需要采用整数变量。

我知道我可以通过使用条件 isinstance(x, int) 来强制使用 int 或更严格的type(x) == int,但 IMO 它不是 pythonic。 我认为我的 Python 代码不应该仅仅因为它是 float 而拒绝 2.0

检查值是否为逻辑整数的最佳方法是什么?

逻辑整数是指任意类型的值,代表整数。

应该可以用于像int这样的算术运算,但我不必检查它, 因为我相信在 Python 中 任何 条件集都会被愚弄。

例如,True-22.0Decimal(2)Fraction(4, 2)逻辑整数, 当'2'2.5 不是时。

目前我使用int(x) == x,但我不确定它是否是最佳解决方案。 我知道我可以使用float(x).is_integer()。 我也看到了x % 1 == 0

【问题讨论】:

你可以在这里扩展我的答案:***.com/a/26892252/3001761。或者,int(x) == x 没有任何问题。 你有一个简单的解决方案 - 使用它。 请注意,仅使用int(x) == x 不一定足够。自定义类可以实现__int__(调用int 时调用的协议),但无法使用与float 等其他数字类型一起使用的比较运算符。 除了根据需要使用对象并处理异常之外,无法确定一个类是否实现了您需要的所有操作。 @NedBatchelder 如果您事先知道所有操作,您可以在我的元类解决方案中使用它们。如果您不知道提前定义“完整性”边界的操作(对于您的应用程序),那么我同意您的看法。 【参考方案1】:

通常人们会检查Integral ABC(抽象基类),但无论它们的值如何,浮点值通常并不意味着被视为整数。如果需要,请记下他们的is_integer 属性:

(1324.34).is_integer()
#>>> False

(1324.00).is_integer()
#>>> True

然后代码就是:

from numbers import Integral

def is_sort_of_integer(value):
    if isinstance(value, Integral):
        return True

    try:
        return value.is_integer()

    except AttributeError:
        return False

如果您还想处理没有is_integer 方法的Decimals、Fractions 等,最好的选择可能就是:

from numbers import Number, Integral

def is_sort_of_integer(value):
    if isinstance(value, Integral):
        return True

    if isinstance(value, Number):
        try:
            return not value % 1
        except TypeError:
            return False

    return False

Integral 检查不应该在这种情况下是必需的,但最好保留它。

【讨论】:

感谢您提及numbers.Integral。我知道当我需要int 时,我可以要求参数为Integral。答案中没有提到其他条件被拒绝,例如3786428937490273548972435971204632547289375498324893265.3,浮动太大而不能递减。【参考方案2】:

解决此问题的一种方法是使用元类来定义__instancecheck__ 的自定义实现,然后定义具有元类的具体类,并在具体类中使用isinstance

这有引入元类机器的缺点,这通常是无关紧要的。

但它的好处是可以干净地封装您希望使用的任何属性,您的应用程序的“逻辑整数”的意思。

这里有一些代码来展示这种方法:

class Integral(type):
    def __instancecheck__(self, other):
        try:
            cond1 = int(other) == other
            cond2 = (other >= 1.0) or (other < 1.0)
            # ... plus whatever other properties you want to check
            return all([cond1, cond2,])
        except:
            return False

class IntLike:
    __metaclass__ = Integral

print isinstance(-1, IntLike)
print isinstance('1', IntLike)
print isinstance(27.2, IntLike)
print isinstance(27.0, IntLike)
print isinstance(fractions.Decimal(2), IntLike)
print isinstance(fractions.Fraction(4, 2), IntLike)

打印出来:

True
False
False
True
True
True

请注意,摆脱逻辑整数的数学概念应该适用于您的程序的想法很重要。除非你带上一些校对机器,否则你不会得到那个。例如,您提到了某些可用的函数等属性,特别是sqrt——但这不适用于负整数,除非您实现自定义行为来检查复杂结果。

它将是特定于应用程序的。例如,其他人可能会选择此代码并对其进行修改,以便 '1' 确实注册为 IntLike,也许为了他们的应用程序,它是正确的。

这就是为什么我喜欢这里的元类方法。它使您可以明确地表示您正在施加的每个条件,并将它们存储在一个位置。然后使用常规的isinstance 机制可以让代码阅读器非常清楚您要做什么。

最后,请注意,没有给定的条件永远是完美的。例如,下面的类可用于“欺骗”int(x) == x 技巧:

class MyInt(object):
    def __init__(self, value):
        self.value = value

    def __int__(self):
        return int(self.value)

    def __add__(self, other):
        return self.value + other

    #... define all needed int operators

    def __eq__(self, other):
        if isinstance(other, float):
            raise TypeError('How dare you compare me to a float!')
        return self.value == other

    # ..etc

然后你会得到这样的行为:

In [90]: mi = MyInt(3)

In [91]: mi + 4
Out[91]: 7

In [92]: mi == 3
Out[92]: True

In [93]: int(mi) == mi
Out[93]: True

In [94]: mi == 3.0
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-93-827bea4a197f> in <module>()
----> 1 mi == 3.0

<ipython-input-89-66fec92fab7d> in __eq__(self, other)
     13     def __eq__(self, other):
     14         if isinstance(other, float):
---> 15             raise TypeError('How dare you compare me to a float!')
     16         return self.value == other
     17 

TypeError: How dare you compare me to a float!

isinstance(mi, IntLike) 是否返回True 将完全取决于您如何为MyInt 实现比较运算符,以及您在Integral__instancecheck__ 中所做的任何额外检查。

【讨论】:

在 Python 3 中应该是 class IntLike(metaclass = Integral): pass。此外,AFAIK,except: 不是最好的方法,更好的是 except Exception:,因为它允许像 KeyboardInterruptSystemExit 这样的异常通过。 Python 3 的问题非常不相关,并不需要在这里记录。将一些代码从 Python 2 转换为 Python 3 的问题将是一个单独的问题,超出了本问题的范围。此外,让其他异常类型通过似乎是个坏主意。它与检查整数的想法超级无关,甚至在这里提到它似乎都非常迂腐和挑剔。在 Python 中使用普通的 except: 非常好并且很常见。 Why is “except: pass” a bad programming practice? 你好像不明白。我没有使用pass,而是返回False。我的程序的逻辑是正确在任何异常时返回False 因为在执行try 块期间以处理信号的方式对 CTRL-C 进行计时几乎是不可能的。唯一会发生这种情况的情况是try 块正在执行非常昂贵的代码,或者故意休眠或等待系统资源。如果它正在做一个简单的计算,那么在 try 块期间处理 CTRL-C 按下的可能性将呈指数增长。对于这个问题,try 的主体应该是简单的整数检查,情况就是这样。如果您将第一个 pass 替换为 time.sleep(1),则 CTRL-C 失败。【参考方案3】:

在某些情况下,int(x) == xx.isinteger()x % 1 == 0 都无法按照我的意愿处理。

例子:

>>> big_float = 9999999999999999.1

big_float 足够大,可以忽略从中减去一些小数(AFAIK,称为下溢):

>>> big_float -1 == big_float
True

然后

>>> def fib(n):
...     current, prev = 1, 0
...     while n > 0:
...         current, prev, n = current+prev, current, n-1
...     return prev
...
>>> fib(big_float) #unwanted infinite loop

在某些情况下,int(x) == xx.isinteger()x % 1 == 0 都无法按照我的意愿处理。

>>> int(big_float) == big_float
True
>>> big_float.is_integer()
True
>>> big_float % 1 == 0
True

解决方案

我们可以像int(big_float)一样检查big_float是否:

>>> int(big_float) -1 == big_float -1
False

当然,这种方法也适用于更琐碎的情况,比如:

>>> x = 2.1
>>> int(x) -1 == x -1
False

当然,你不必减去 1,你可以使用任何你需要的数学运算。

注意这种情况可能会抛出异常:

>>> x = '2'
>>> int(x) -1 == x -1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'str' and 'int'

【讨论】:

【参考方案4】:

简单,尝试将其转换为整数。如果int() 有效,则“逻辑上是整数”,否则无效。

try:
    int(thing)
    is_integer = True
except ValueError:
    is_integer = False

但是,通常情况下,与其这样做,不如在需要的代码中使用 int(thing),如果它最终不是整数,则捕获错误并适当地处理这种情况。

【讨论】:

所以字符串"໑"“逻辑上”是一个整数? 我明确表示2.5 不是逻辑整数,但您的代码说它是。 绝对是我的错误。在这种情况下,只需执行 int(thing) == thing 即可检查这两种情况

以上是关于如何检查值是不是在逻辑上是整数?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 plpgsql 检查一个值是不是为整数?

如何使用 Python 在 Selenium WD 中检查变量的值是不是为整数? [复制]

检查输入到文本字段的值是不是是给定范围内的整数

检查双精度值是不是为整数 - Swift

检查整数是不是为 0 并返回布尔值的简短方法

如何检查特定整数是不是在列表中