为什么Python没有签名功能?

时间:2009-12-31 16:56:41

标签: python language-design

我无法理解为什么Python没有sign函数。它有abs内置(我认为是sign的姐妹),但没有sign

在python 2.6中,甚至有一个copysign函数(在math中),但没有任何符号。当您只需撰写copysign(x,y)然后直接从sign获取copysign时,为什么还要编写abs(x) * sign(y)?后者会更清楚:x带有y的符号,而对于copysign,你必须记住它是带有y或y的符号的x,带有x的符号!

显然sign(x)提供的内容不仅仅是cmp(x,0),但它也更具可读性(对于像python这样的高可读性语言,这本来是一个很大的优点)。

如果我是一名蟒蛇设计师,我就是另一种方式:没有cmp内置,而是sign。当你需要cmp(x,y)时,你可以做一个sign(x-y)(或者,对于非数字的东西更好,只需要一个x> y - 当然这应该要求sorted接受一个布尔值而不是整数比较器)。这也会更明确:x>y时为正(而cmp时,当第一 更大时,您必须记住约定为正面,但它可能是另一种方式)。当然cmp因其他原因而有意义(例如,在排序非数字事物时,或者如果你希望排序稳定,这不可能只使用布尔值)

所以,问题是:为什么Python设计师决定让sign函数退出语言?为什么要对copysign而不是其父sign进行麻烦?

我错过了什么吗?

编辑 - 在Peter Hansen评论之后。 很公平,你没有使用它,但你没有说你使用python的。在我使用python的7年中,我无数次需要它,而最后一个是打破骆驼背部的稻草!

是的,你可以通过cmp,但是我需要通过它的90%的时间都是像成语一样 lambda x,y: cmp(score(x),score(y))可以使用标志就好了。

最后,我希望你同意signcopysign更有用,所以即使我买了你的观点,为什么还要在数学中定义它而不是标志呢? copysign如何比签名更有用?

14 个答案:

答案 0 :(得分:195)

修改

确实有一个patchmath中包含sign(),但未被接受,因为他们不同意what it should return in all the edge cases(+/- 0 ,+ / - nan等)

所以他们决定只实施copysign,虽然更详细,但used to delegate to the end user the desired behavior for edge cases可以sometimes might require the call to cmp(x,0)


我不知道为什么它不是内置的,但我有一些想法。

copysign(x,y):
Return x with the sign of y.

最重要的是,copysignsign的超集!使用x = 1调用copysignsign函数相同。所以你可以使用copysign忘掉它

>>> math.copysign(1, -4)
-1.0
>>> math.copysign(1, 3)
1.0

如果你厌倦了传递两个完整的论点,你可以用这种方式实现sign,它仍然可以与其他人提到的IEEE内容兼容:

>>> sign = functools.partial(math.copysign, 1) # either of these
>>> sign = lambda x: math.copysign(1, x) # two will work
>>> sign(-4)
-1.0
>>> sign(3)
1.0
>>> sign(0)
1.0
>>> sign(-0.0)
-1.0
>>> sign(float('nan'))
-1.0

其次,通常当你想要某些东西的符号时,你最终会将它与另一个值相乘。当然,这基本上是copysign所做的。

所以,而不是:

s = sign(a)
b = b * s

你可以这样做:

b = copysign(b, a)

是的,我很惊讶你已经使用Python 7年了,并认为cmp可以很容易地删除并替换为sign!您是否从未使用__cmp__方法实现课程?您是否从未调用过cmp并指定了自定义比较器函数?

总之,我发现自己也想要一个sign函数,但第一个参数为1的copysign将正常工作。我不同意signcopysign更有用,因为我已经证明它只是相同功能的一个子集。

答案 1 :(得分:52)

“copysign”由IEEE 754定义,是C99规范的一部分。这就是为什么它在Python中。该函数不能完全由abs(x)* sign(y)实现,因为它应该如何处理NaN值。

>>> import math
>>> math.copysign(1, float("nan"))
1.0
>>> math.copysign(1, float("-nan"))
-1.0
>>> math.copysign(float("nan"), 1)
nan
>>> math.copysign(float("nan"), -1)
nan
>>> float("nan") * -1
nan
>>> float("nan") * 1
nan
>>> 

这使得copysign()比sign()更有用。

至于为什么IEEE的signbit(x)在标准Python中不可用的具体原因,我不知道。我可以做出假设,但这是猜测。

数学模块本身使用signbit(1,x)作为检查x是负还是非负的方法。对于大多数处理数学函数的情况,这些函数似乎比具有返回1,0或-1的符号(x)更有用,因为有一个案例需要考虑。例如,以下内容来自Python的数学模块:

static double
m_atan2(double y, double x)
{
        if (Py_IS_NAN(x) || Py_IS_NAN(y))
                return Py_NAN;
        if (Py_IS_INFINITY(y)) {
                if (Py_IS_INFINITY(x)) {
                        if (copysign(1., x) == 1.)
                                /* atan2(+-inf, +inf) == +-pi/4 */
                                return copysign(0.25*Py_MATH_PI, y);
                        else
                                /* atan2(+-inf, -inf) == +-pi*3/4 */
                                return copysign(0.75*Py_MATH_PI, y);
                }
                /* atan2(+-inf, x) == +-pi/2 for finite x */
                return copysign(0.5*Py_MATH_PI, y);

在那里你可以清楚地看到copysign()是一个比三值sign()函数更有效的函数。

您写道:

  

如果我是一名蟒蛇设计师,那我就是arond的另一种方式:没有cmp()内置,但是一个符号()

这意味着你不知道cmp()用于除数字之外的东西。 cmp(“This”,“That”)无法用sign()函数实现。

编辑以在其他地方整理我的其他答案

你的理由基于如何经常一起看abs()和sign()。由于C标准库不包含任何类型的“sign(x)”函数,因此我不知道您如何证明您的观点。有一个abs(int)和fabs(双)和fabsf(浮动)和fabsl(长)但没有提到标志。有“copysign()”和“signbit()”,但这些仅适用于IEEE 754号码。

对于复数,在Python中返回的符号(-3 + 4j)会被实现吗? abs(-3 + 4j)返回5.0。这是一个明确的例子,说明如何在sign()没有意义的地方使用abs()。

假设sign(x)被添加到Python中,作为abs(x)的补充。如果'x'是实现__abs __(self)方法的用户定义类的实例,则abs(x)将调用x .__ abs __()。为了正常工作,以相同的方式处理abs(x),Python必须获得符号(x)插槽。

对于相对不需要的功能,这是过度的。此外,为什么签名(x)存在且非负(x)和非正(x)不存在?我的Python数学模块实现的片段显示了如何使用copybit(x,y)来实现非负(),这是一个简单的符号(x)不能做到的。

Python应该支持更好地支持IEEE 754 / C99数学函数。这将添加一个signbit(x)函数,它可以在浮点数的情况下执行您想要的操作。它不适用于整数或复数,更不用说字符串,也不会有你想要的名字。

你问“为什么”,答案是“符号(x)没用”。你声称它很有用。然而,你的评论表明你不知道能够做出这样的断言,这意味着你必须展示其需要的令人信服的证据。说NumPy实现它并不足够令人信服。您需要显示如何使用符号函数改进现有代码的案例。

并且它超出了StackOverflow的范围。把它改为Python列表中的一个。

答案 2 :(得分:30)

另一个用于sign()的衬垫

sign = lambda x: (1, -1)[x<0]

如果您希望它为x = 0返回0:

sign = lambda x: x and (1, -1)[x<0]

答案 3 :(得分:17)

由于cmp已为removed,您可以使用

获得相同的功能
def cmp(a, b):
    return (a > b) - (a < b)

def sign(a):
    return (a > 0) - (a < 0)

适用于floatint甚至Fraction。在float的情况下,通知sign(float("nan"))为零。

Python不要求比较返回布尔值,因此强制比较bool()可以防止允许但不常见的实现:

def sign(a):
    return bool(a > 0) - bool(a < 0)

答案 4 :(得分:7)

尝试运行此操作,其中x是任意数字

int_sign = bool(x > 0) - bool(x < 0)

对bool()的强制处理比较运算符不返回布尔值的possibility

答案 5 :(得分:5)

numpy有一个sign功能,并为你提供其他功能的奖励。所以:

import numpy as np
x = np.sign(y)

请注意,结果是numpy.float64:

>>> type(np.sign(1.0))
<type 'numpy.float64'>

对于像json这样的东西,这很重要,因为json不知道如何序列化numpy.float64类型。在这种情况下,您可以这样做:

float(np.sign(y))

获得常规浮动。

答案 6 :(得分:4)

是的,正确的sign()函数至少应该在数学模块中 - 因为它是numpy。因为经常需要它来用于面向数学的代码。

math.copysign()也是独立的。

cmp()obj.__cmp__() ...通常具有高度重要性。不仅仅是面向数学的代码。考虑比较/排序元组,日期对象,......

http://bugs.python.org/issue1640关于遗漏math.sign()的开发论点是奇怪的,因为:

  • 没有单独的-NaN
  • sign(nan) == nan无忧(如exp(nan)
  • sign(-0.0) == sign(0.0) == 0无忧无虑
  • sign(-inf) == -1无忧无虑

- 因为它在numpy

答案 7 :(得分:4)

事实并非如此。

解决此问题的最佳方法是:

sign = lambda x: bool(x > 0) - bool(x < 0)

答案 8 :(得分:2)

符合Wikipedia定义

definition on Wikipedia的内容为:

sign definition

因此

sign = lambda x: -1 if x < 0 else (1 if x > 0 else 0)

此函数定义executes fast并产生保证的correct results for 0, 0.0, -0.0, -4 and 5(请参阅对这些错误答案的注释)。

答案 9 :(得分:0)

你不需要一个,你可以使用:

If not number == 0:
    sig = number/abs(number)
else:
    sig = 0

答案 10 :(得分:0)

在Python 2中,cmp()返回一个整数:不要求结果为-1,0或1,因此sign(x)与cmp(x,0)不同。

在Python 3中,cmp()已被删除,有利于丰富的比较。对于cmp(),Python 3建议(https://docs.python.org/3/whatsnew/3.0.html):

def cmp(a, b):
    return (a > b) - (a < b)

对于cmp()来说很好,但又不能用于sign(),因为比较运算符不需要返回布尔值(https://docs.python.org/3/reference/datamodel.html#object.lt)。

为了解决这种可能性,必须将比较结果强制为布尔值:

 def sign(a):
    return bool(x > 0) - bool(x < 0)

这适用于任何完全有序的类型(包括NaN或无穷大等特殊值)。

答案 11 :(得分:0)

我的两分钱,如果我们将1解释为正数,将0解释为负数,也许解释为is_positive oneliner:

输入:

x = -42
sign = 1 if x >= 0 else 0
print(sign)    

输出:

>> 0

答案 12 :(得分:0)

其他答案中列出的许多情况忽略了特殊情况 (+/-0) 或假设 sign(-0.0) == sign(0.0)。这可能很幼稚,但根据 IEEE 的当前实现,我们已经有了 -0.0 == 0.0 并且有了 sign() 可以让我们消除两者之间的歧义。

FogleBird 提供的示例似乎是最好的定义,因为它似乎可以处理 +/- 0、INFINITY 和 NaN。

答案 13 :(得分:-6)

“sign”的原因不包括在内,如果我们在内置函数列表中包含所有有用的单行,那么Python将不再容易实用。 如果您经常使用此功能,那么为什么不自己考虑它?这样做并不是很困难甚至是单调乏味。