python中有内置的标识函数吗?


145

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

def identity(*args)
    return args

我的用例是这样的

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

当然,我可以使用identity上面定义的方法,但是内置方法肯定会运行得更快(并避免我自己引入的错误)​​。

显然,mapfilter使用None的身份,但这是具体到它们的实现。

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

6
你是什么意思map and filter use None for the identity
马特·芬威克

15
@MattFenwick:map(None, [1, 2, 3])
Greg Hewgill

6
检查返回值。您的args变量将是一个序列(在这种情况下)一个值,因此可以省略声明中的星号,也可以在返回之前将其解压缩。
德克

11
@GregHewgill:遗憾的是,这在Python 3.x中不起作用。
伊桑·弗曼

6
@GregHewgill我不好。谷歌搜索后,我从文档中拿走了它。但是Python2.x文档始终排在第一位……
rds,2012年

Answers:


99

做更多的研究,没有,没有一个功能,问题1673203被问到。来自Raymond Hettinger表示不会

最好让人们自己编写琐碎的传递,并考虑签名和时间成本。

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

_ = lambda *args: args
  • 优点:可以使用任意数量的参数
  • 缺点:结果是参数的盒装版本

要么

_ = lambda x: x
  • 优点:不会改变参数的类型
  • 缺点:只能使用1个位置参数

13
请注意,这不是身份功能。
Marcin 2012年

1
@Marcin感谢您的发言。为了避免误导任何人,我添加了两者的优点/缺点。现在,我真的相信应该有一个内置函数可以接受任何数量的参数,并且是一个真实的标识:)
rds 2012年

7
好答案。但是,当采用多个参数时,真正的身份函数会返回什么?
Marcin 2012年

5
@Marcin:都没有,只是按照他在问题中的要求进行。
伊桑·弗曼

4
是的,谢谢,我有一个lambda x: x用于一个字符串参数的简单身份函数。@Marcin我希望我能做lambda *args: *args:-)
rds

28

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

几乎可以肯定是您想要的-一个具有相同的调用约定和return的函数,gettext.gettext该函数不变地返回其参数,并被明确命名以描述其作用以及打算在何处使用。如果性能在这里是至关重要的考虑因素,我会感到非常震惊。


我看不到您指的是什么答案,“如答案所示,解决该问题还会带来其他问题”。具体来说,使用就足够了id= lambda *args: args if len(args)>1 else args[0]
最多

21

你的会很好的。当参数数量固定后,您可以使用如下匿名函数:

lambda x: x

8
您也可以使用varargs :lambda *args: args。这确实是一种风格选择。

我喜欢第二种更好的方法,因为它需要许多参数。
rds'1

4
@delnan @rds- *args版本具有不同的返回类型,因此即使对于单参数情况,它们也不等效。
Marcin 2012年

8
@delnan:您说的是一种风格选择,这错误地暗示这两种形式的语义没有区别。
Marcin 2012年

1
@Marcin:不幸的是,我暗示了这一点。我的意思之间的选择deflambda这样的简单功能。

7

Python中没有内置的标识函数。Haskell id函数的模仿为:

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

用法示例:

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

由于identity除了返回给定的参数外什么都不做,因此我认为它不会比本机实现慢。


无论完成设置后要执行什么操作,呼叫本身的构建都会花费时间。
chepner

@chepner您能否详细解释您的意思?也必须构造对本机函数的调用,对吗?这种构造比非本机函数的调用构造更快吗?
SergiyKolesnikov

1
调用用户定义的函数至少与调用内置函数一样昂贵,并且可能更昂贵,因为一旦调用了用户定义的函数,其他任何事情都可能会调用更多的用户定义的或内置的函数。在功能上。
chepner

6

不,没有。

请注意,您的identity

  1. 等价于lambda * args:args
  2. 将装箱参数-即

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

因此,lambda arg: arg如果您需要真正的身份功能,则可能要使用。

注意:此示例将隐藏内置id函数(您可能永远不会使用)。


1
请注意,id是一个内置函数,此代码段将覆盖它。
Arnie97 '19

@ Arnie97博览会!我忘了id
Marcin

4

如果速度无关紧要,则应该处理所有情况:

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)

1

单参数函数的存根

gettext.gettext(OP的示例用例)接受单个参数message。如果一个人需要为它存根,没有理由退换货[message],而不是messagedef identity(*args): return args)。因此两者

_ = lambda message: message

def _(message):
    return message

伏贴。

...但是内置程序肯定会运行得更快(并避免我自己引入的错误)​​。

在这种琐碎的情况下,错误几乎无关紧要。对于预定义类型的参数,例如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中的内存地址。


40%的加速比“似乎不太值得”吗?如果身份充当运行该功能的“默认过滤器”,例如,在10,000x10,000像素的图像上每个通道运行一次(也许不是每天,但一定会很常见),则两者之间的差值为25和9执行时间秒!无论如何,谢谢您的Cython示例。
9999

@ 9999years我同意。我删除了有价值的评论。也感谢您改善答案。我在您的基础上做了一些小改动。
saaj

如果您有10,000x10,000像素的图像,那么我强烈建议您使用numpy之类的矢量化操作。这样会更快一些,使用更少的内存,并且不需要编写cython代码。
anthonybell '18

-2

线程很旧。但是仍然想发布这个。

可以为参数和对象建立身份方法。在下面的示例中,ObjOut是ObjIn的标识。上面所有其他示例都没有处理dict ** wargs。

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}

这看起来像是参考,如果是这样,那么它是从哪里来的?
杰夫·普基特

@JeffPuckettII我没有关注您的问题。您是在问新对象是否是参考?
2016年

您在“可能建立一个身份...”中使用了引号突出显示,这表示来自另一个来源的引用。如果这些是您自己的话,那么我建议您不要将其突出显示为报价。真的没什么大不了的。但是,如果这是来自其他来源的报价,则应包括对其的引用。
杰夫·普基特

您如何回答最初的问题map(identity, [1, 2, 3])退货[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())->结果:([[1,2,3],)
2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.