如何检查值是不是在逻辑上是整数?
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
、-2
、2.0
、Decimal(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
方法的Decimal
s、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:
,因为它允许像 KeyboardInterrupt
和 SystemExit
这样的异常通过。
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) == x
、x.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) == x
、x.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 即可检查这两种情况以上是关于如何检查值是不是在逻辑上是整数?的主要内容,如果未能解决你的问题,请参考以下文章