Gibt es eine integrierte Identitätsfunktion in Python?

145

Ich möchte auf eine Funktion verweisen, die nichts bewirkt:

def identity(*args)
    return args

Mein Anwendungsfall ist so etwas

try:
    gettext.find(...)
    ...
    _ = gettext.gettext
else:
    _ = identity

Natürlich könnte ich das identityoben definierte verwenden, aber ein eingebautes würde sicherlich schneller laufen (und Fehler vermeiden, die von mir selbst verursacht wurden).

Anscheinend mapund filterVerwendung Nonefür die Identität, aber dies ist spezifisch für ihre Implementierungen.

>>> _=None
>>> _("hello")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
rds
quelle
6
Was meinst du damit map and filter use None for the identity?
Matt Fenwick
15
@ MattFenwick:map(None, [1, 2, 3])
Greg Hewgill
6
Überprüfen Sie den Rückgabewert. Ihre args-Variable besteht aus einer Folge von (in diesem Szenario) einem Wert. Lassen Sie daher entweder das Sternchen in der Deklaration weg oder entpacken Sie es vor der Rückgabe.
Dirk
11
@ GregHewgill: Leider funktioniert das in Python 3.x nicht.
Ethan Furman
6
@ GregHewgill Mein schlechtes. Ich habe das dem Arzt nach dem Googeln abgenommen. Aber das Python2.x-Dokument steht immer an erster Stelle ...
6.

Antworten:

99

Bei weiteren Nachforschungen gibt es keine, ein Feature wurde in Ausgabe 1673203 gefragt. Und von Raymond Hettinger sagte, es werde keine geben :

Es ist besser, die Leute ihre eigenen trivialen Pass-Throughs schreiben zu lassen und über die Unterschrift und die Zeitkosten nachzudenken.

Ein besserer Weg, dies zu tun, ist tatsächlich (ein Lambda vermeidet es, die Funktion zu benennen):

_ = lambda *args: args
  • Vorteil: Nimmt eine beliebige Anzahl von Parametern
  • Nachteil: Das Ergebnis ist eine Box-Version der Parameter

ODER

_ = lambda x: x
  • Vorteil: Ändert den Typ des Parameters nicht
  • Nachteil: Nimmt genau 1 Positionsparameter
rds
quelle
13
Beachten Sie, dass dies keine Identitätsfunktion ist.
Marcin
1
@Marcin Danke für die Bemerkung. Ich habe die Vor- und Nachteile der beiden hinzugefügt, um niemanden irrezuführen. Und jetzt glaube ich wirklich, dass es eine eingebaute Funktion geben sollte, die eine beliebige Anzahl von Parametern akzeptiert und eine wahre Identität ist :)
rds
7
Gute Antwort. Was würde eine echte Identitätsfunktion jedoch zurückgeben, wenn mehrere Parameter verwendet werden?
Marcin
5
@Marcin: Weder noch nach dem, was er in seiner Frage gefragt hat.
Ethan Furman
4
Ja, danke, ich habe eine triviale lambda x: xIdentitätsfunktion, die für einen Zeichenfolgenparameter funktioniert. @Marcin Ich wünschte, ich könnte tun lambda *args: *args:-)
rds
28

Eine Identitätsfunktion, wie sie in https://en.wikipedia.org/wiki/Identity_function definiert ist , nimmt ein einzelnes Argument und gibt es unverändert zurück:

def identity(x):
    return x

Was Sie verlangen, wenn Sie sagen, dass Sie die Signatur möchten, def identity(*args)ist keine reine Identitätsfunktion, da Sie möchten, dass mehrere Argumente verwendet werden. Das ist in Ordnung, aber dann stoßen Sie auf ein Problem, da Python-Funktionen nicht mehrere Ergebnisse zurückgeben. Sie müssen also einen Weg finden, alle diese Argumente in einen Rückgabewert zu packen.

Die übliche Methode zum Zurückgeben von "mehreren Werten" in Python besteht darin, ein Tupel der Werte zurückzugeben - technisch gesehen ist dies ein Rückgabewert, der jedoch in den meisten Kontexten verwendet werden kann, als wären es mehrere Werte. Aber das hier zu tun bedeutet, dass Sie bekommen

>>> def mv_identity(*args):
...     return args
...
>>> mv_identity(1,2,3)
(1, 2, 3)
>>> # So far, so good. But what happens now with single arguments?
>>> mv_identity(1)
(1,)

Und die schnelle Behebung dieses Problems führt zu anderen Problemen, wie die verschiedenen Antworten hier gezeigt haben.

Zusammenfassend ist in Python keine Identitätsfunktion definiert, weil:

  1. Die formale Definition (eine einzelne Argumentfunktion) ist nicht so nützlich und trivial zu schreiben.
  2. Das Erweitern der Definition auf mehrere Argumente ist im Allgemeinen nicht genau definiert, und Sie sollten Ihre eigene Version besser definieren, die so funktioniert, wie Sie es für Ihre spezielle Situation benötigen.

Für Ihren genauen Fall,

def dummy_gettext(message):
    return message

ist mit ziemlicher Sicherheit das, was Sie wollen - eine Funktion, die dieselbe Aufrufkonvention und Rückgabe wie hat gettext.gettext, die ihr Argument unverändert zurückgibt und eindeutig benannt ist, um zu beschreiben, was sie tut und wo sie verwendet werden soll. Ich wäre ziemlich schockiert, wenn die Leistung hier eine entscheidende Rolle spielen würde.

Paul Moore
quelle
Ich sehe nicht, auf welche Antworten Sie sich beziehen mit "Das Beheben dieses Problems führt zu anderen Problemen, wie die Antworten gezeigt haben". Insbesondere ist es genug zu verwenden id= lambda *args: args if len(args)>1 else args[0].
Max
21

Ihre wird gut funktionieren. Wenn die Anzahl der Parameter festgelegt ist, können Sie eine anonyme Funktion wie die folgende verwenden:

lambda x: x
tback
quelle
8
Sie können dies auch mit varargs tun : lambda *args: args. Es ist wirklich eine stilistische Wahl.
Ich mag die zweite besser, da es eine beliebige Anzahl von Argumenten braucht.
rds
4
@delnan @rds - Die *argsVersion hat einen anderen Rückgabetyp, daher sind sie selbst für den Fall mit einem Argument nicht gleichwertig.
Marcin
8
@delnan: Du hast gesagt, dass es eine stilistische Wahl ist, was fälschlicherweise impliziert, dass es keinen Unterschied in der Semantik der beiden Formen gibt.
Marcin
1
@Marcin: Es ist bedauerlich, wenn ich das impliziert habe. Ich meinte die Wahl zwischen defund lambdafür solch einfache Funktionen.
7

In Python ist keine Identitätsfunktion integriert. Eine Nachahmung der Haskell- idFunktion wäre:

identity = lambda x, *args: (x,) + args if args else x

Anwendungsbeispiel:

identity(1)
1
identity(1,2)
(1, 2)

Da identitynichts anderes tut, als die angegebenen Argumente zurückzugeben, denke ich nicht, dass es langsamer ist als eine native Implementierung.

SergiyKolesnikov
quelle
Es ist der Aufbau des Anrufs selbst, der Zeit braucht, unabhängig davon, was Sie nach Abschluss dieses Setups tun.
Chepper
@chepner Könntest du genauer erklären, was du meinst? Ein Aufruf einer nativen Funktion muss ebenfalls erstellt werden, oder? Wird diese Konstruktion schneller ausgeführt als eine Aufrufkonstruktion für eine nicht native Funktion?
SergiyKolesnikov
1
Ein Aufruf einer benutzerdefinierten Funktion ist mindestens so teuer wie ein Aufruf einer integrierten Funktion, und wahrscheinlich umso teurer, als wenn Sie die benutzerdefinierte Funktion aufgerufen haben, kann alles andere mehr benutzerdefinierte oder integrierte Funktionen aufrufen. in Funktionen.
Chepper
6

Nein, gibt es nicht.

Beachten Sie, dass Ihre identity:

  1. entspricht lambda * args: args
  2. Wird seine Argumente boxen - dh

    In [6]: id = lambda *args: args
    
    In [7]: id(3)
    Out[7]: (3,)

Daher möchten Sie möglicherweise verwenden, lambda arg: argwenn Sie eine echte Identitätsfunktion wünschen.

NB: In diesem Beispiel wird die integrierte idFunktion (die Sie wahrscheinlich nie verwenden werden) beschattet .

Marcin
quelle
1
Beachten Sie, dass id eine integrierte Funktion ist und von diesem Snippet überschrieben wird.
Arnie97
@ Arnie97 Messe! Ich habe vergessenid
Marcin
4

Wenn die Geschwindigkeit keine Rolle spielt, sollte dies alle Fälle behandeln:

def identity(*args, **kwargs):
    if not args:
        if not kwargs:
            return None
        elif len(kwargs) == 1:
            return  next(iter(kwargs.values()))
        else:
            return (*kwargs.values(),)
    elif not kwargs:
        if len(args) == 1:
            return args[0]
        else:
            return args
    else:
        return (*args, *kwargs.values())

Anwendungsbeispiele:

print(identity())
None
$identity(1)
1
$ identity(1, 2)
(1, 2)
$ identity(1, b=2)
(1, 2)
$ identity(a=1, b=2)
(1, 2)
$ identity(1, 2, c=3)
(1, 2, 3)
Binyan Hu
quelle
1

Stub einer Einzelargumentfunktion

gettext.gettext(der Anwendungsfall des OP) akzeptiert ein einzelnes Argument message. Wenn man dafür einen Stub braucht, gibt es keinen Grund, [message]statt message( def identity(*args): return args) zurückzukehren. Also beides

_ = lambda message: message

def _(message):
    return message

passt perfekt.

... aber ein eingebautes würde sicherlich schneller laufen (und Fehler vermeiden, die von mir selbst verursacht wurden).

Fehler in einem so trivialen Fall sind kaum relevant. Für ein Argument vom vordefinierten Typ können wir struns str()beispielsweise als Identitätsfunktion verwenden (da die Zeichenfolge interniert , behält es sogar die Objektidentität bei, siehe idHinweis unten) und seine Leistung mit der Lambda-Lösung vergleichen:

$ python3 -m timeit -s "f = lambda m: m" "f('foo')"
10000000 loops, best of 3: 0.0852 usec per loop
$ python3 -m timeit "str('foo')"
10000000 loops, best of 3: 0.107 usec per loop

Eine Mikrooptimierung ist möglich. Zum Beispiel der folgende Cython- Code:

test.pyx

cpdef str f(str message):
    return message

Dann:

$ pip install runcython3
$ makecython3 test.pyx
$ python3 -m timeit -s "from test import f" "f('foo')"
10000000 loops, best of 3: 0.0317 usec per loop

Integrierte Objektidentitätsfunktion

Verwechseln Sie eine Identitätsfunktion nicht mit der idintegrierten Funktion, die die 'Identität' eines Objekts (dh eine eindeutige Kennung für dieses bestimmte Objekt und nicht den Wert dieses Objekts im Vergleich zum ==Operator), seine Speicheradresse in CPython, zurückgibt.

saaj
quelle
Eine 40% ige Beschleunigung "scheint es nicht zu wert zu sein"? In Fällen, in denen die Identität als "Standardfilter" für eine Funktion fungiert, die beispielsweise einmal pro Kanal auf einem Bild mit 10.000 x 10.000 Pixeln ausgeführt wird (möglicherweise nicht täglich, aber sicherlich nicht ungewöhnlich), ist dies der Unterschied zwischen 25 und 9 Sekunden Ausführungszeit! Trotzdem vielen Dank für das Cython-Beispiel.
9999 Jahre
@ 9999 Jahre Ich stimme zu. Ich habe den Kommentar zur Würdigkeit entfernt. Vielen Dank auch für die Verbesserung der Antwort. Ich habe ein paar kleinere Änderungen zusätzlich zu Ihren vorgenommen.
Saaj
Wenn Sie ein Bild mit 10.000 x 10.000 Pixel haben, würde ich dringend empfehlen, vektorisierte Operationen mit so etwas wie Numpy zu verwenden. Es ist viel schneller, verbraucht viel weniger Speicher und erfordert kein Schreiben von Cython-Code.
Anthonybell
-2

Der Thread ist ziemlich alt. Wollte das aber trotzdem posten.

Es ist möglich, eine Identitätsmethode sowohl für Argumente als auch für Objekte zu erstellen. Im folgenden Beispiel ist ObjOut eine Identität für ObjIn. Alle anderen obigen Beispiele haben sich nicht mit diktierten ** kwargs befasst.

class test(object):
    def __init__(self,*args,**kwargs):
        self.args = args
        self.kwargs = kwargs
    def identity (self):
        return self

objIn=test('arg-1','arg-2','arg-3','arg-n',key1=1,key2=2,key3=3,keyn='n')
objOut=objIn.identity()
print('args=',objOut.args,'kwargs=',objOut.kwargs)

#If you want just the arguments to be printed...
print(test('arg-1','arg-2','arg-3','arg-n',key1=1,key2=2,key3=3,keyn='n').identity().args)
print(test('arg-1','arg-2','arg-3','arg-n',key1=1,key2=2,key3=3,keyn='n').identity().kwargs)

$ py test.py
args= ('arg-1', 'arg-2', 'arg-3', 'arg-n') kwargs= {'key1': 1, 'keyn': 'n', 'key2': 2, 'key3': 3}
('arg-1', 'arg-2', 'arg-3', 'arg-n')
{'key1': 1, 'keyn': 'n', 'key2': 2, 'key3': 3}
Sud
quelle
das sieht aus wie eine Referenz, wenn ja, woher kommt es dann?
Jeff Puckett
@ JeffPuckettII Ich bin deiner Frage nicht gefolgt. Fragen Sie, ob das neue Objekt eine Referenz ist?
Sud
Sie haben ein Blockquote-Highlight für "Es ist möglich, eine Identität zu erstellen ..." verwendet, das eine Referenz aus einer anderen Quelle impliziert. Wenn dies Ihre eigenen Worte sind, würde ich vorschlagen, sie nicht als Zitat hervorzuheben. wirklich keine große Sache. Wenn dies jedoch ein Zitat aus einer anderen Quelle ist, sollten Sie einen Verweis darauf einfügen.
Jeff Puckett
Wie beantworten Sie die ursprüngliche Frage map(identity, [1, 2, 3])zurückkehrt [1, 2, 3]?
rds
class test1(object): def __init__(self,*args,**kwargs): self.args = args self.kwargs = kwargs def identity (self): return self.args print(test1([1,2,3]).identity())-> Ergebnis: ([1, 2, 3],)
Sud