为什么Python没有签名功能?

Dav*_*ide 217 python language-design

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

在python 2.6中甚至有一个copysign函数(在数学中),但没有符号.copysign(x,y)当你只能写一个sign然后copysign直接从中获取时,为什么还要写一个abs(x) * sign(y)?后者会更加清晰:x带有y的符号,而对于copysign,你必须记住它的x是否带有y或y的符号,带有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呢?

我错过了什么吗?

编辑 - 在彼得汉森评论之后.很公平,你没有使用它,但你没有说你使用python的.在我使用蟒蛇的7年中,我无数次需要它,最后一根是打破骆驼背部的稻草!

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

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

Fog*_*ird 212

编辑:

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

所以他们决定只实现copysign,它可以用来向最终用户委托边缘情况所需的行为(虽然更冗长),这有时可能需要调用cmp(x,0).


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

copysign(x,y):
Return x with the sign of y.
Run Code Online (Sandbox Code Playgroud)

最重要的copysign是,是超集sign!copysign使用x = 1 调用与sign函数相同.所以你可以使用copysign忘记它.

>>> math.copysign(1, -4)
-1.0
>>> math.copysign(1, 3)
1.0
Run Code Online (Sandbox Code Playgroud)

如果您厌倦了传递两个完整的参数,您可以实现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
Run Code Online (Sandbox Code Playgroud)

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

所以,而不是:

s = sign(a)
b = b * s
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

b = copysign(b, a)
Run Code Online (Sandbox Code Playgroud)

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

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

  • 在[0,0.0,-0.0]中使用`[int(copysign(1,zero))为零,给出`[1,1,-1]`.根据http://en.wikipedia.org/wiki/Sign_function,应该是`[0,0,0]` (30认同)
  • 看到false sign()替换定义,乘以sign(a)的伪等价,copysign动机的错误解释,以及问题中已经提到的正确替换"cmp(x,0)" - 有没有多少信息,我不清楚为什么这是这么多选票的"接受"答案. (12认同)
  • @Andrew - @ user238424的通话顺序是正确的.`copysign(a,b)`返回带有b的符号的b - b是变化的输入,a是用b的符号标准化的值.在这种情况下,评论者说明copysign(1,x)作为sign(x)的替换失败,因为它为x = 0返回1,而sign(0)将评估为0. (11认同)
  • `s = sign(a)b = b*s`不等于`b = copysign(b,a)`!它不考虑b的符号.例如,如果`a = b = -1`,第一个代码将返回1,而第二个代码返回-1 (10认同)
  • 浮标将"标志"与"价值"分开; -0.0是负数,即使这似乎是一个实现错误.简单地使用`cmp()`将得到所需的结果,可能几乎所有人都会关心的情况:`[cmp(零,0)为零(0,0.0,-0.0,-4,5)] = =>`[0,0,0,-1,1]`. (7认同)
  • 我从未见过`sign(0) == 1` 的符号函数。这似乎不符合我听说过的任何约定。 (4认同)
  • 你的顺序错了。[int(copysign(zero, 1)) for Zero in (0, 0.0, -0.0)] 给出您要查找的 [0,0,0] 。 (2认同)

And*_*lke 56

"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
>>> 
Run Code Online (Sandbox Code Playgroud)

这使得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);
Run Code Online (Sandbox Code Playgroud)

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

你写了:

如果我是一名python设计师,我就会采用另一种方式:没有内置的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实现它并不足够令人信服.您需要显示如何使用sign函数改进现有代码的情况.

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

  • 好吧,如果那会让你开心,我不会,但是Python 3既没有`cmp()`也没有`sign()`:-) (5认同)
  • 编写一个能够与IEEE 754正常工作的好的sign()函数并非易事.这是将它包含在语言中的一个好点,而不是将其排除在外,即使我没有在问题中详细说明这一点 (4认同)
  • 符号或正负号函数是标准数学函数,定义为 sign(z) = z/|z|。它对于复数的含义是明确定义的;只需将相同的定义与除法和绝对值的复杂版本一起使用即可。结果是一个复数,其大小为 1;例如,符号(-3+4j) 是-0.6+0.8j。我不明白 C 中该函数的缺失与 Python 中该函数的缺失有什么关系。它存在于 Microsoft BASIC 中;这有关系吗? (4认同)
  • 您对"如果希望排序稳定"的方式的评论意味着您也不知道排序的稳定性.你的copysign和sign相同的声明表明你在这篇文章之前对IEEE 754数学知之甚少.Python应该在核心中实现所有754数学函数吗?它应该为非C99编译器做些什么?非754平台?"isnonnegative"和"isnonpositive"也是有用的功能.Python应该还包括那些吗?abs(x)延伸到x .__ abs __(),所以sign(x)应该是x .__ sign __()吗?对它的需求或需求很少,为什么它应该被置于核心? (2认同)
  • math.copysign(1,float(" - nan"))在2.7中尝试时返回1.0而不是-1.0 (2认同)

dan*_*lmo 31

标志的另一个班轮()

sign = lambda x: (1, -1)[x<0]
Run Code Online (Sandbox Code Playgroud)

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

sign = lambda x: x and (1, -1)[x<0]
Run Code Online (Sandbox Code Playgroud)

  • 为什么?问题本身承认“cmp(x, 0)”相当于“sign”,并且“lambda x: cmp(x, 0)”比您建议的更具可读性。 (6认同)
  • 你不能读`cmp(x,0)`并知道它的作用. (6认同)
  • `sign = lambda x:-1如果x <0则1`是**快**15%**.与`sign = lambda x:x和(-1,如果x <0,否则为1)`相同. (5认同)
  • 确实,我错了。我假设“cmp”被指定返回-1,0,+1,但我发现规范并不能保证这一点。 (3认同)
  • 使用列表代替“-1 if x &lt; 0 else 1”有什么优势吗? (3认同)
  • 我检查了各种例子。如果您考虑@MateenUlhaq 的建议,它是最快的符号函数,返回 -1, 0, 1。 (2认同)

小智 22

由于cmp已被删除,您可以获得相同的功能

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

def sign(a):
    return (a > 0) - (a < 0)
Run Code Online (Sandbox Code Playgroud)

它适用于float,int甚至Fraction.在这种情况下float,通知sign(float("nan"))是零.

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

def sign(a):
    return bool(a > 0) - bool(a < 0)
Run Code Online (Sandbox Code Playgroud)


Ser*_*ndt 10

仅符合Wikipedia定义的正确答案

维基百科上定义如下:

标志定义

因此,

sign = lambda x: -1 if x < 0 else (1 if x > 0 else (0 if x == 0 else NaN))                                              
Run Code Online (Sandbox Code Playgroud)

出于所有意图和目的,可以简化为:

sign = lambda x: -1 if x < 0 else (1 if x > 0 else 0)
Run Code Online (Sandbox Code Playgroud)

此函数定义执行速度很快,并能确保为0、0.0,-0.0,-4和5提供正确的结果(请参见其他错误答案的注释)。

请注意,零(0)既不是正数也不是负数

  • (a &gt; 0) - (a &lt; 0) 也符合。 (4认同)
  • 这个答案说明了 python 是多么简洁而强大。 (2认同)
  • @JürgenStrobel 我确切地知道你的意思,我也一直在考虑这个问题。我现在扩展了正确的形式主义的答案,同时保留了大多数用例的简化版本。 (2认同)

Luc*_*uca 7

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

import numpy as np
x = np.sign(y)
Run Code Online (Sandbox Code Playgroud)

请注意,结果是numpy.float64:

>>> type(np.sign(1.0))
<type 'numpy.float64'>
Run Code Online (Sandbox Code Playgroud)

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

float(np.sign(y))
Run Code Online (Sandbox Code Playgroud)

获得常规浮动.


小智 7

尝试运行它,其中x是任意数字

int_sign = bool(x > 0) - bool(x < 0)
Run Code Online (Sandbox Code Playgroud)

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

  • @yucer 不,他真正的意思是强制转换为 bool (无论如何,它是 int 的子类),因为理论上的可能性,他给出了解释的链接。 (2认同)

kxr*_*kxr 5

是的,正确的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 中


All*_*enT 5

在 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)
Run Code Online (Sandbox Code Playgroud)

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

为了处理这种可能性,必须将比较结果强制转换为布尔值:

 def sign(x):
    return bool(x > 0) - bool(x < 0)
Run Code Online (Sandbox Code Playgroud)

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


Mic*_*ter 5

它只是没有。

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

sign = lambda x: bool(x > 0) - bool(x < 0)
Run Code Online (Sandbox Code Playgroud)