[ python-Feature Requests-1673203 ] add identity function

SourceForge.net noreply at sourceforge.net
Thu Mar 22 21:06:37 CET 2007


Feature Requests item #1673203, was opened at 2007-03-03 19:21
Message generated for change (Comment added) made by twobitsprite
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=355470&aid=1673203&group_id=5470

Please note that this message will contain a full copy of the comment thread,
including the initial issue submission, for this request,
not just the latest update.
Category: None
Group: None
Status: Open
Resolution: None
Priority: 5
Private: No
Submitted By: paul rubin (phr)
Assigned to: Raymond Hettinger (rhettinger)
Summary: add identity function

Initial Comment:
Requested and assigned to Raymond at his suggestion:

http://groups.google.com/group/comp.lang.python/msg/603870361743c85c

There should be an identify function identity(x)=x.

I suggest it also take and ignore an optional second arg: identity(x1,x2)=x1.  The second arg can be useful in some generator expressions:

foo = (x for x in bar if condition(x) and identity(True, memoize(x))

That allows calling memoize (or some other function) on the selected elements in the genexp, and disposing of the returned value.  It's sort of like the const function (K combinator) to go along with the identity function's I combinator.  OK, the above is not really in the functional spirit, but it's been useful.

There could conceivably be also an actual const function const(k)=partial(identity,k) but I can't remember needing that in Python code.  The two-arg identity function (uncurried version of const) is probably enough.

----------------------------------------------------------------------

Comment By: Memotype (twobitsprite)
Date: 2007-03-22 16:06

Message:
Logged In: YES 
user_id=1679533
Originator: NO

--- bltinmodule-orig.c  2007-03-22 16:00:21.452245559 -0400
+++ bltinmodule.c       2007-03-22 15:56:19.353115310 -0400
@@ -69,6 +69,17 @@
        return PyNumber_Absolute(v);
 }

+static PyObject *
+builtin_identity(PyObject *self, PyObject *v)
+{
+       return v;
+}
+
+PyDoc_STRVAR(identity_doc,
+"identity(x) -> x\n\
+\n\
+The identity function. Simply returns is first argument.");
+
 PyDoc_STRVAR(abs_doc,
 "abs(number) -> number\n\
 \n\
@@ -2281,6 +2292,7 @@
 #endif
        {"vars",        builtin_vars,       METH_VARARGS, vars_doc},
        {"zip",         builtin_zip,        METH_VARARGS, zip_doc},
+       {"identity",    builtin_identity,   METH_O, identity_doc},
        {NULL,          NULL},
 };



----------------------------------------------------------------------

Comment By: Memotype (twobitsprite)
Date: 2007-03-22 15:37

Message:
Logged In: YES 
user_id=1679533
Originator: NO

Well, I'm suggesting that it be in __builtins__, but whatever... If I had
it my way I'd program in Haskell every chance I got anyways, I only use
python because my boss/co-workers prefer it.

----------------------------------------------------------------------

Comment By: Alexander Belopolsky (belopolsky)
Date: 2007-03-22 15:32

Message:
Logged In: YES 
user_id=835142
Originator: NO

> I can't believe there is such resistance for something
> as trivial as a function which returns it's first argument.

The resistance is precisely because it is so trivial. As  josiahcarlson
suggested before, "Not all x line functions should be built into Python." 
Compare the following two alternatives:

def identity(*args): return args[0]

and

from functools import identity

The first option is only slightly longer than the second, but it is
actually much clearer.  With the  second option, it is not obvious that
identity takes an arbitrary number of arguments and if it does, which
argument it will return.

The only advantage is that if coded in C, functools.identity may be
slightly faster. However, given that 

>>> dis(lambda x: x)
  1           0 LOAD_FAST                0 (x)
              3 RETURN_VALUE

I am not sure a C coded identity can significantly beat it.  The real
savings would come if python compiler could optimize away calls to identity
altogether, but nobody has explained how that could be done or why it would
be easier to optimize away identity that lambda x:x.



----------------------------------------------------------------------

Comment By: Memotype (twobitsprite)
Date: 2007-03-22 15:25

Message:
Logged In: YES 
user_id=1679533
Originator: NO

Also, map like functions start to look a bit ugly (of course, this is
overly simplistic, but serves as an example):

def my_map(func, items):
    acc = []
    for item in items:
        if func == None:
            acc.append(item)
        else:
            acc.append(func(item))
    return acc

which has the obvious overhead of a test for every iteration. Of course,
you could just make two versions of the for loop, one which applies <func>
and one which doesn't, but that immetiately violates once-and-only-once and
also forces the function to be overly concerned with what it is passed.

this is _much_ cleaner:

def my_map2(func, items):
    return [func(i) for i in items]

and the user of the function can simply pass <identity> as <func> and this
keeps the details of what to do with the items outside of the function.

Without identity, whenever I want to allow the user to inject code to
modify data, I have to account for the fact that they might want to do
nothing with it, and so I have to test for None constantly. This is simply
bad practice and, IMHO, violates everything python stands for, which is
elegance, simplicity and "batteries included", right?

If you want I patch, I can try to provide one; however I have very minimal
knowledge of how the vm works, and I've only looked at the code a couple of
times. In fact, I might even go ahead and do that, it can't be that
difficult.

----------------------------------------------------------------------

Comment By: Memotype (twobitsprite)
Date: 2007-03-22 15:06

Message:
Logged In: YES 
user_id=1679533
Originator: NO

It still feels like an ugly hack for something so simple. I can't believe
there is such resistance for something as trivial as a function which
returns it's first argument.

----------------------------------------------------------------------

Comment By: Josiah Carlson (josiahcarlson)
Date: 2007-03-22 13:49

Message:
Logged In: YES 
user_id=341410
Originator: NO

twobitsprite: your use-case is different from that of others.  While you
could use an identity function for your purposes, a lambda would work just
fine.  Regardless, there is call overhead, which can only be reduced by not
performing a call at all.

In terms of an identity function versus tuple creation and indexing as per
belopolsky's suggestion...

>>> timeit.Timer("x(1+2,3+4)", "x = lambda *args:args[-1]").timeit()
0.99381552025397468
>>> timeit.Timer("(1+2,3+4)[-1]", "").timeit()
0.49153579156927663

Tuple is faster.  Just use a tuple.

----------------------------------------------------------------------

Comment By: Memotype (twobitsprite)
Date: 2007-03-22 09:06

Message:
Logged In: YES 
user_id=1679533
Originator: NO

I also would like to have a built-in identity function (in fact, I found
this by googling "python identity function"). My use-case is a bit
different. I ofter find myself wanting to simply specify a function for be
used in a loop, something like:

def f(items):
    if something:
        wrapper = int
    else:
        wrapper = identity

    for item in items:
        yield wrapper(item)

of course, usually it's a bit more complex than that, but you get the
idea... and I supposed its actually more like the previous use-case than I
thought.

I realize I could just use "lambda x: x", but I feel that comes with an
unnecessary performance impact for something so trivial. I don't know how
much python does to compile built-in functions, but I imagine that the
identity function can be mostly optimized out at compile time if it were
built-in.

Just my two-cents.


----------------------------------------------------------------------

Comment By: Alexander Belopolsky (belopolsky)
Date: 2007-03-19 15:57

Message:
Logged In: YES 
user_id=835142
Originator: NO

I have just realized that the requested functionality is known in C as the
comma operator.  I often find myself writing "return Py_INCREF(o),o;" in my
C code, but I cannot really defend that idiom against "Py_INCREF(o); return
o;" alternative.  My personal reason is entirely C-specific, if  followed
an if(), the first form does not require curly braces.

In any case, comma operator can be emulated in python as

exp1,expr2,expr3   ->  (exp1,expr2,expr3)[-1]


Since multi-argument "identity" is likely to be rejected, my proposal to
alter the order of arguments is moot.  My other suggestion that with
identity, map(None, ..) should be deprecated in favor of map(identity, ..)
is probably an arument against the identity proposal now.  

----------------------------------------------------------------------

Comment By: Collin Winter (collinwinter)
Date: 2007-03-19 15:05

Message:
Logged In: YES 
user_id=1344176
Originator: NO

I can see adding the 1-argument form to operator or functools (as it's
useful in functional programming), but the 2-argument form you've suggested
is right out. If you really feel the need to torture a "for" loop into a
genexp/listcomp like that,

foo = (x for x in bar if condition(x) and [memoize(x)])

does the same thing using today's capabilities.

----------------------------------------------------------------------

Comment By: Josiah Carlson (josiahcarlson)
Date: 2007-03-12 16:06

Message:
Logged In: YES 
user_id=341410
Originator: NO

Not all x line functions should be built into Python.  Further, Python's
standard syntax offers an infix operator that does the same thing (though
in slightly different order as described below, you can reorder with
minimal effort).

identity(X, Y) -> (Y and False) or X

Also, the only use-case that you are provided and that I can imagine, are
examples like you provide where one is changing state within a statement
(if, elif, while, etc.) or expression (generator, list comprehension,
conditional, etc.).

----------------------------------------------------------------------

Comment By: Alexander Belopolsky (belopolsky)
Date: 2007-03-05 09:21

Message:
Logged In: YES 
user_id=835142
Originator: NO

1. If this proposal is accepted, it will make sense to deprecate the use
of None as an identity function in map:

>>> map(None, range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

2. Some other languages have an dyadic identity function that returns the
*second* argument. 

For example, K has : primitive:

  identity:(:)
  identity[1;2]
2

The rationale in K is that it is useful in an ammed function that replaces
entries of an an array with a result of a dyadic function applied to the
old and the supplied value and it is natural to have old value first:

  @[1 2 3;1;-;20]
1 -18 3
  @[1 2 3;1;:;20]
1 20 3

This rationale does not apply to Python, but in the absence of other
reasons to choose the order of arguments, Python may as well follow the
precedent. Does anyone know a less exotic language that has a dyadic
identity?




----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=355470&aid=1673203&group_id=5470


More information about the Python-bugs-list mailing list