为什么Python没有符号功能?

问题:为什么Python没有符号功能?

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

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

显然sign(x),除了cmp(x,0),它不提供任何其他功能,但是它也将更具可读性(对于像python这样的易读性语言,这将是一个很大的优势)。

如果我是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))就可以很好地使用sign了。

最后,我希望您同意这sign会比有用copysign,所以即使我购买了您的视图,为什么还要在数学中定义它而不是符号呢?copysign如何比sign有用呢?

I can’t understand why Python doesn’t have a sign function. It has an abs builtin (which I consider sign‘s sister), but no sign.

In python 2.6 there is even a copysign function (in math), but no sign. Why bother to write a copysign(x,y) when you could just write a sign and then get the copysign directly from abs(x) * sign(y)? The latter would be much more clear: x with the sign of y, whereas with copysign you have to remember if it’s x with the sign of y or y with the sign of x!

Obviously sign(x) does not provide anything more than cmp(x,0), but it would be much more readable that this too (and for a greatly readable language like python, this would have been a big plus).

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)

So, the question is: why did the Python designer(s) decide to leave the sign function out of the language? Why the heck bother with copysign and not its parent sign?

Am I missing something?

EDIT – after Peter Hansen comment. Fair enough that you didn’t use it, but you didn’t say what you use python for. In 7 years that I use python, I needed it countless times, and the last is the straw that broke the camel’s back!

Yes, you can pass cmp around, but 90% of the times that I needed to pass it was in an idiom like lambda x,y: cmp(score(x),score(y)) that would have worked with sign just fine.

Finally, I hope you agree that sign would be more useful than copysign, so even if I bought your view, why bother about defining that in math, instead of sign? How can copysign be so much useful than sign?


回答 0

编辑:

确实,数学中包含一个补丁,但未被接受,因为他们在所有极端情况(+/- 0,+ /-nan等)上均未达成共识。sign()

因此,他们决定仅实施copysign,尽管可以使用它(尽管更为冗长),才能将最终情况下所需的行为委托给最终用户有时可能需要调用cmp(x,0)


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

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

最重要的copysign是,是的超集signcopysignx = 1的调用与sign函数相同。这样您就可以使用它,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就可以了。我不同意这样做sign会比有用copysign,因为我已经证明它只是相同功能的一部分。

EDIT:

Indeed there was a patch which included sign() in math, but it wasn’t accepted, because they didn’t agree on what it should return in all the edge cases (+/-0, +/-nan, etc)

So they decided to implement only copysign, which (although more verbose) can be used to delegate to the end user the desired behavior for edge cases – which sometimes might require the call to cmp(x,0).


I don’t know why it’s not a built-in, but I have some thoughts.

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

Most importantly, copysign is a superset of sign! Calling copysign with x=1 is the same as a sign function. So you could just use copysign and forget about it.

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

If you get sick of passing two whole arguments, you can implement sign this way, and it will still be compatible with the IEEE stuff mentioned by others:

>>> 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

Secondly, usually when you want the sign of something, you just end up multiplying it with another value. And of course that’s basically what copysign does.

So, instead of:

s = sign(a)
b = b * s

You can just do:

b = copysign(b, a)

And yes, I’m surprised you’ve been using Python for 7 years and think cmp could be so easily removed and replaced by sign! Have you never implemented a class with a __cmp__ method? Have you never called cmp and specified a custom comparator function?

In summary, I’ve found myself wanting a sign function too, but copysign with the first argument being 1 will work just fine. I disagree that sign would be more useful than copysign, as I’ve shown that it’s merely a subset of the same functionality.


回答 1

“ 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是负数还是非负数的方法。在大多数情况下,处理数学函数似乎比使sign(x)返回1、0或-1有用,因为要考虑的情况要少得多。例如,以下内容来自Python的math模块:

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(),但是有一个sign()

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

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

您的辩解基于abs()和sign()经常一起出现的方式。由于C标准库不包含任何类型的’sign(x)’函数,因此我不知道您如何证明自己的观点。有一个abs(int)和fabs(double)和fabsf(float)和fabsl(long),但没有提及符号。有“ 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 __()。为了正常工作,以与Python相同的方式处理abs(x),Python将必须获得一个符号(x)插槽。

对于相对不需要的功能来说,这是过多的。此外,为什么应该有sign(x)而不存在nonnegative(x)和nonpositive(x)?我来自Python数学模块实现的代码片段显示了如何使用copybit(x,y)来实现nonnegative(),而简单的sign(x)无法做到这一点。

Python应该支持对IEEE 754 / C99数学函数的更好支持。这将添加一个signbit(x)函数,该函数将在浮点数的情况下实现您想要的功能。它不适用于整数或复数,更不用说字符串了,并且没有您要查找的名称。

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

而且它超出了StackOverflow的范围。将其改为Python列表之一。

“copysign” is defined by IEEE 754, and part of the C99 specification. That’s why it’s in Python. The function cannot be implemented in full by abs(x) * sign(y) because of how it’s supposed to handle NaN values.

>>> 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
>>> 

That makes copysign() a more useful function than sign().

As to specific reasons why IEEE’s signbit(x) is not available in standard Python, I don’t know. I can make assumptions, but it would be guessing.

The math module itself uses copysign(1, x) as a way to check if x is negative or non-negative. For most cases dealing with mathematical functions that seems more useful than having a sign(x) which returns 1, 0, or -1 because there’s one less case to consider. For example, the following is from Python’s math module:

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);

There you can clearly see that copysign() is a more effective function than a three-valued sign() function.

You wrote:

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

That means you don’t know that cmp() is used for things besides numbers. cmp(“This”, “That”) cannot be implemented with a sign() function.

Edit to collate my additional answers elsewhere:

You base your justifications on how abs() and sign() are often seen together. As the C standard library does not contain a ‘sign(x)’ function of any sort, I don’t know how you justify your views. There’s an abs(int) and fabs(double) and fabsf(float) and fabsl(long) but no mention of sign. There is “copysign()” and “signbit()” but those only apply to IEEE 754 numbers.

With complex numbers, what would sign(-3+4j) return in Python, were it to be implemented? abs(-3+4j) return 5.0. That’s a clear example of how abs() can be used in places where sign() makes no sense.

Suppose sign(x) were added to Python, as a complement to abs(x). If ‘x’ is an instance of a user-defined class which implements the __abs__(self) method then abs(x) will call x.__abs__(). In order to work correctly, to handle abs(x) in the same way then Python will have to gain a sign(x) slot.

This is excessive for a relatively unneeded function. Besides, why should sign(x) exist and nonnegative(x) and nonpositive(x) not exist? My snippet from Python’s math module implementation shows how copybit(x, y) can be used to implement nonnegative(), which a simple sign(x) cannot do.

Python should support have better support for IEEE 754/C99 math function. That would add a signbit(x) function, which would do what you want in the case of floats. It would not work for integers or complex numbers, much less strings, and it wouldn’t have the name you are looking for.

You ask “why”, and the answer is “sign(x) isn’t useful.” You assert that it is useful. Yet your comments show that you do not know enough to be able to make that assertion, which means you would have to show convincing evidence of its need. Saying that NumPy implements it is not convincing enough. You would need to show cases of how existing code would be improved with a sign function.

And that it outside the scope of StackOverflow. Take it instead to one of the Python lists.


回答 2

另一个衬里的sign()

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

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

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

Another one liner for sign()

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

If you want it to return 0 for x = 0:

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

回答 3

由于cmp删除,因此您可以获得与

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)

Since cmp has been removed, you can get the same functionality with

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

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

It works for float, int and even Fraction. In the case of float, notice sign(float("nan")) is zero.

Python doesn’t require that comparisons return a boolean, and so coercing the comparisons to bool() protects against allowable, but uncommon implementation:

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

回答 4

仅符合Wikipedia定义的正确答案

维基百科上定义如下:

因此,

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)既不是正数也不是负数

Only correct answer compliant with the Wikipedia definition

The definition on Wikipedia reads:

Hence,

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

Which for all intents and purposes may be simplified to:

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

This function definition executes fast and yields guaranteed correct results for 0, 0.0, -0.0, -4 and 5 (see comments to other incorrect answers).

Note that zero (0) is neither positive nor negative.


回答 5

numpy具有符号功能,并且还为您提供其他功能的加成。所以:

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))

获得定期浮动。

numpy has a sign function, and gives you a bonus of other functions as well. So:

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

Just be careful that the result is a numpy.float64:

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

For things like json, this matters, as json does not know how to serialize numpy.float64 types. In that case, you could do:

float(np.sign(y))

to get a regular float.


回答 6

尝试运行此命令,其中x是任意数字

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

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

Try running this, where x is any number

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

The coercion to bool() handles the possibility that the comparison operator doesn’t return a boolean.


回答 7

是的,正确的sign()函数至少应该在math模块中-就像在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中

Yes a correct sign() function should be at least in the math module – as it is in numpy. Because one frequently needs it for math oriented code.

But math.copysign() is also useful independently.

cmp() and obj.__cmp__() … have generally high importance independently. Not just for math oriented code. Consider comparing/sorting tuples, date objects, …

The dev arguments at http://bugs.python.org/issue1640 regarding the omission of math.sign() are odd, because:

  • There is no separate -NaN
  • sign(nan) == nan without worry (like exp(nan) )
  • sign(-0.0) == sign(0.0) == 0 without worry
  • sign(-inf) == -1 without worry

— as it is in numpy


回答 8

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

在Python 3中,cmp()已删除它,以进行丰富的比较。对于cmp(),Python 3 建议这样做

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

这对cmp()很好,但是又不能用于sign(),因为比较运算符不需要返回booleans

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

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

这适用于所有type完全有序的(包括特殊值,例如NaNinfinies)。

In Python 2, cmp() returns an integer: there’s no requirement that the result be -1, 0, or 1, so sign(x) is not the same as cmp(x,0).

In Python 3, cmp() has been removed in favor of rich comparison. For cmp(), Python 3 suggests this:

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

which is fine for cmp(), but again can’t be used for sign() because the comparison operators need not return booleans.

To deal with this possibility, the comparison results must be coerced to booleans:

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

This works for any type which is totally ordered (including special values like NaN or infinities).


回答 9

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

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

You dont need one, you can just use:

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

Or create a function as described by others:

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

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

回答 10

只是没有。

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

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

It just doesn’t.

The best way to fix this is:

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

回答 11

之所以没有包含“符号”,是因为如果我们在内置函数列表中包含所有有用的单行代码,那么使用Python就会变得不那么容易和实用。如果您经常使用此功能,那么为什么不自己考虑呢?这样做并不难,甚至乏味。

The reason “sign” is not included is that if we included every useful one-liner in the list of built-in functions, Python wouldn’t be easy and practical to work with anymore. If you use this function so often then why don’t you do factor it out yourself? It’s not like it’s remotely hard or even tedious to do so.