Waarom heeft Python geen tekenfunctie?

Ik begrijp niet waarom Python geen functie signheeft. Het heeft een absingebouwd (die ik beschouw als de zus van sign), maar geen sign.

In python 2.6 is er zelfs een functie copysign(in wiskunde), maar geen teken. Waarom zou je de moeite nemen om een copysign(x,y)te schrijven als je ook gewoon een signkunt schrijven en dan het copysignrechtstreeks van abs(x) * sign(y)? Dat laatste zou veel duidelijker zijn: x met het teken van y, terwijl je bij copysign moet onthouden of het x is met het teken van y of y met het teken van x!

Uiteraard biedt sign(x)niet meer dan cmp(x,0), maar het zou veel leesbaarder zijn dat dit ook (en voor een leesbare taal zoals python, dit zou een groot pluspunt zijn geweest).

Als ik een python-ontwerper was, zou ik de andere kant op zijn gegaan: geen cmpingebouwd, maar een sign. Als je cmp(x,y)nodig hebt, kun je gewoon een sign(x-y)doen (of, nog beter voor niet-numerieke dingen, gewoon een x>y – dit had natuurlijk moeten vereisen dat sortedeen boolean accepteerde in plaats van een integer comparator). Dit zou ook duidelijker zijn: positief wanneer x>y(terwijl u bij cmpde conventie positief moet onthouden wanneer de eerstegroter, maar het kan ook andersom zijn). Natuurlijk is cmpop zichzelf logisch om andere redenen (bijvoorbeeld bij het sorteren van niet-numerieke dingen, of als je wilt dat de sortering stabiel is, wat niet mogelijk is met alleen een boolean)

Dus de vraag is: waarom hebben de Python-ontwerper(s) besloten om de functie signuit de taal te laten? Waarom zou je je in godsnaam bezighouden met copysignen niet met het bovenliggende sign?

Mis ik iets?

EDIT – na commentaar van Peter Hansen.
Eerlijk genoeg dat je het niet hebt gebruikt, maar je hebt niet gezegd waarvoor je python gebruikt. In de 7 jaar dat ik python gebruik, had ik het ontelbare keren nodig, en de laatste is de druppel die de emmer deed overlopen!

Ja, je kunt cmp doorgeven, maar 90% van de keren dat ik moest slagen was in een idioom als
lambda x,y: cmp(score(x),score(y))dat zou prima met sign hebben gewerkt.

Ten slotte hoop ik dat u het ermee eens bent dat signnuttiger zou zijn dan copysign, dus zelfs als ik uw mening zou kopen, waarom zou ik me dan druk maken om dat in wiskunde te definiëren, in plaats van teken? Hoe kan copysign zo nuttig zijn dan sign?


Antwoord 1, autoriteit 100%

BEWERKEN:

Er was inderdaad een patchmet sign()in wiskunde, maar het werd niet geaccepteerd, omdat ze het niet eens waren over wat het zou moeten retourneren in alle randgevallen(+/-0, +/-nan, enz.)

Dus besloten ze om alleen copysign te implementeren, wat (hoewel uitgebreider) gebruikt kan worden om de eindgebruiker de gewenst gedrag voor randgevallen– waarvoor soms de aanroep van cmp(x,0).


Ik weet niet waarom het geen ingebouwde functie is, maar ik heb een paar gedachten.

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

Het belangrijkste is dat copysigneen superset is van sign! Het aanroepen van copysignmet x=1 is hetzelfde als een signfunctie. U kunt dus gewoon copysigngebruiken en vergeet het.

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

Als je het beu bent om twee hele argumenten door te geven, kun je signop deze manier implementeren, en het zal nog steeds compatibel zijn met de IEEE-dingen die door anderen worden genoemd:

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

Ten tweede, meestal als je het teken van iets wilt, vermenigvuldig je het gewoon met een andere waarde. En dat is natuurlijk wat copysigndoet.

Dus, in plaats van:

s = sign(a)
b = b * s

Je kunt gewoon doen:

b = copysign(b, a)

En ja, het verbaast me dat je Python al 7 jaar gebruikt en denkt dat cmpzo gemakkelijk kan worden verwijderd en vervangen door sign! Heb je nog nooit een klasse geïmplementeerd met een __cmp__methode? Heb je nog nooit cmpaangeroepen en een aangepaste vergelijkingsfunctie opgegeven?

Samengevat, ik heb gemerkt dat ik ook een functie signwil, maar copysignmet als eerste argument 1 zal prima werken. Ik ben het er niet mee eens dat signnuttiger zou zijn dan copysign, aangezien ik heb aangetoond dat het slechts een subset is van dezelfde functionaliteit.


Antwoord 2, autoriteit 25%

“copysign” wordt gedefinieerd door IEEE 754 en maakt deel uit van de C99-specificatie. Daarom staat het in Python. De functie kan niet volledig worden geïmplementeerd door abs(x) * sign(y) vanwege de manier waarop deze NaN-waarden zou moeten verwerken.

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

Dat maakt copysign() een nuttigere functie dan sign().

Wat betreft de specifieke redenen waarom IEEE’s signbit(x) niet beschikbaar is in standaard Python, weet ik niet. Ik kan aannames doen, maar het zou gissen zijn.

De wiskundemodule zelf gebruikt copysign(1, x) als een manier om te controleren of x negatief of niet-negatief is. In de meeste gevallen lijkt het omgaan met wiskundige functies nuttiger dan het hebben van een teken (x) dat 1, 0 of -1 retourneert, omdat er één geval minder is om te overwegen. Het volgende komt bijvoorbeeld uit de wiskundemodule van 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);

Daar kun je duidelijk zien dat copysign() een effectievere functie is dan een driewaardige sign()-functie.

Je schreef:

Als ik een python-ontwerper was, zou ik andersom zijn: geen cmp() ingebouwd, maar een teken()

Dat betekent dat je niet weet dat cmp() voor andere dingen dan getallen wordt gebruikt. cmp(“This”, “That”) kan niet worden geïmplementeerd met een sign()-functie.

Bewerken om mijn aanvullende antwoorden elders te verzamelen:

U baseert uw rechtvaardigingen op hoe abs() en sign() vaak samen worden gezien. Aangezien de C-standaardbibliotheek geen enkele ‘sign(x)’-functie bevat, weet ik niet hoe u uw standpunten rechtvaardigt. Er is een abs(int) en fabs(double) en fabsf(float) en fabsl(long) maar geen melding gemaakt van teken. Er zijn “copysign()” en “signbit()”, maar die zijn alleen van toepassing op IEEE 754-nummers.

Wat zou teken(-3+4j) met complexe getallen retourneren in Python, als het zou worden geïmplementeerd? abs(-3+4j) retour 5.0. Dat is een duidelijk voorbeeld van hoe abs() kan worden gebruikt op plaatsen waar sign() geen zin heeft.

Stel dat sign(x) is toegevoegd aan Python, als aanvulling op abs(x). Als ‘x’ een instantie is van een door de gebruiker gedefinieerde klasse die de __abs__(self) methode implementeert, dan zal abs(x) x.__abs__() aanroepen. Om correct te werken, om abs(x) op dezelfde manier te behandelen, zal Python een teken(x)-slot moeten krijgen.

Dit is overdreven voor een relatief onnodige functie. Trouwens, waarom zouden teken(x) wel bestaan en niet-negatief(x) en niet-positief(x) niet? Mijn fragment uit de implementatie van de wiskundige module van Python laat zien hoe copybit(x, y) kan worden gebruikt om niet-negatief() te implementeren, wat een eenvoudig teken (x) niet kan doen.

Python zou betere ondersteuning moeten bieden voor de wiskundige IEEE 754/C99-functie. Dat zou een functie signbit(x) toevoegen, die zou doen wat je wilt in het geval van floats. Het zou niet werken voor gehele getallen of complexe getallen, laat staan strings, en het zou niet de naam hebben die u zoekt.

Je vraagt “waarom”, en het antwoord is “teken(x) is niet nuttig.” U beweert dat het nuttig is. Toch blijkt uit uw opmerkingen dat u niet genoeg weet om die bewering te kunnen doen, wat betekent dat u overtuigend bewijs zou moeten leveren van de noodzaak ervan. Zeggen dat NumPy het implementeert, is niet overtuigend genoeg. Je zou voorbeelden moeten laten zien van hoe bestaande code zou worden verbeterd met een tekenfunctie.

En dat het buiten het bereik van StackOverflow valt. Breng het in plaats daarvan naar een van de Python-lijsten.


Antwoord 3, autoriteit 15%

Nog een oneliner voor sign()

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

Als je wilt dat het 0 teruggeeft voor x = 0:

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

Antwoord 4, autoriteit 11%

Sinds cmpverwijderdis, kan dezelfde functionaliteit krijgen met

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

Het werkt voor float, intEN ZELFDE Fraction. In het geval van float, Kennisgeving sign(float("nan"))is nul.

Python vereist niet dat vergelijkingen een Boolean retourneren, en dus dwingen de vergelijkingen naar Bool () beschermt tegen toegestane, maar ongewone implementatie:

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

Antwoord 5, Autoriteit 7%

Alleen correcte antwoord die voldoet aan de Wikipedia-definitie

De Definitie op Wikipedia leest:

Vandaar,

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

Welke voor alle intenties en doeleinden kan worden vereenvoudigd naar:

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

Deze functie-definitie Voert snel en levert Gegarandeerd Correcte resultaten voor 0, 0.0, -0.0, -4 en 5 (zie opmerkingen op andere onjuiste antwoorden).

Houd er rekening mee dat nul (0) noch positief noch negatief .


Antwoord 6, Autoriteit 5%

Numpy heeft een tekenfunctie en geeft u ook een bonus van andere functies. Dus:

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

Pas op dat het resultaat een numpy.float64 is:

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

Voor zaken als json is dit van belang, aangezien json niet weet hoe numpy.float64-typen moeten worden geserialiseerd. In dat geval kunt u het volgende doen:

float(np.sign(y))

om een normale float te krijgen.


Antwoord 7, autoriteit 4%

Probeer dit uit te voeren, waarbij x een willekeurig getal is

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

De dwang tot bool() handelt de mogelijkheidaf die de vergelijkingsoperator geeft geen boolean terug.


Antwoord 8, autoriteit 2%

Ja, een correcte sign()-functie moet minimaal zijn in de wiskundemodule – zoals het is in numpy. Omdat je het vaak nodig hebt voor wiskundig georiënteerde code.

Maar math.copysign()is ook zelfstandig bruikbaar.

cmp()en obj.__cmp__()… zijn over het algemeen onafhankelijk van elkaar van groot belang. Niet alleen voor wiskundig georiënteerde code. Overweeg om tupels, datumobjecten, … te vergelijken/sorteren.

De dev-argumenten op http://bugs.python.org/issue1640met betrekking tot het weglaten van math.sign()zijn vreemd, omdat:

  • Er is geen aparte -NaN
  • sign(nan) == nanzonder zorgen (zoals exp(nan))
  • sign(-0.0) == sign(0.0) == 0zonder zorgen
  • sign(-inf) == -1zonder zorgen

– zoals het in numpy is


Antwoord 9, Autoriteit 2%

Het is gewoon niet.

De beste manier om dit te repareren is:

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

Antwoord 10, Autoriteit 2%

In Python 2, cmp()retourneert een geheel getal: er is geen vereiste dat het resultaat is -1, 0 of 1, dus sign(x)is dat niet hetzelfde als cmp(x,0).

In Python 3, cmp()is verwijderd ten gunste van rijke vergelijking. Voor cmp(), python 3 suggereert dit :

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

wat prima is voor CMP (), maar opnieuw kan niet worden gebruikt voor teken () omdat de vergelijkingsoperators niet Booleans .

Om met deze mogelijkheid om te gaan, moeten de vergelijkingsresultaten worden gedwongen aan Booleans:

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

Dit werkt voor elke typedie volledig is besteld (inclusief speciale waarden zoals NaNof oneindigheden).


Antwoord 11

Veel gevallen vermeld in andere antwoorden overzien speciale gevallen (+/- 0) of veronderstelling maken dat teken (-0.0) == teken (0.0). Het kan naïef zijn, maar met de huidige implementatie van IEEE hebben we al -0.0 == 0.0 en het hebben van teken () zou ons in staat stellen om te onduberen tussen de twee.

Voorbeeld van Futlebird lijkt de beste definitie te zijn voor zover het lijkt te hanteren +/- 0, Infinity en Nan.


Antwoord 12

Je hebt er geen nodig, je kunt gewoon het volgende gebruiken:

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

Of maak een functie zoals beschreven door anderen:

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

Antwoord 13

De reden dat “teken” niet is opgenomen, is dat als we elke nuttige one-liner in de lijst met ingebouwde functies zouden opnemen, Python niet gemakkelijk en praktisch meer zou zijn om mee te werken.
Als je deze functie zo vaak gebruikt, waarom zou je er dan zelf geen rekening mee houden? Het is niet zo moeilijk of zelfs vervelend om dit te doen.

Other episodes