在 python 中是否有一个内置的标识函数?

我想指出一个什么都不做的函数:

def identity(*args)
return args

我的用例是这样的

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

当然,我可以使用上面定义的 identity,但是内置的运行速度肯定会更快(并且可以避免我自己引入的 bug)。

显然,mapfilter使用 None作为标识,但这是特定于它们的实现的。

>>> _=None
>>> _("hello")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
67136 次浏览

当参数的数量被修正时,你可以使用这样一个匿名函数:

lambda x: x

第1673203期Raymond Hettinger 说不会有的中有一个专题被问到:

最好让人们编写自己的微不足道的传递 考虑签名和时间成本。

因此,实际上更好的方法是(lambda 避免命名函数) :

_ = lambda *args: args
  • 优点: 接受任意数量的参数
  • 缺点: 结果是一个装箱的参数版本

或者

_ = lambda x: x
  • 不改变参数的类型
  • 缺点: 只有1个位置参数

不,没有。

注意你的 identity:

  1. 等效于 lambda * args: args
  2. 将盒子它的 Args-即。

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

帖子很老了,但还是想发这个。

可以同时为参数和对象构建标识方法。在下面的示例中,ObjecOut 是 ObjecIn 的标识。上面的所有其他例子都没有处理 dictkwargs。

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}

https://en.wikipedia.org/wiki/Identity_function中定义的标识函数接受一个参数,并将其不变地返回:

def identity(x):
return x

当您说想要签名 def identity(*args)时,您所要求的是严格来说 没有是一个标识函数,因为您想要它接受多个参数。这很好,但是之后您遇到了一个问题,因为 Python 函数不会返回多个结果,所以您必须找到一种方法,将所有这些参数填充到一个返回值中。

在 Python 中,返回“多个值”的通常方法是返回值的元组——从技术上讲,这是一个返回值,但它可以在大多数上下文中使用,就好像它是多个值一样。但是在这里这么做意味着

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

快速解决 那个问题会带来其他问题,正如这里的各种答案所显示的那样。

因此,总的来说,Python 中没有定义标识函数,因为:

  1. 形式上的定义(单个参数函数)没有多大用处,写起来也很简单。
  2. 一般来说,将定义扩展到多个参数并没有得到很好的定义,最好是定义自己的版本,按照特定情况下需要的方式工作。

为了你的案子,

def dummy_gettext(message):
return message

几乎可以肯定这就是您想要的——一个具有与 gettext.gettext相同的调用约定和返回值的函数,gettext.gettext不变地返回参数,并且明确地命名为描述它所做的事情以及打算在哪里使用它。如果性能是这里的关键考虑因素,我会非常震惊的。

单参数函数的存根

gettext.gettext (OP 的示例用例)接受单个参数 message。如果需要一个存根,没有理由返回 [message]而不是 message(def identity(*args): return args)。两者皆是

_ = lambda message: message


def _(message):
return message

非常合身。

... 但是内置的运行速度肯定会更快(并且可以避免我自己引入的 bug)。

在这种微不足道的情况下,bug 几乎没有关系。对于预定义类型的参数,比如 str,我们可以使用 str()本身作为标识函数(因为 字符串实习它甚至保留了对象标识,参见下面的 id说明) ,并将其性能与 lambda 解决方案进行比较:

$ 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

微观优化是可能的。例如,下面的 Cython代码:

Test.pyx

cpdef str f(str message):
return message

然后:

$ 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

内置对象标识函数

不要把标识函数和返回 物体的“身份”(与 ==操作符相比,返回特定对象的唯一标识符而不是该对象的值)的 id内置函数混为一谈,后者是它在 CPython 中的内存地址。

Python 中没有内置的标识函数,对 Haskell 的 id函数的模仿如下:

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

示例用法:

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

因为 identity除了返回给定的参数之外什么也不做,所以我不认为它比本机实现慢。

如果速度不重要,这应该能够处理所有情况:

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

使用例子:

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)

补充所有的答案:

注意,Python stdlib 中有一个隐式约定,其中 HOF 将它的 key参数函数默认为标识函数,从而解释 None

例如: sortedheapq.mergemaxmin等。

因此,考虑您的 HOF 期望 key遵循相同的模式并不是一个坏主意。

换句话说:

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

(这是完全正确的)

你可以写:

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

如果你愿意的话。

这个话题有很多好的答案和讨论。我只是想指出,在 OP 的情况下,在标识函数中只有一个参数,从编译的角度来说,使用 lambda 或定义函数(在这种情况下,您可能应该定义函数来保持 PEP8兼容性)并不重要。字节码在功能上是相同的:

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

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