182

I'd like to point to a function that does nothing:

def identity(*args)
    return args

my use case is something like this

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

Of course, I could use the identity defined above, but a built-in would certainly run faster (and avoid bugs introduced by my own).

Apparently, map and filter use None for the identity, but this is specific to their implementations.

>>> _=None
>>> _("hello")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
rds
  • 26,253
  • 19
  • 107
  • 134
  • 8
    What do you mean by `map and filter use None for the identity`? – Matt Fenwick Jan 05 '12 at 18:53
  • 20
    @MattFenwick: `map(None, [1, 2, 3])` – Greg Hewgill Jan 05 '12 at 18:55
  • 6
    Check out the return value. Your args variable will be a sequence of (in this scenario) one value, so either omit the asterisk in the declaration, or unpack it befor returning. – Dirk Jan 05 '12 at 18:56
  • 13
    @GregHewgill: Sadly, that doesn't work in Python 3.x. – Ethan Furman Jan 05 '12 at 19:59
  • 2
    @EthanFurman: Thanks, good to know, I didn't try it there. – Greg Hewgill Jan 05 '12 at 20:00
  • 6
    @GregHewgill My bad. I took that from the doc after googling. But the Python2.x doc always comes first... – rds Jan 06 '12 at 00:07
  • 2
    Is there something else these answers need to provide to be accepted? – Ethan Furman Jan 13 '12 at 00:03
  • 2
    What is your use case for the `identity` function? – Brian Cain Oct 16 '14 at 02:32
  • 2
    In this case you could also do directly `def _(x): return x` – kxr Feb 11 '16 at 19:27
  • 2
    One use case I can think of is when you have boundary variables and you want to switch them from wrapped-around, raising an exception when out-of-bounds, or completely unchecked. The unchecked case would simply use an identity function instead of one that wraps or raises an exception ;p – Darren Ringer Jun 27 '16 at 21:00
  • I think the answer is `id= lambda *args: args if len(args)>1 else args[0]`. It will return x when you call id(x), for any object x or sequence of multiple arguments in which case that sequence will be returned as a tuple: I can't see a more natural realization of what is asked. – Max Feb 21 '20 at 22:44

10 Answers10

122

Doing some more research, there is none, a feature was asked in issue 1673203 And from Raymond Hettinger said there won't be:

Better to let people write their own trivial pass-throughs and think about the signature and time costs.

So a better way to do it is actually (a lambda avoids naming the function):

_ = lambda *args: args
  • advantage: takes any number of parameters
  • disadvantage: the result is a boxed version of the parameters

OR

_ = lambda x: x
  • advantage: doesn't change the type of the parameter
  • disadvantage: takes exactly 1 positional parameter
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
rds
  • 26,253
  • 19
  • 107
  • 134
  • 21
    Note that this is not an identity function. – Marcin Jan 05 '12 at 19:06
  • 2
    @Marcin Thanks for the remark. I have added advantages/drawbacks of the two in order not to mislead anyone. And now, I really believe there should have been a builtin function that accepts any number of parameters and is a true identity :) – rds Jan 05 '12 at 19:14
  • 10
    Nice answer. However, what would a true identity function return when taking multiple parameters? – Marcin Jan 05 '12 at 19:30
  • @Marcin: The same multiple parameters. They would be in a tuple, but this is the correct way to either 1) store multiple items in one variable, or 2) unbox them (`a, b, c = identity(1, 2, 3)`). – Ethan Furman Jan 05 '12 at 19:45
  • 1
    @EthanFurman: That is the behaviour that is already available. One presumes that @ rds wants something else. – Marcin Jan 05 '12 at 22:02
  • 1
    @Marcin: What he wants is that behaviour as a built-in. Since that's not available, the remaining question is how should his custom function handle single arguments. – Ethan Furman Jan 05 '12 at 22:15
  • 1
    @EthanFurman: Are you psychic, or rds' official spokesman now? – Marcin Jan 05 '12 at 22:18
  • 5
    @Marcin: Neither, just going by what he asked in his question. – Ethan Furman Jan 05 '12 at 22:48
  • 4
    Yes thanks, I have a trivial `lambda x: x` identity function that works for one string parameter. @Marcin I wish I could do `lambda *args: *args` :-) – rds Jan 06 '12 at 00:01
  • @rds: Have you looked at my (updated) answer? It returns a single item when a single item was passed in, and a tuple when multiple items are passed in. – Ethan Furman Jan 06 '12 at 00:20
  • @rds: I think `lambda *args: args if len(args)>1 else args[0]` does what you want: It will return x when you call id(x), for any object x or sequence of more than one args in which case that sequence will be returned as a tuple. – Max Feb 21 '20 at 22:47
  • "Better to let people write their own trivial pass-throughs and think about the signature and time costs" is such a strange argument against this given the million other examples in Python that do the exact opposite, where ease of writing and reading code is more important than the runtime cost behind the scenes... – gsgx Aug 30 '22 at 16:29
52

An identity function, as defined in https://en.wikipedia.org/wiki/Identity_function, takes a single argument and returns it unchanged:

def identity(x):
    return x

What you are asking for when you say you want the signature def identity(*args) is not strictly an identity function, as you want it to take multiple arguments. That's fine, but then you hit a problem as Python functions don't return multiple results, so you have to find a way of cramming all of those arguments into one return value.

The usual way of returning "multiple values" in Python is to return a tuple of the values - technically that's one return value but it can be used in most contexts as if it were multiple values. But doing that here means you get

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

And fixing that problem quickly gives other issues, as the various answers here have shown.

So, in summary, there's no identity function defined in Python because:

  1. The formal definition (a single argument function) isn't that useful, and is trivial to write.
  2. Extending the definition to multiple arguments is not well-defined in general, and you're far better off defining your own version that works the way you need it to for your particular situation.

For your precise case,

def dummy_gettext(message):
    return message

is almost certainly what you want - a function that has the same calling convention and return as gettext.gettext, which returns its argument unchanged, and is clearly named to describe what it does and where it's intended to be used. I'd be pretty shocked if performance were a crucial consideration here.

Paul Moore
  • 6,569
  • 6
  • 40
  • 47
  • I don't see to what answers you refer with " fixing that problem gives other issues, as answers have shown". Specifically, it's enough to use `id= lambda *args: args if len(args)>1 else args[0]`. – Max Feb 21 '20 at 22:37
  • 6
    @Max, with your proposal of `_ = lambda *args: args if len(args)>1 else args[0]`, calling `_((1,2))` (with one tuple argument!) will yield `(1,2)`, which is the same result as calling `_(1,2)` (with two integer arguments). So, your function is not injective: You cannot tell from the output what the input was. That's a pretty ill-defined state of affairs for an identity function. (It should be bijective, which includes injectivity.) – Peter Thomassen Jan 15 '21 at 19:23
25

yours will work fine. When the number of parameters is fix you can use an anonymous function like this:

lambda x: x
tback
  • 11,138
  • 7
  • 47
  • 71
  • 9
    You can do this with varargs too: `lambda *args: args`. It's really a stylistic choice. –  Jan 05 '12 at 18:57
  • I like the second better, since it takes any number of arguments. – rds Jan 05 '12 at 18:59
  • 8
    @delnan @rds - the `*args` version has a different return type, so they are not equivalent even for the single-argument case. – Marcin Jan 05 '12 at 19:00
  • @Marcin: I for one know they aren't equivalent, but both versions can be written with `lambda` as well as with `def`. That was my whole point. –  Jan 05 '12 at 19:02
  • 10
    @delnan: You said that it's a stylistic choice, which incorrectly implies that there is no difference in the semantics of the two forms. – Marcin Jan 05 '12 at 19:04
  • 2
    @Marcin: It's unfortunate if I implied that. I meant the choice between `def` and `lambda` for such simple functions. –  Jan 05 '12 at 19:31
13

There is no a built-in identity function in Python. An imitation of the Haskell's id function would be:

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

Example usage:

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

Since identity does nothing except returning the given arguments, I do not think that it is slower than a native implementation would be.

SergiyKolesnikov
  • 7,369
  • 2
  • 26
  • 47
  • It's the construction of the call itself that takes time, regardless of what you do after that setup is complete. – chepner Aug 07 '19 at 20:22
  • @chepner Could you explain in more detail what you mean? A call to a native function must be constructed too, right? Is this construction done faster than a call construction to a non-native function? – SergiyKolesnikov Mar 26 '20 at 12:46
  • 1
    A call to a user-defined function is at least as expensive as a call to a built-in function, and probably more so because once you have called the user-defined function, anything else it may invoke *more* user-defined or built-in functions. – chepner Mar 26 '20 at 12:54
  • As it is (Apr 2023), without any arguments, `identity()` fails; this one would also fix that case: ``identity = lambda x=None, *args: (x,) + args if args else x``. – ankostis Apr 29 '23 at 15:49
  • @ankostis It depends on how we define the identity function. As it is implemented right now, it always requires an argument. Otherwise, it is undefined (i.e., fails). – SergiyKolesnikov May 05 '23 at 21:03
7

No, there isn't.

Note that your identity:

  1. is equivalent to lambda *args: args
  2. Will box its args - i.e.

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

So, you may want to use lambda arg: arg if you want a true identity function.

NB: This example will shadow the built-in id function (which you will probably never use).

Marcin
  • 48,559
  • 18
  • 128
  • 201
5

If the speed does not matter, this should handle all cases:

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

Examples of usage:

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)
ankostis
  • 8,579
  • 3
  • 47
  • 61
Binyan Hu
  • 71
  • 1
  • 2
1

Stub of a single-argument function

gettext.gettext (the OP's example use case) accepts a single argument, message. If one needs a stub for it, there's no reason to return [message] instead of message (def identity(*args): return args). Thus both

_ = lambda message: message

def _(message):
    return message

fit perfectly.

...but a built-in would certainly run faster (and avoid bugs introduced by my own).

Bugs in such a trivial case are barely relevant. For an argument of predefined type, say str, we can use str() itself as an identity function (because of string interning it even retains object identity, see id note below) and compare its performance with the lambda solution:

$ 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

A micro-optimisation is possible. For example, the following Cython code:

test.pyx

cpdef str f(str message):
    return message

Then:

$ 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

Build-in object identity function

Don't confuse an identity function with the id built-in function which returns the 'identity' of an object (meaning a unique identifier for that particular object rather than that object's value, as compared with == operator), its memory address in CPython.

saaj
  • 23,253
  • 3
  • 104
  • 105
  • A 40% speedup "doesn't seem too worth it"? In cases where the identity operates as a "default filter" for a function that runs, say, once per channel on a 10,000x10,000 pixel image (perhaps not every-day but certainly not uncommon), that's the difference between 25 and 9 seconds of execution time! Regardless, thank you for the Cython example. – 9999years Jul 06 '18 at 15:09
  • @9999years I agree. I've removed the worthiness comment. Also thanks for improving the answer. I've made a few minor changes on top of yours. – saaj Jul 06 '18 at 15:23
  • If you have a 10,000x10,000 pixel image then I would strongly recommend using vectorized operations using something like numpy. It will be way faster, use way less memory, and not require writing cython code. – anthonybell Dec 11 '18 at 23:10
1

Lots of good answers and discussion are in this topic. I just want to note that, in OP's case where there is a single argument in the identity function, compile-wise it doesn't matter if you use a lambda or define a function (in which case you should probably define the function to stay PEP8 compliant). The bytecodes are functionally identical:

import dis
function_method = compile("def identity(x):\n    return x\ny=identity(Type('x', (), dict()))", "foo", "exec")
dis.dis(function_method)
  1           0 LOAD_CONST               0 (<code object identity at 0x7f52cc30b030, file "foo", line 1>)
              2 LOAD_CONST               1 ('identity')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (identity)

  3           8 LOAD_NAME                0 (identity)
             10 LOAD_NAME                1 (Type)
             12 LOAD_CONST               2 ('x')
             14 LOAD_CONST               3 (())
             16 LOAD_NAME                2 (dict)
             18 CALL_FUNCTION            0
             20 CALL_FUNCTION            3
             22 CALL_FUNCTION            1
             24 STORE_NAME               3 (y)
             26 LOAD_CONST               4 (None)
             28 RETURN_VALUE

Disassembly of <code object identity at 0x7f52cc30b030, file "foo", line 1>:
  2           0 LOAD_FAST                0 (x)
              2 RETURN_VALUE

And lambda

import dis
lambda_method = compile("identity = lambda x: x\ny=identity(Type('x', (), dict()))", "foo", "exec")
dis.dis(lambda_method)
  1           0 LOAD_CONST               0 (<code object <lambda> at 0x7f52c9fbbd20, file "foo", line 1>)
              2 LOAD_CONST               1 ('<lambda>')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (identity)

  2           8 LOAD_NAME                0 (identity)
             10 LOAD_NAME                1 (Type)
             12 LOAD_CONST               2 ('x')
             14 LOAD_CONST               3 (())
             16 LOAD_NAME                2 (dict)
             18 CALL_FUNCTION            0
             20 CALL_FUNCTION            3
             22 CALL_FUNCTION            1
             24 STORE_NAME               3 (y)
             26 LOAD_CONST               4 (None)
             28 RETURN_VALUE

Disassembly of <code object <lambda> at 0x7f52c9fbbd20, file "foo", line 1>:
  1           0 LOAD_FAST                0 (x)
              2 RETURN_VALUE
Andrew Holmgren
  • 1,225
  • 1
  • 11
  • 18
0

Adding to all answers:

Notice there is an implicit convention in Python stdlib, where a HOF defaulting it's key parameter function to the identity function, interprets None as such.

E.g. sorted, heapq.merge, max, min, etc.

So, it is not bad idea to consider your HOF expecting key to following the same pattern.

That is, instead of:

def my_hof(x, key=lambda _: _):
   ...

(whis is totally right)

You could write:

def my_hof(x, key=None):
    if key is None: key = lambda _: _
    ...

If you want.

jgomo3
  • 1,153
  • 1
  • 13
  • 26
  • 1
    fyi I'm pretty sure `key = lambda _: _ if key is None` isn't valid Python syntax, it would be `if key is None: key = lamda _: _` – Harold Cooper Mar 24 '22 at 18:41
  • 1
    @HaroldCooper you are right. The form sufixing the `if` requires an `else`. And the lambda would include all the if statement anyways. The other way would be then `key = (lambda _: _) if key is None else None`. But I prefer the form you present prefixing the `if`. I'm editing now the answer. – jgomo3 Mar 26 '22 at 00:18
-2

The thread is pretty old. But still wanted to post this.

It is possible to build an identity method for both arguments and objects. In the example below, ObjOut is an identity for ObjIn. All other examples above haven't dealt with dict **kwargs.

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
  • 183
  • 1
  • 7
  • this looks like a reference, if so, then where is it from? – Jeff Puckett Apr 28 '16 at 19:16
  • @JeffPuckettII I didn't follow your question. Are you asking if the new object is a reference? – Sud Apr 28 '16 at 21:52
  • you used a blockquote highlight for "It is possible to build an identity..." which implies a reference from another source. If these are your own words, then I would suggest not highlighting it as a quote. really not a big deal. but if this is a quote from another source, then you should include a reference to it. – Jeff Puckett Apr 28 '16 at 22:01
  • How do you answer the original question `map(identity, [1, 2, 3])` returns `[1, 2, 3]`? – rds May 11 '16 at 22:52
  • `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())` --> Result: ([1, 2, 3],) – Sud Jun 01 '16 at 20:18
  • Your `.identity()` function is no different from bare assignment: `objOut = objIn.identity()` is `objOut = objIn`. This thread is about a function that returns what is was given: `objIn is identity(objIn)` -- `kwargs` makes no sense in that context. – Ethan Furman Dec 07 '17 at 18:01