为什么Python没有符号函数?

2024-04-29 07:13:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我不明白为什么Python没有sign函数。它有一个内置的abs(我认为是sign的妹妹),但是没有sign

在python 2.6中甚至有一个copysign函数(在math中),但没有符号。当你只需要写一个sign,然后直接从abs(x) * sign(y)获得copysign时,为什么还要费心写一个copysign(x,y)?后者要清楚得多:x的符号是y,而对于copypsign,你必须记住是x的符号是y,还是y的符号是x!

显然sign(x)提供的信息不比cmp(x,0)多,但这也更具可读性(对于python这样一种可读性很强的语言,这是一个很大的优势)。

如果我是一个python设计人员,我会反过来:不是内置的,而是一个sign。当你需要cmp(x,y)时,你可以只做一个sign(x-y)(或者,对于非数值的东西来说,更好的方法是,只做一个x>;y-当然这需要sorted接受一个布尔值而不是一个整数比较器)。这也会更清楚:当x>y时为阳性(而使用cmp时,必须记住当第一个大的时为阳性,但可能是相反的方式)。当然cmp本身也有意义,因为其他原因(例如,当排序非数字的东西时,或者如果您希望排序是稳定的,这不可能与简单的布尔值一起使用)

所以,问题是:为什么Python设计器决定不使用该语言的sign函数?为什么要麻烦copysign而不是它的父sign

我遗漏了什么吗?

编辑-在彼得·汉森评论之后。 很公平,你没有使用它,但你没有说你用python做什么。在我使用python的7年里,我无数次地需要它,最后一个是折断骆驼背的稻草!

是的,你可以通过cmp,但是我需要通过的90%的次数是用一个类似于 lambda x,y: cmp(score(x),score(y))这对sign很有用。

最后,我希望您同意signcopysign更有用,所以即使我接受了您的观点,为什么还要费心在数学中定义它,而不是用符号?文案怎么能比文案有用呢?


Tags: 函数语言排序符号费心abs阳性内置
3条回答

编辑:

确实有一个patch包含了sign()math中,但是没有被接受,因为他们在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 ^{}


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

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函数,但是copysign第一个参数是1的情况下工作得很好。我不同意signcopysign更有用,因为我已经证明它只是相同功能的子集。

“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()更有用。

至于标准Python中没有IEEE的signbit(x)的具体原因,我不知道。我可以做出假设,但那只是猜测。

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

你写道:

If I were a python designer, I would been the other way around: no cmp() builtin, but a sign()

这意味着您不知道除了数字之外,cmp()还用于其他事情。cmp(“This”,“That”)不能用sign()函数实现。

编辑以将我的其他答案整理到其他位置

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

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

假设符号(x)被添加到Python中,作为abs(x)的补充。如果“x”是一个实现了uu abs_uu(self)方法的用户定义类的实例,那么abs(x)将调用x.u abs_u()。为了正常工作,为了以同样的方式处理abs(x),Python必须获得一个符号(x)插槽。

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

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

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

它超出了StackOverflow的范围。把它放到一个Python列表中。

另一行符号()

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

如果希望它返回0(x=0):

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

相关问题 更多 >