Nie rozumiem, dlaczego Python nie ma sign
funkcji. Ma abs
wbudowaną (którą uważam za sign
siostrę), ale niesign
.
W Pythonie 2.6 jest nawet copysign
funkcja (w matematyce ), ale nie ma znaku. Po co męczyć się, aby napisać, copysign(x,y)
kiedy możesz po prostu napisać a, sign
a następnie uzyskać copysign
bezpośrednio odabs(x) * sign(y)
? To drugie byłoby znacznie wyraźniejsze: x ze znakiem y, podczas gdy przy copysign musisz pamiętać, czy to x ze znakiem y lub y ze znakiem x!
Oczywiście sign(x)
nie zapewnia nic więcej niżcmp(x,0)
, ale byłoby to o wiele bardziej czytelne (i dla bardzo czytelnego języka, takiego jak python, byłby to duży plus).
Gdybym był projektantem Pythona, byłbym jeszcze inny: brak cmp
wbudowanego, ale sign
. Gdy potrzebujesz cmp(x,y)
, możesz po prostu zrobić sign(x-y)
(lub nawet lepiej dla liczb nienumerycznych, tylko x> y - oczywiście powinno to wymagać sorted
zaakceptowania wartości logicznej zamiast komparatora liczb całkowitych). Byłoby to również bardziej jasne: pozytywne, gdy x>y
(podczas gdy cmp
musisz pamiętać, że konwencja jest pozytywna, gdy pierwszy jest większy , ale może być odwrotnie). Oczywiście cmp
ma to sens z innych powodów (np. Podczas sortowania rzeczy nienumerycznych lub jeśli chcesz, aby sort był stabilny, co nie jest możliwe przy użyciu zwykłej wartości logicznej)
Pytanie brzmi: dlaczego projektanci Pythona postanowili zrezygnować z sign
funkcji poza językiem? Dlaczego, u licha, niepokoił się, copysign
a nie jego rodzic sign
?
Czy coś brakuje?
EDYCJA - po komentarzu Petera Hansena. W porządku, że go nie używałeś, ale nie powiedziałeś, do czego używasz Pythona. W ciągu 7 lat używania Pythona potrzebowałem go niezliczoną ilość razy, a ostatnia to słoma, która złamała grzbiet wielbłąda!
Tak, możesz przekazać cmp, ale 90% razy, które musiałem przekazać, było w takim idiomie
lambda x,y: cmp(score(x),score(y))
, że działałoby to ze znakiem w porządku.
Wreszcie, mam nadzieję, że zgadzasz się, że sign
byłoby to bardziej przydatne niż copysign
, więc nawet jeśli kupiłem twój pogląd, po co zawracać sobie głowę definiowaniem go w matematyce zamiast znaku? W jaki sposób copysign może być tak użyteczny niż znak?
źródło
Odpowiedzi:
EDYTOWAĆ:
Rzeczywiście była łatka, która obejmowała
sign()
w matematyce , ale nie została zaakceptowana, ponieważ nie zgodzili się, co powinna zwrócić we wszystkich przypadkach na krawędzi (+/- 0, +/- nan itp.)Postanowili więc wdrożyć tylko copysign, który (choć bardziej szczegółowy) może zostać użyty do przekazania użytkownikowi końcowemu pożądanego zachowania w przypadkach brzegowych - które czasami mogą wymagać wywołania
cmp(x,0)
.Nie wiem, dlaczego to nie jest wbudowane, ale mam pewne przemyślenia.
Co najważniejsze,
copysign
jest nadzbioremsign
! Wywołaniecopysign
przy x = 1 jest takie samo jaksign
funkcja. Więc może po prostu użyćcopysign
i zapomnieć o nim .Jeśli masz dość przekazywania dwóch całych argumentów, możesz je wdrożyć
sign
ten sposób i nadal będzie on zgodny z materiałami IEEE wspomnianymi przez innych:Po drugie, zwykle, gdy chcesz znak czegoś, po prostu pomnóż to przez inną wartość. I oczywiście to w zasadzie to, co
copysign
robi.Zamiast więc:
Możesz po prostu zrobić:
I tak, jestem zaskoczony, że używasz Pythona od 7 lat i myślę, że
cmp
można go tak łatwo usunąć i zastąpićsign
! Czy nigdy nie wdrożyłeś klasy z__cmp__
metody? Czy nigdy nie zadzwoniłeścmp
i nie określiłeś niestandardowej funkcji porównawczej?Podsumowując, odkryłem, że chcę też
sign
funkcji, alecopysign
z pierwszym argumentem 1 będzie działać dobrze. Nie zgadzam się, żesign
byłoby to bardziej przydatne niżcopysign
, ponieważ pokazałem, że jest to tylko podzbiór tej samej funkcjonalności.źródło
[int(copysign(1, zero)) for zero in (0, 0.0, -0.0)]
daje[1, 1, -1]
. To powinno być[0, 0, 0]
zgodne z en.wikipedia.org/wiki/Sign_functioncopysign(a,b)
zwraca a ze znakiem b - b jest zmiennym wejściem, a jest wartością normalizowaną do znaku b. W tym przypadku komentator pokazuje, że copysign (1, x) jako zamiennik znaku (x) kończy się niepowodzeniem, ponieważ zwraca 1 dla x = 0, podczas gdy znak (0) miałby wartość 0.cmp()
da pożądane rezultaty, prawdopodobnie w prawie każdym przypadku, na którym wszyscy by się przejmowali:[cmp(zero, 0) for zero in (0, 0.0, -0.0, -4, 5)]
==>[0, 0, 0, -1, 1]
.s = sign(a) b = b * s
nie jest równoważne zb = copysign(b, a)
! Nie uwzględnia znaku b. Np. Jeślia=b=-1
pierwszy kod zwróci 1, a drugi -1„copysign” jest zdefiniowany przez IEEE 754 i stanowi część specyfikacji C99. Właśnie dlatego jest w Pythonie. Funkcji nie można w pełni zaimplementować znakiem abs (x) * (y), ponieważ ma ona obsługiwać wartości NaN.
To sprawia, że copysign () jest bardziej przydatną funkcją niż sign ().
Jeśli chodzi o konkretne powody, dla których znak IEEE (x) nie jest dostępny w standardowym języku Python, nie wiem. Potrafię poczynić założenia, ale byłoby to zgadywanie.
Sam moduł matematyczny używa copysign (1, x) jako sposobu sprawdzenia, czy x jest ujemne czy nieujemne. W większości przypadków zajmujących się funkcjami matematycznymi, które wydają się bardziej przydatne niż posiadanie znaku (x), który zwraca 1, 0 lub -1, ponieważ jest jeden mniej przypadku do rozważenia. Na przykład następujące informacje pochodzą z modułu matematycznego Pythona:
Widać tam wyraźnie, że copysign () jest bardziej efektywną funkcją niż trzywartościowa funkcja sign ().
Napisałeś:
Oznacza to, że nie wiesz, że cmp () jest używany do rzeczy oprócz liczb. cmp („This”, „That”) nie można zaimplementować za pomocą funkcji sign ().
Edytuj, aby zestawić moje dodatkowe odpowiedzi w innym miejscu :
Opierasz swoje uzasadnienia na tym, jak często abs () i sign () są często postrzegane razem. Ponieważ biblioteka standardowa C nie zawiera żadnej funkcji „znak (x)”, nie wiem, jak uzasadniasz swoje poglądy. Jest abs (int) i fabs (double) i fabsf (float) i fabsl (long), ale nie ma wzmianki o znaku. Istnieją „copysign ()” i „signbit ()”, ale dotyczą one tylko numerów IEEE 754.
W przypadku liczb zespolonych, co by znak (-3 + 4j) zwrócił w Pythonie, gdyby miał zostać zaimplementowany? abs (-3 + 4j) zwraca 5.0. To wyraźny przykład użycia abs () w miejscach, w których sign () nie ma sensu.
Załóżmy, że do Pythona dodano znak (x) jako uzupełnienie abs (x). Jeśli „x” jest instancją klasy zdefiniowanej przez użytkownika, która implementuje metodę __abs __ (self), wówczas abs (x) wywoła x .__ abs __ (). Aby działać poprawnie, aby obsługiwać abs (x) w ten sam sposób, Python będzie musiał uzyskać znak szczelinę (x).
Jest to nadmierne jak na względnie niepotrzebną funkcję. Poza tym, dlaczego znak (x) powinien istnieć, a nieujemna (x) i niepodatnicza (x) nie istnieć? Mój fragment kodu z implementacji modułu matematycznego Pythona pokazuje, w jaki sposób copybit (x, y) może być użyty do implementacji wartości nieujemnej (), czego nie potrafi prosty znak (x).
Python powinien obsługiwać lepszą obsługę funkcji matematycznych IEEE 754 / C99. Dodałoby to funkcję signbit (x), która zrobiłaby co chcesz w przypadku float. Nie działałoby to dla liczb całkowitych lub liczb zespolonych, a tym bardziej dla łańcuchów i nie miałoby nazwy, której szukasz.
Pytasz „dlaczego”, a odpowiedź brzmi „znak (x) nie jest użyteczny”. Zapewniasz, że jest to przydatne. Jednak twoje komentarze pokazują, że nie wiesz wystarczająco dużo, aby móc twierdzić, co oznacza, że musiałbyś wykazać przekonujące dowody na jego potrzebę. Mówienie, że NumPy to implementuje, nie jest wystarczająco przekonujące. Musisz pokazać przypadki, w których istniejący kod zostałby ulepszony za pomocą funkcji znaku.
I że wykracza to poza StackOverflow. Zamiast tego przenieś go na jedną z list w języku Python.
źródło
cmp()
anisign()
:-)Kolejna linijka dla sign ()
Jeśli chcesz, aby zwracał 0 dla x = 0:
źródło
cmp(x, 0)
jest ono równoważnesign
ilambda x: cmp(x, 0)
jest bardziej czytelne niż sugerujesz.-1 if x < 0 else 1
?sign = lambda x: -1 if x < 0 else 1
jest 15% szybszy . To samo zsign = lambda x: x and (-1 if x < 0 else 1)
.Ponieważ
cmp
został usunięty , możesz uzyskać tę samą funkcjonalność dziękiDziała
float
,int
a nawetFraction
. W przypadkufloat
zawiadomieniasign(float("nan"))
zero.Python nie wymaga, aby porównania zwracały wartość logiczną, a więc wymuszanie porównań na bool () chroni przed dopuszczalną, ale rzadką implementacją:
źródło
Tylko poprawna odpowiedź zgodna z definicją Wikipedii
Definicja Wikipedia czytamy:
W związku z tym,
Który dla wszystkich celów i celów można uprościć:
Ta definicja funkcji działa szybko i daje wynik zapewnia prawidłowe wyniki dla 0, 0,0, -0,0, -4 i 5 (patrz komentarze do innych niepoprawnych odpowiedzi).
Zauważ, że zero (0) nie jest ani dodatnie, ani ujemne .
źródło
numpy ma funkcję znaku i daje ci również bonus innych funkcji. Więc:
Uważaj tylko, aby wynik był liczbą numpy.float64:
W przypadku rzeczy takich jak json ma to znaczenie, ponieważ json nie wie, jak serializować typy numpy.float64. W takim przypadku możesz wykonać:
aby uzyskać zwykły pływak.
źródło
Spróbuj uruchomić to, gdzie x jest dowolną liczbą
Koercja na bool () obsługuje możliwość, że operator porównania nie zwraca wartości logicznej.
źródło
Tak, poprawna
sign()
funkcja powinna znajdować się przynajmniej w module matematycznym - tak jak w numpy. Ponieważ często jest potrzebny do kodu zorientowanego na matematykę.Ale
math.copysign()
jest także przydatny niezależnie.cmp()
iobj.__cmp__()
... mają ogólnie duże znaczenie niezależnie. Nie tylko dla kodu zorientowanego na matematykę. Rozważ porównanie / sortowanie krotek, obiektów daty, ...Argumenty deweloperów na http://bugs.python.org/issue1640 dotyczące pominięcia
math.sign()
są dziwne, ponieważ:-NaN
sign(nan) == nan
bez obaw (jakexp(nan)
)sign(-0.0) == sign(0.0) == 0
bez obawsign(-inf) == -1
bez obaw- jak to jest w numpy
źródło
W Pythonie 2
cmp()
zwraca liczbę całkowitą: nie ma wymogu, aby wynik wynosił -1, 0 lub 1, więcsign(x)
nie jest taki sam jakcmp(x,0)
.W Pythonie 3
cmp()
został usunięty na rzecz bogatego porównania. Dlacmp()
Python 3 sugeruje to :co jest w porządku dla cmp (), ale znowu nie może być użyte do sign (), ponieważ operatory porównania nie muszą zwracać boolanów .
Aby poradzić sobie z tą możliwością, wyniki porównania muszą zostać wymuszone na logiczne:
Działa to dla wszystkich,
type
które są całkowicie uporządkowane (w tym specjalne wartości, takie jakNaN
lub nieskończoności).źródło
Nie potrzebujesz jednego, możesz po prostu użyć:
źródło
x / abs(x)
zajmuje to nieco więcej czasu niż tylko łańcuchoweif/else
sprawdzenie, po której stronie 0 jest zmienna, lub w tym przypadku za pomocą obleśnego, ale satysfakcjonującego,return (x > 0) - (x < 0)
aby odjąćbool
wartości i zwrócićint
True
iFalse
jak1
i0
można absolutnie to zrobić i dostać albo1
,0
albo-1
.def sign(x): return (x > 0) - (x < 0)
nie zwróci abool
, zwróci iint
- jeśli zdasz0
,0
wróciszPo prostu nie.
Najlepszym sposobem na rozwiązanie tego jest:
źródło
Powodem, dla którego „znak” nie jest uwzględniony, jest to, że gdybyśmy umieścili każdy przydatny jednowierszowy na liście wbudowanych funkcji, Python nie byłby łatwy i praktyczny do pracy z nim. Jeśli używasz tej funkcji tak często, dlaczego jej nie rozliczyć? Nie jest to wcale trudne, ani nawet żmudne.
źródło
abs()
zostało pominięte.sign()
iabs()
są często używane razem,sign()
jest najbardziej przydatny z nich (IMO) i żaden z nich nie jest zbyt trudny ani żmudny do wdrożenia (nawet jeśli jest podatny na błędy, zobacz, jak ta odpowiedź jest błędna: stackoverflow.com/questions/1986152/... )sign()
rzadko jest użyteczny. Najczęściej zajmujesz się różnymi ścieżkami kodu w zależności od tego, czy zmienna jest dodatnia czy ujemna, i w takim przypadku bardziej czytelne jest wyraźne zapisanie warunku.