Pythonに符号関数がないのはなぜですか?


241

なぜPythonにsign関数がないのか理解できません。それは持っているabs(私は考える組み込みsignの妹)が、ありませんsign

Python 2.6にはcopysignmathの)関数さえありますが、符号はありません。なぜcopysign(x,y)a signと書いて、copysign直接から直接得ることができるのに、わざわざaを書くのabs(x) * sign(y)ですか?後者の方がはるかに明確です。xにyの符号が付いているのに対し、copysignでは、xがyの符号を持つか、yがxの符号を持つかを覚えておく必要があります。

明らかにsign(x)以外のものは何も提供しませんcmp(x,0)が、これもはるかに読みやすくなります(Pythonのような非常に読みやすい言語の場合、これは大きなプラスになります)。

もし私がpythonデザイナーだったら、私はその逆でした:cmp組み込みではなくsign。必要なcmp(x,y)場合は、単にaを実行することができます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がそれよりも有用であることをあなたが同意することを望みますcopysign、それで私があなたの見解を買ったとしても、なぜそれを符号の代わりに数学で定義することに悩むのですか?どのようにして、コピーサインはサインよりも非常に便利ですか?


33
@dmazzoni:この議論は、このサイトのすべての質問に有効ではないでしょうか?stackoverflowを閉じて、関連するトピックの開発者またはユーザーのメーリングリストにすべての質問をしてください!
Davide、

43
質問の適切な場所は、回答される可能性が高い場所です。したがって、stackoverflowは適切な場所です。
Stefano Borini、

23
-1:@Davide:「なぜ」および「なぜしないのか」の質問は、一般的にここでは答えられません。Python開発のプリンシパルのほとんどはここでは質問に答えないので、「なぜ」または「なぜそうでない」の質問に答えることはほとんどありません(あるとしても)。さらに、解決する問題はありません。あなたは怒りを持っているように聞こえます。問題が発生した場合(「この例では、サインがないことをどうすれば回避できますか?」)は賢明です。この場所では「なぜしない」は賢明ではありません。
S.Lott、2009

31
質問は少し感情的かもしれませんが、私はそれが悪い質問だとは思いません。多くの人が組み込みのサイン関数を探していると思いますので、なぜそれがないのか不思議に思うかもしれません。
FogleBird 2009

17
これは完全に客観的な質問です。「Pythonに特定の機能がない理由」は、言語設計の歴史についての正当なクエリであり、python-devまたは他のフォーラム(ブログの投稿)からの適切なディスカッションにリンクすることで回答できます。コア開発者がたまたまトピックをハッシュ化しています。以前に自分でpython-devのちょっとした歴史についてGoogleに挑戦したことがあるので、この言語の初心者が行き止まりにぶつかる理由を理解し、より経験豊富なPythonの人が答えることを期待してここに質問することができます!
Brandon Rhodes

回答:


228

編集:

実際、mathに含まれているパッチがありましたが、すべてのエッジケース(+/- 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

2つの引数全体を渡すのにうんざりしている場合は、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

第2に、通常、何かのサインが必要な場合は、それを別の値で乗算するだけです。そしてもちろん、それが基本的に何をするかcopysignです。

したがって、代わりに:

s = sign(a)
b = b * s

あなたはただ行うことができます:

b = copysign(b, a)

そして、はい、あなたが7年間Pythonを使用していてcmp、簡単に削除して置き換えられると思いますsign__cmp__メソッドでクラスを実装したことはありませんか?cmpカスタムコンパレーター関数を呼び出して指定したことはありませんか?

要約すると、私signも関数が必要だcopysignと気づきましたが、最初の引数が1の場合は問題なく機能します。これは同じ機能のサブセットにすぎないことを示したので、これsignよりも便利だcopysignとは思いません。


35
Givesを使用し[int(copysign(1, zero)) for zero in (0, 0.0, -0.0)]ます[1, 1, -1]en.wikipedia.org/wiki/Sign_functionに[0, 0, 0]従っている必要があります
user238424

12
@Andrew-@ user238424の呼び出し順序は正しいです。copysign(a,b)bの符号付きのaを返します-bは変動入力、aはbの符号付きで正規化される値です。が、X = 0のために1を返すための符号(0)が0に評価であろう一方、この場合には、コメント投稿は、サイン(x)の代わりとしてそのcopysign(1、x)の失敗を示している
PaulMcG

7
フロートは「値」とは別に「符号」を保持します。-0.0は、実装エラーのように見えても、負の数です。単純に使用cmp()すると、おそらく誰もが気にするほぼすべてのケースで、望ましい結果が得られます:[cmp(zero, 0) for zero in (0, 0.0, -0.0, -4, 5)]==> [0, 0, 0, -1, 1]
pythonlarry 2013年

11
s = sign(a) b = b * sと同等ではありませんb = copysign(b, a)!bの符号は考慮されません。たとえばa=b=-1、最初のコードが1を返し、2番目のコードが-1を返す場合
Johannes Jendersie

14
誤ったsign()置換定義、sign(a)との乗算に相当する偽、copysignの動機に関する誤った説明、および正しい置換 "cmp(x、0)"が質問ですでに言及されているのを見てください。あまり情報がなく、なぜこれが非常に多くの票を集めた「受け入れられた」回答なのかは不明です。
kxr

59

「copysign」は、IEEE 754およびC99仕様の一部で定義されています。それがPythonにある理由です。NaN値の処理方法が原因で、abs(x)* sign(y)で関数を完全に実装することはできません。

>>> 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で使用できない特定の理由については、わかりません。私は仮定を立てることができますが、それは推測です。

数学モジュール自体は、xが負であるか負でないかをチェックする方法としてcopysign(1、x)を使用します。考慮すべきケースが1つ少ないため、1、0、または-1を返すsign(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()が3値の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番号にのみ適用されます。

複素数の場合、Pythonでsign(-3 + 4j)は何を返しますか?abs(-3 + 4j)は5.0を返します。これは、sign()が意味をなさない場所でabs()を使用する方法の明確な例です。

sign(x)がabs(x)を補完するものとしてPythonに追加されたとします。'x'が__abs __(self)メソッドを実装するユーザー定義クラスのインスタンスである場合、abs(x)はx .__ abs __()を呼び出します。正しく機能するために、abs(x)を同じ方法で処理するには、Pythonは符号(x)スロットを取得する必要があります。

これは、比較的不要な機能には過剰です。さらに、なぜsign(x)が存在し、nonnegative(x)とnonpositive(x)が存在しないのですか?Pythonの数学モジュール実装のスニペットは、copybit(x、y)を使用してnonnegative()を実装する方法を示しています。これは、単純なsign(x)では実行できません。

Pythonは、IEEE 754 / C99数学関数のサポートを改善する必要があります。これにより、signbit(x)関数が追加され、floatの場合に必要な機能が実行されます。整数や複素数、文字列がはるかに少ない場合は機能せず、探している名前がありません。

「なぜ」と尋ねると、答えは「sign(x)は役に立たない」です。あなたはそれが便利だと主張します。しかし、あなたのコメントはあなたがその主張をすることができるほど十分に知らないことを示しています、それはあなたがその必要性の説得力のある証拠を示さなければならないことを意味します。NumPyが実装していると言っても、十分に説得力があります。既存のコードがsign関数でどのように改善されるかを示す必要があります。

そして、それはStackOverflowの範囲外です。代わりに、Pythonリストの1つに移動してください。


5
まあ、それがあなたを幸せにするかどうかはわかりませcmp()んが、Python 3にはどちらもありませんsign():-)
Antoine P.

4
IEEE 754で適切に機能する適切なsign()関数を作成することは簡単ではありません。これは私が疑問にこの点を詳しく説明していなかったにもかかわらず、むしろそれを除外よりも、言語に含める良い点だろう
ダヴィデ

2
「ソートを安定させたい場合」についてのコメントは、安定したソートがどのように機能するかについても知らないことを意味します。コピーサインとサインは同等であるというあなたの声明は、あなたがこの投稿の前にIEEE 754数学についてあまり知らなかったことを示しています。Pythonは754の数学関数をすべてコアに実装する必要がありますか?C99以外のコンパイラでは何をすべきですか?754以外のプラットフォーム?「isnonnegative」と「isnonpositive」も便利な機能です。Pythonもそれらを含める必要がありますか?abs(x)はx .__ abs __()に従うため、sign(x)はx .__ sign __()に従う必要がありますか?需要も需要もほとんどないのに、なぜコアにスタックする必要があるのでしょうか。
Andrew Dalke、

2
math.copysign(1、float( "-nan"))は、2.7で試したときに-1.0ではなく1.0を返します
dansalmo

34

sign()のもう1つのライナー

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

x = 0に対して0を返したい場合:

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

1
どうして?質問自体は、cmp(x, 0)と同等でありsignlambda x: cmp(x, 0)あなたが提案したものよりも読みやすいことを認めています。
ToolmakerSteve

1
確かに、私は間違っていました。「cmp」が-1,0、+ 1を返すように指定されていると想定していましたが、仕様ではそれが保証されていないようです。
ToolmakerSteve

綺麗な。最初の質問に答えます:python intまたはfloat to -1、0、1?
scharfmn 2015年

1
代わりにリストを使用する利点はあります-1 if x < 0 else 1か?
Mateen Ulhaq

6
sign = lambda x: -1 if x < 0 else 115%高速化。と同じsign = lambda x: x and (-1 if x < 0 else 1)です。
Mateen Ulhaq 2018

26

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)

13

ウィキペディアの定義に準拠した正解のみ

ウィキペディア定義は次のとおりです。

サイン定義

したがって、

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)は正でも負でもないことに注意してください。


1
この回答は、簡潔でありながら強力なpythonがいかに優れているかを示しています。
NelsonGon

1
不可解:コードはWP定義を実装せず、中央の句を最後にデフォルトの句に置き換えます。これはnanのような非実数を処理するために必要ですが、WPステートメント( 'Hence')の直後にあると誤って表示されています。
ユルゲン・ストローベル

1
@JürgenStrobel私はあなたが何を言っているのか正確に知っており、私もこの問題を長い間考えてきました。ほとんどのユースケースで簡略化されたバージョンを維持しながら、正しい形式化のために答えを拡張しました。
Serge Stroobandt

10

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

通常のフロートを取得します。


10

これを実行してみてください。xは任意の数値です

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

bool()への型強制は、比較演算子がブール値を返さない可能性を処理します。


良い考えですが、私が意味すると思います:int_sign = int(x> 0)
-int

つまり、int_sign = lambda x:(x> 0)-(x <0)
yucer

1
@yucerいいえ、彼はブールへのキャスト(とにかくintのサブクラスです)を意味しました。説明へのリンクを与えた理論的な可能性があるからです。
Walter

この構成の唯一の欠点は、引数が2回表示されることです。これは、単一の変数である場合にのみ問題ありません
Walter Tross

5

はい、正しいsign()関数は少なくともmathモジュールにある必要があります-numpyにあるためです。数学指向のコードで頻繁に必要になるためです。

しかし、math.copysign()独立しても便利です。

cmp()obj.__cmp__()...独立して一般的に重要性が高い。数学指向のコードだけではありません。タプル、日付オブジェクトなどの比較/ソートを検討してください...

省略に関するhttp://bugs.python.org/issue1640のdev引数math.sign()は奇妙です。

  • 分離はありません -NaN
  • sign(nan) == nan 心配することなく(のようにexp(nan)
  • sign(-0.0) == sign(0.0) == 0 心配することなく
  • sign(-inf) == -1 心配することなく

-それは派手に


4

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()には使用できません。

この可能性に対処するには、比較結果をブール値に強制変換する必要があります。

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

これtypeは、完全に順序付けされたもの(NaNまたはのような特別な値を含む)に対して機能します。


0

あなたはそれを必要としません、あなたは単に使うことができます:

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

4
変数がオンになっている0のどちら側にあるかを確認するx / abs(x)ためにチェーンif/elseするよりも少し時間がかかることを指摘する必要があります。あるいは、ぬるぬるに満足return (x > 0) - (x < 0)してbool値を差し引いてint

1
Pythonの扱いTrueFalseなど10、あなたは絶対にこれを行うと、いずれかを取得することができ10または-1def sign(x): return (x > 0) - (x < 0)戻らないだろうbool、それは戻りますint-あなたが合格した場合0、あなたが買ってあげる0バック


-8

「記号」が含まれていない理由は、組み込み関数のリストにすべての有用な1行を含めると、Pythonを使用するのが簡単で実用的でなくなるためです。この関数を頻繁に使用する場合は、自分で分解してみませんか?それを行うのが遠くて難しい、あるいは退屈でさえあるようなものではありません。


6
さて、これabs()も取り残された場合のみ購入します。sign()abs()一緒に使用されることが多くsign()、2つのうち最も有用(IMO)であり、実装するのがリモートで困難または面倒なものはありません(エラーが発生しやすいにもかかわらず、この答えがどのように間違っているかを確認してください:stackoverflow.com/questions/1986152/…
Davide、

1
sign()それ自体の数値結果はほとんど役に立ちません。ほとんどの場合、変数が正であるか負であるかに基づいて異なるコードパスを使用します。その場合、条件を明示的に記述する方が読みやすくなります。
Antoine P.

3
abs()は、sign()よりも頻繁に使用されます。そして、私はあなたにnumPyトラッカーを紹介しました。sign(-3 + 4j)はどうあるべきですか?一方、abs(-3 + 4j)は5.0です。sign()とabs()が一緒に見られることが多いと断言します。C標準ライブラリには 'sign'関数がないので、どこでデータを取得していますか?
Andrew Dalke
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.