198

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
12
  • 8
    What do you mean by map and filter use None for the identity? Commented Jan 5, 2012 at 18:53
  • 21
    @MattFenwick: map(None, [1, 2, 3]) Commented Jan 5, 2012 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. Commented Jan 5, 2012 at 18:56
  • 13
    @GregHewgill: Sadly, that doesn't work in Python 3.x. Commented Jan 5, 2012 at 19:59
  • 6
    @GregHewgill My bad. I took that from the doc after googling. But the Python2.x doc always comes first... Commented Jan 6, 2012 at 0:07

11 Answers 11

127

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
Sign up to request clarification or add additional context in comments.

12 Comments

Note that this is not an identity function.
@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 :)
Nice answer. However, what would a true identity function return when taking multiple parameters?
@Marcin: Neither, just going by what he asked in his question.
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 :-)
|
58
+500

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.

2 Comments

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

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

lambda x: x

6 Comments

You can do this with varargs too: lambda *args: args. It's really a stylistic choice.
I like the second better, since it takes any number of arguments.
@delnan @rds - the *args version has a different return type, so they are not equivalent even for the single-argument case.
@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: It's unfortunate if I implied that. I meant the choice between def and lambda for such simple functions.
|
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.

5 Comments

It's the construction of the call itself that takes time, regardless of what you do after that setup is complete.
@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?
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.
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 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).
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).

2 Comments

Note that id is a built-in function, and this snippet will overwrite it.
@Arnie97 Fair! I forgot about id
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)

Comments

2

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

Comments

2

If you really want to avoid creating your own function and are using functional programming, functoolz has identity():

from toolz.functoolz import identity
identity(3) 

Note that def identity(*args): return args is not the true identity function, because it "packs" its arguments; identity(1,2) returns (1,2). Strictly speaking, the true identity function is a map f: X -> X defined by f(x) = x for all x. Mathematically pedantically, multiple arguments are actually abuse of notation for a single tuple of arguments (or a curried function).

Comments

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.

3 Comments

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

2 Comments

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 _: _
@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.
-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}

6 Comments

this looks like a reference, if so, then where is it from?
@JeffPuckettII I didn't follow your question. Are you asking if the new object is a reference?
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.
How do you answer the original question map(identity, [1, 2, 3]) returns [1, 2, 3]?
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],)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.