Python'un neden bir işaret işlevi yok?


241

Python'un neden bir signişlevi olmadığını anlayamıyorum . Bir absyerleşkesi var (ki signkız kardeşi olduğunu düşünüyorum ), ama hayır sign.

Python 2.6'da copysign( matematikte ) bir işlev bile var , ancak işaret yok. Neden yazmaya zahmet copysign(x,y)sen sadece bir yazabilirsiniz zaman signsonra olsun copysigndoğrudan abs(x) * sign(y)? İkincisi çok daha açık olacaktır: x y işareti ile, copysign ile x'in y işareti veya y işareti ile x olup olmadığını hatırlamanız gerekir!

Açıkçası sign(x)bundan başka bir şey sağlamaz cmp(x,0), ancak bunun da çok daha okunabilir olması gerekir (ve python gibi büyük ölçüde okunabilir bir dil için bu büyük bir artı olurdu).

Eğer bir python tasarımcısı olsaydım, başka bir yol olurdu: cmpyerleşik değil, ama sign. İhtiyacınız olduğunda cmp(x,y), sadece bir sign(x-y)(veya sayısal olmayan şeyler için daha iyi, sadece x> y - elbette sortedbunun bir tamsayı karşılaştırıcısı yerine bir boole kabul etmesini gerektiriyordu ). Bu aynı zamanda daha açık olacaktır: pozitif olduğunda x>y(oysa ile cmpne zaman kongre Pozitif hatırlamak zorunda ilk olan büyük , ancak tersi olabilir). Tabii ki cmpbaşka nedenlerle kendi başına mantıklıdır (örneğin, sayısal olmayan şeyleri sıralarken veya sıralamanın istikrarlı olmasını istiyorsanız, bu sadece bir boole ile kullanılması mümkün değildir)

Peki, soru şu: Python tasarımcıları signişlevi neden dil dışında bırakmaya karar verdiler ? Heck neden copysignebeveyni ile uğraşmıyor sign?

Bir şey mi kaçırıyorum?

EDIT - Peter Hansen yorum yaptıktan sonra. Kullanmamanız için yeterince adil, ama python için ne kullandığınızı söylemediniz. Python kullandığım 7 yıl içinde, sayısız kez ihtiyacım vardı ve son olarak devenin sırtını kıran saman!

Evet, cmp'yi etrafta geçirebilirsiniz, ancak geçmek için ihtiyaç duyduğum zamanların% 90'ı, böyle lambda x,y: cmp(score(x),score(y))iyi bir işaretle çalışacak gibi bir deyimdeydi .

Son olarak, umarım signbunun bundan daha yararlı olacağını kabul edersiniz copysign, bu yüzden görüşünüzü alsam bile, neden bunu işaret yerine matematikte tanımlamakla uğraşasınız? Copysign, işaretten çok nasıl yararlı olabilir?


33
@dmazzoni: Bu argüman bu sitedeki tüm sorular için işe yaramaz mı? stackoverflow'u kapatın ve her konuyu ilgili konu geliştiricisine veya kullanıcı posta listesine sorun!
Davide

43
Bir soru için uygun yer, cevaplanması muhtemel herhangi bir yerdir. Böylece yığın akışı uygun bir yerdir.
Stefano Borini

23
-1: @Davide: "Neden" ve "neden olmasın" soruları genellikle burada cevaplanamaz. Python geliştirme prensiplerinin çoğu burada soruları cevaplamadığından, nadiren (eğer varsa) bir "neden" veya "neden olmasın" sorusuna cevap alacaksınız. Ayrıca, çözmek için bir sorununuz yok. Bir rantın varmış gibi geliyor. Eğer bir sorununuz varsa ("Bu örnekte işaret eksikliğinin üstesinden nasıl gelebilirim ...") bu mantıklı. "Neden olmasın" bu mekan için mantıklı değil.
S.Lott

31
Soru biraz duygusal olabilir, ama bunun kötü bir soru olduğunu düşünmüyorum. Eminim birçok insan yerleşik bir işaret fonksiyonu aramıştır, bu yüzden neden bir tane olmadığını merak edebilir.
FogleBird

17
Bu tamamen objektif bir soru: “Neden” Python, verilen herhangi bir özelliğin, Python'un veya diğer forumlardan (bazen blog gönderileri) Python'un uygun tartışmasına bağlanarak yanıtlanabilecek dil tasarım tarihi hakkında meşru bir sorgu olmasıdır. çekirdek geliştiriciler bir konu ortaya çıkarmak için olur. Google'a daha önce kendimi python-dev'deki tarih parçaları için denedikten sonra, dile yeni gelen bir kişinin neden çıkmaza girebileceğini ve buraya daha deneyimli bir Python kullanıcısının cevap vermesini umarak gelmesini anlayabiliyorum!
Brandon Rhodes

Yanıtlar:


228

DÜZENLE:

Gerçekten de dahil edilen bir yama vardısign() matematik , ancak, kabul edilmedi onlar üzerinde anlaşmaya varamadığı için tüm kenar durumlarda dönmelidir neyi (+/- 0, +/- nan, vs)

Onlar uygulamaya karar Yani sadece edilebilen (daha ayrıntılı rağmen), copysign son kullanıcıya uç örnekleri için istenen davranışı devretme kullanılan - hangi bazen çağrısına gerektirebilircmp(x,0) .


Bunun neden yerleşik olmadığını bilmiyorum, ama bazı düşüncelerim var.

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

En önemlisi, copysignbir süper set sign! copysignX = 1 ile çağrı yapmak bir signfonksiyon ile aynıdır . Sadece kullanabilirsiniz Yani copysignve unutabilirsiniz .

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

Eğer iki argümanı geçmekten bıkarsanız, sign bu şekilde ve yine de başkaları tarafından belirtilen IEEE şeyleriyle uyumlu olacaktır:

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

İkincisi, genellikle bir şeyin işaretini istediğinizde, onu başka bir değerle çarpmanız yeterlidir. Ve tabii ki temelde copysignbu.

Yani, yerine:

s = sign(a)
b = b * s

Sadece şunları yapabilirsiniz:

b = copysign(b, a)

Ve evet, 7 yıldır Python kullandığınıza şaşırıyorum ve cmpbu kadar kolay çıkarılabilir ve yerini alabilir sign! Daha önce hiç__cmp__ yöntemle ? Hiç cmpözel karşılaştırıcı işlevi aramadınız ve belirtmediniz mi?

Özetle, kendimi signde bir işlev isterken buldum , ancak copysignilk argüman 1 ile gayet iyi çalışacak. Ben sadece aynı işlevselliğin bir alt kümesi olduğunu gösterdiğim gibi, signdaha yararlı olacağını kabul copysignetmiyorum.


35
Kullanarak [int(copysign(1, zero)) for zero in (0, 0.0, -0.0)]verir [1, 1, -1]. Bu en.wikipedia.org/wiki/Sign_function[0, 0, 0]
user238424

12
@Andrew - @ user238424 adlı kişinin çağrı sırası doğru. copysign(a,b)b işareti ile bir a döndürür - b değişen girdidir, a b işareti ile normalleştirilecek değerdir. Bu durumda, yorumcu, x (0) yerine 1 döndürdüğü için (x) işareti 0 olarak değerlendirileceği için (x) işareti (x) yerine bir copysign (1, x) işleminin başarısız olduğunu gösterir.
PaulMcG

7
Şamandıralar "işaret" i "değer" den ayrı tutar; -0.0, bir uygulama hatası gibi görünse bile, negatif bir sayıdır. Sadece kullanmak cmp()istediğiniz sonuçları verecektir, muhtemelen herkesin umrunda olduğu her durum için: [cmp(zero, 0) for zero in (0, 0.0, -0.0, -4, 5)]==> [0, 0, 0, -1, 1].
pythonlarry

11
s = sign(a) b = b * seşdeğer değildir b = copysign(b, a)! B işaretini dikkate almaz. Örneğin a=b=-1, birinci kod 1, ikinci kod -1 döndürürse
Johannes Jendersie

14
Yanlış işaret () değiştirme tanımını, işaret (a) ile çarpma için yanlış eşdeğeri, copysign'ın motivasyonu için yanlış açıklama ve soruda daha önce bahsedilen doğru değiştirme "cmp (x, 0)" ifadesini görme - var fazla bilgi yok ve bu neden bu kadar çok oy ile "kabul" cevabı olduğu açık değildir.?
kxr

59

"copysign" IEEE 754 ve C99 belirtiminin bir parçası tarafından tanımlanır. Bu yüzden Python'da. NaN değerlerini nasıl ele alması gerektiği için işlev abs (x) * işareti (y) ile tam olarak uygulanamaz.

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

Bu copysign () işlevini sign () işlevinden daha kullanışlı hale getirir.

IEEE'nin işaretinin (x) standart Python'da bulunmamasının belirli nedenlerine gelince, bilmiyorum. Varsayımlar yapabilirim, ama tahmin ediyorum.

Math modülünün kendisi x'in negatif olup olmadığını kontrol etmek için copysign (1, x) kullanır. Matematiksel işlevlerle uğraşan çoğu durumda, 1, 0 veya -1 döndüren bir işarete (x) sahip olmaktan daha yararlı görünüyor çünkü dikkate alınması gereken bir tane daha az durum var. Örneğin, Python'un matematik modülünden aşağıdakiler:

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

Burada copysign () işlevinin üç değerli sign () işlevinden daha etkili bir işlev olduğunu açıkça görebilirsiniz.

Sen yazdın:

Eğer bir python tasarımcısı olsaydım, tam tersi olurdu: hiçbir cmp () yerleşik değil, bir işaret ()

Bu, cmp () öğelerinin sayıların yanı sıra şeyler için kullanıldığını bilmediğiniz anlamına gelir. cmp ("This", "That") bir sign () işleviyle uygulanamaz.

Başka cevaplarımı başka bir yerde harmanlamak için düzenleyin :

Gerekçelerinizi abs () ve sign () öğelerinin birlikte nasıl görüldüğüne dayandırırsınız. C standart kitaplığı herhangi bir türde 'sign (x)' fonksiyonu içermediğinden, görüşlerinizi nasıl haklı çıkardığınızı bilmiyorum. Bir abs (int) ve fabs (çift) ve fabsf (şamandıra) ve fabsl (uzun) ama hiçbir belirtisi yok. "Copysign ()" ve "signbit ()" vardır ancak bunlar yalnızca IEEE 754 numaraları için geçerlidir.

Karmaşık sayılarla, Python'da işaret (-3 + 4j) dönüşü uygulanacaktı, uygulanacak mıydı? abs (-3 + 4j) dönüş 5.0. Bu, abs () yönteminin sign () yönteminin mantıklı olmadığı yerlerde nasıl kullanılabileceğinin açık bir örneğidir.

Abs (x) 'nin bir tamamlayıcısı olarak Python'a işaret (x) eklendiğini varsayalım. 'X' __abs __ (self) yöntemini uygulayan kullanıcı tanımlı bir sınıf örneğiyse abs (x) x .__ abs __ () öğesini çağırır. Doğru şekilde çalışmak için abs (x) 'yi aynı şekilde işlemek için Python'un bir işaret kazanması gerekir (x) yuvası .

Bu, nispeten gereksiz bir işlev için aşırıdır. Ayrıca, neden (x) işareti var ve negatif (x) ve pozitif (x) yok? Python'un matematik modülü uygulamasındaki snippet'im, copybit (x, y) 'nin basit bir işaretin (x) yapamadığı negatif olmayan () uygulamak için nasıl kullanılabileceğini gösterir.

Python, IEEE 754 / C99 matematik işlevi için daha iyi desteğe sahip olmalıdır. Bu, yüzer durumda istediğinizi yapacak bir işaret (x) işlevi ekleyecektir. Tamsayılar veya karmaşık sayılar, çok daha az dize için işe yaramaz ve aradığınız isme sahip olmaz.

"Neden" diye soruyorsunuz ve yanıt "işareti (x) kullanışlı değil" dir. Yararlı olduğunu iddia edersiniz. Yine de yorumlarınız bu iddiayı yapabilmek için yeterince bilmediğinizi gösteriyor, bu da onun ihtiyacına dair ikna edici kanıtlar göstermeniz gerektiği anlamına geliyor. NumPy'nin bunu uyguladığını söylemek yeterince ikna edici değil. Bir işaret fonksiyonuyla mevcut kodun nasıl geliştirileceğini gösteren vakaları göstermeniz gerekir.

Ve StackOverflow'un kapsamı dışında olduğunu. Bunun yerine Python listelerinden birine götürün.


5
Peki, bu seni mutlu edecekse bilmiyorum, ama Python 3'ün ne cmp()de yok sign():-)
Antoine P.

4
IEEE 754 ile düzgün çalışacak iyi bir sign () işlevi yazmak önemsiz değildir. Sorunda bu noktayı ayrıntılandırmasam da, onu dışarıda bırakmak yerine dile eklemek iyi bir nokta olurdu
Davide

2
"Sıralamanın kararlı olmasını istiyorsanız" hakkındaki yorumunuz, sıralamanın nasıl çalıştığını da bilmediğiniz anlamına gelir. Copysign and sign ifadesine eşdeğer olan ifadeniz, bu yazıdan önce IEEE 754 matematiği hakkında fazla bir şey bilmediğinizi gösteriyor. Python, 754 matematik fonksiyonunun tümünü çekirdek olarak uygulamalı mı? C99 olmayan derleyiciler için ne yapmalı? 754 olmayan platformlar? "isnonnegative" ve "isnonpositive" de yararlı işlevlerdir. Python da bunları içermeli mi? abs (x) x .__ abs __ () 'i etkiliyor, yani (x) işareti x .__ sign __ ()' e ertelenmeli mi? Buna çok az talep ya da ihtiyaç var, o zaman neden çekirdeğe sıkışmalı?
Andrew Dalke

2
math.copysign (1, float ("- nan")) 2.7 'de denediğimde -1.0 yerine 1.0 döndürür
dansalmo

34

Sign için başka bir astar ()

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

X = 0 için 0 döndürmesini istiyorsanız:

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

1
neden? Sorunun kendisi , önerdiğinize cmp(x, 0)eşdeğer signve lambda x: cmp(x, 0)daha okunabilir olduğunu kabul eder .
ToolmakerSteve

1
Gerçekten yanılmışım. Ben 'cmp' -1,0, + 1 dönmek için belirtildiğini varsaymıştı, ama spec bunu garanti olmadığını görüyorum.
ToolmakerSteve

Güzel. Başlatılan soruyu cevaplar: python int veya float -1, 0, 1?
scharfmn

1
Bunun yerine listeleri kullanmanın bir avantajı var mı -1 if x < 0 else 1?
Mateen Ulhaq

6
sign = lambda x: -1 if x < 0 else 1olduğu % 15 daha hızlı . İle aynı sign = lambda x: x and (-1 if x < 0 else 1).
Mateen Ulhaq

26

Kaldırıldığından beri cmp, aynı işlevselliği

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

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

Hatta float, inthatta işe yarıyor Fraction. Bu durumudafloat uyarı sign(float("nan"))sıfırdır.

Python, karşılaştırmaların bir boole döndürmesini gerektirmez ve bu nedenle karşılaştırmaları bool () öğesine zorlamak, izin verilen, ancak yaygın olmayan uygulamalara karşı koruma sağlar:

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

13

Sadece Wikipedia tanımına uygun doğru cevap

Vikipedi'de tanımı şöyledir:

işareti tanımı

Bu nedenle,

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

Hangi tüm amaç ve amaçlar için basitleştirilebilir:

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

Bu fonksiyon tanımı hızlı çalışır ve 0, 0.0, -0.0, -4 ve 5 için garantili doğru sonuçlar verir (diğer yanlış cevapların yorumlarına bakın).

Not bu sıfır (0) ne pozitif ne de negatif .


1
Bu cevap, özlü ama güçlü python'un nasıl olabileceğini göstermektedir.
NelsonGon

1
Quibble: Kod WP tanımını uygulamaz, ortadaki maddeyi sonunda varsayılan bir maddeyle değiştirir. Bu, nan gibi gerçek olmayan sayıları işlemek için gerekli olsa da, yanlış bir şekilde doğrudan WP ifadesini ('Hence') takip ettiği şeklinde gösterilmektedir.
Jürgen Strobel

1
@ JürgenStrobel Ne demek istediğinizi tam olarak biliyorum ve bu konuyu uzun zamandır düşünüyorum. Şimdi çoğu biçim için basitleştirilmiş versiyonu korurken yanıtı doğru formalizm için genişlettim.
Serge Stroobandt

10

numpy bir işaret fonksiyonuna sahiptir ve size diğer fonksiyonların bonusunu verir. Yani:

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

Sonucun bir numpy olduğuna dikkat edin.

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

Json gibi şeyler için, bu önemlidir, çünkü json numpy.float64 türlerini nasıl serileştireceğini bilmiyor. Bu durumda şunları yapabilirsiniz:

float(np.sign(y))

düzenli bir şamandıra almak için.


10

X'in herhangi bir sayı olduğu yerde çalıştırmayı deneyin

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

Bool () öğesine uygulanan baskı , karşılaştırma operatörünün bir boolean döndürmemesi olasılığını işler .


İyi fikir, ama demek istediğim: int_sign = int (x> 0) - int (x <0)
yucer

Yani: int_sign = lambda x: (x> 0) - (x <0)
yaş

1
@yucer no, açıklamaya bağlantıyı verdiği teorik olasılığı nedeniyle gerçekten oyuncu kadrosunu boole (zaten int'in bir alt sınıfıdır) demekti.
Walter Tross

Bu yapının tek dezavantajı, argümanın iki kez ortaya çıkmasıdır, bu sadece tek bir değişkense iyi
Walter Tross

5

Evet doğru bir sign()fonksiyon en azından matematik modülünde olmalıdır - numpy olduğu gibi. Çünkü matematik yönelimli kod için sık sık buna ihtiyaç vardır.

Ancak math.copysign()bağımsız olarak da yararlıdır.

cmp()ve obj.__cmp__()... bağımsız olarak genellikle yüksek öneme sahiptir. Sadece matematik odaklı kod için değil. Tüpleri, tarih nesnelerini, karşılaştırmayı / sıralamayı düşünün ...

Http://bugs.python.org/issue1640 adresindeki ihmaller konusundaki dev argümanlar math.sign()tuhaf, çünkü:

  • Ayrı yok -NaN
  • sign(nan) == nan endişe etmeden (gibi exp(nan))
  • sign(-0.0) == sign(0.0) == 0 endişesiz
  • sign(-inf) == -1 endişesiz

- numpy olduğu gibi


4

Python 2'de cmp()bir tamsayı döndürür: sonucun -1, 0 veya 1 sign(x)olması gerekmez , bu nedenle aynı değildircmp(x,0) .

Python 3'te cmp()zengin karşılaştırma lehine kaldırıldı. İçin cmp()Python 3, bu da anlaşılacağı :

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

Bu da cmp () için uygundur, ancak karşılaştırma işleçlerinin booleans döndürmesi gerekmediğinden tekrar sign () için kullanılamaz .

Bu olasılıkla başa çıkmak için karşılaştırma sonuçları boolean'lara zorlanmalıdır:

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

Bu type, tamamen sipariş edilenler için geçerlidir ( NaNsonsuz değerler veya özel değerler dahil ).


0

Birine ihtiyacınız yok, sadece kullanabilirsiniz:

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

4
O işaret ayılar x / abs(x)sadece zincirleme biraz daha uzun sürer if/elsedeğişken üzerinde, ya da kullanan bu konuda sümüksü henüz adı tatmin 0 hangi tarafına kontrol etmek return (x > 0) - (x < 0)çıkarmak booldeğerlere ve bir dönüşint

1
Python davranır Trueve Falseyanı 1ve 0, kesinlikle bunu yapmak ve ya alabilirsiniz 1, 0ya da -1. def sign(x): return (x > 0) - (x < 0)Bir geri dönmeyecek bool, bir dönersiniz int- geçtiğiniz takdirde 0elde edeceğiniz 0geri

0

Sadece öyle değil.

Bunu düzeltmenin en iyi yolu:

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

-8

"İşaret" in nedeni, yerleşik işlevler listesine her kullanışlı tek astarı dahil etsek, Python'un artık çalışmak için kolay ve pratik olmayacağıdır. Eğer bu fonksiyonu bu kadar sık ​​kullanırsanız, neden bunu kendiniz hesaba katmazsınız? Uzaktan zor ve hatta sıkıcı gibi değil.


6
Bunu sadece abs()dışarıda bırakılırsa satın alırım . sign()ve abs()genellikle birlikte kullanılırlar sign(), ikisi (IMO) için en faydalı olanıdır ve hiçbiri uygulamak zor veya sıkıcı değildir (hataya eğilimli olmasına rağmen, bu cevabın nasıl yanlış yaptığını görün: stackoverflow.com/questions/1986152/… )
Davide

1
Mesele şu ki, sign()kendisinin sayısal sonucu nadiren yararlıdır. Çoğu zaman yaptığınız, bir değişkenin pozitif veya negatif olup olmadığına bağlı olarak farklı kod yolları kullanmaktır ve bu durumda koşulu açıkça yazmak daha kolay okunabilir.
Antoine P.

3
abs (), bir işaretin () olacağından çok daha sık kullanılır. Ve sizi () işaretinin ne kadar zor olabileceğini gösteren NumPy izleyicisine yönlendirdim. (-3 + 4j) işareti ne olmalı? Abs (-3 + 4j) 5,0 iken. Sign () ve abs () öğelerinin sıklıkla birlikte görüldüğünü iddia edersiniz. C standart kitaplığında 'işaret' işlevi yoktur, bu nedenle verilerinizi nereden alıyorsunuz?
Andrew Dalke
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.