我不明白为什么Python没有符号函数。它有腹肌(我认为它是sign的姐妹),但没有sign。

在python 2.6中,甚至有一个copysign函数(在数学中),但没有符号。当你可以只写一个符号,然后直接从abs(x) * sign(y)得到copyysign时,为什么还要麻烦写一个copyysign (x,y)呢?后者会更清楚:x和y的符号,而对于copysign,你必须记住它是x和y的符号还是y和x的符号!

显然,sign(x)提供的东西比cmp(x,0)多不了什么,但它的可读性也比这强得多(对于python这样可读性很强的语言来说,这将是一个很大的优点)。

If I were a python designer, I would been the other way arond: no cmp builtin, but a sign. When you need cmp(x,y), you could just do a sign(x-y) (or, even better for non-numerical stuff, just a x>y - of course this should have required sorted accepting a boolean instead of an integer comparator). This would also be more clear: positive when x>y (whereas with cmp you have to remember the convention positive when the first is bigger, but it could be the other way around). Of course cmp makes sense in its own for other reasons (e.g. when sorting non-numerical things, or if you want the sort to be stable, which is not possible using with simply a boolean)

那么,问题是:为什么Python设计者决定将符号函数排除在语言之外?为什么要麻烦复制符号而不是它的父符号呢?

我遗漏了什么吗?

编辑-在Peter Hansen评论之后。 很好,你没有使用它,但你没有说你用python做什么。在我使用python的7年里,我需要它无数次,最后一次是压垮骆驼的最后一根稻草!

是的,你可以传递cmp,但是90%的情况下,我需要传递的是一个成语 x,y: cmp(score(x) score(y))用符号就可以了。

最后,我希望你同意sign比copysign更有用,所以即使我同意你的观点,为什么要在数学中定义它,而不是sign呢?复印签名怎么比签名有用这么多?


当前回答

编辑:

确实有一个补丁在数学中包含了sign(),但它没有被接受,因为他们不同意它在所有边缘情况下(+/-0,+/-nan等)应该返回什么。

因此,他们决定只实现copysign,它(虽然更冗长)可以用于将边界情况所需的行为委托给最终用户——有时可能需要调用cmp(x,0)。


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

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

最重要的是,copyysign是一个超集的符号!用x=1调用copysign与符号函数相同。所以你可以只使用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并指定自定义比较器函数?

总之,我发现自己也想要一个符号函数,但是第一个参数为1的copysign就可以了。我不认为sign会比copysign更有用,因为我已经说明了它只是相同功能的一个子集。

其他回答

它就是没有。

解决这个问题的最好方法是:

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

这个符号函数返回1表示正值,-1表示负值,0表示0.0和-0.0(和nan…)

sign()的另一行代码

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

如果你想让它在x = 0时返回0

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

维基百科上的定义如下:

因此,为了符合定义:

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

就所有意图和目的而言,可以简化为:

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

这个函数定义执行速度很快,并保证得到0、0.0、-0.0、-4和5的正确结果(请参阅其他错误答案的注释)。

注意,零(0)既不是正的也不是负的。

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的符号位(x)在标准Python中不可用的具体原因,我不知道。我可以做假设,但那只是猜测。

math模块本身使用copysign(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()函数更有效的函数。

你写的:

如果我是python设计师,我会用另一种方式:没有内置cmp,而是一个符号。

这意味着您不知道cmp()用于数字以外的东西。cmp("This", "That")不能用sign()函数实现。

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

您的理由是abs()和sign()经常一起出现。由于C标准库不包含任何类型的符号(x)函数,我不知道你如何证明你的观点。有abs(int)和fabs(double)和fabsf(float)和fabsl(long),但没有提到sign()。有copysign()和signbit(),但它们只适用于IEEE 754数字。

对于复数,sign(-3+4j)在Python中会返回什么,如果要实现的话?Abs (-3+4j)返回5.0。这是如何在sign()没有意义的地方使用abs()的一个清楚的例子。

假设将sign(x)添加到Python中,作为abs(x)的补充。如果x是实现__abs__(self)方法的用户定义类的实例,则abs(x)将调用x.__abs__()。为了正确工作,以同样的方式处理abs(x),那么Python必须获得__sign__(x)插槽。

这对于一个相对不需要的函数来说是多余的。此外,为什么符号(x)存在而非负的(x)和非正的(x)不存在?我的Python数学模块实现片段展示了如何使用copysign(x, y)来实现非负(),这是简单符号(x)无法做到的。

Python应该更好地支持IEEE 754/C99数学函数。这将添加一个符号位(x)函数,这将在浮点数的情况下完成你想要的。它不能用于整数或复数,更不用说字符串了,而且它没有您正在寻找的名称。

你问“为什么”,答案是“符号(x)没有用”。你断言它是有用的。然而,你的评论表明,你知道的还不够多,无法做出这样的断言,这意味着你必须拿出令人信服的证据来证明它的必要性。说NumPy实现了它是不够有说服力的。您需要展示如何使用sign()函数改进现有代码的情况。

并且它超出了StackOverflow的范围。相反,请使用Python列表中的一个。

由于cmp已被删除,您可以使用

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

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

它适用于float, int和Fraction。在float的情况下,注意sign(float("nan"))为零。

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

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