Pythonに組み込みのID関数はありますか?


145

何もしない関数を指摘したいと思います。

def identity(*args)
    return args

私のユースケースはこのようなものです

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

もちろん、identity上記の定義を使用することもできますが、組み込みの方が確かに高速に実行されます(そして、自分で導入したバグを回避します)。

どうやら、mapそしてアイデンティティのためにfilter使用します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ですか?
Matt Fenwick、2012年

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

6
戻り値を確認してください。args変数は(このシナリオでは)1つの値のシーケンスになるため、宣言でアスタリスクを省略するか、返す前にアンパックします。
Dirk

11
@GregHewgill:残念ながら、これはPython 3.xでは機能しません。
イーサンファーマン2012年

6
@GregHewgill悪い。グーグルした後、私はそれをドキュメントから取り出しました。しかしPython2.xドキュメントは常に...最初に来る
RDS

回答:


99

いくつかのより多くの研究をやって、何も存在しない、機能がに頼まれた問題1673203からレイモンド・ヘッティンガーがないだろうと述べました

自分で簡単なパススルーを書いて、署名と時間のコストについて考えさせてください。

したがって、それを行うためのより良い方法は実際にあります(ラムダは関数に名前を付けることを避けます):

_ = lambda *args: args
  • 利点:任意の数のパラメーターを取ります
  • 欠点:結果はパラメーターのボックスバージョンです

または

_ = lambda x: x
  • 利点:パラメータのタイプを変更しない
  • 欠点:位置パラメーターを1つだけとる

13
これは恒等関数ではないことに注意してください。
Marcin

1
@Marcin発言ありがとうございます。誰かを誤解させないために、2つの利点と欠点を追加しました。そして今、私は本当に、任意の数のパラメーターを受け入れ、真のアイデンティティである組み込み関数があったはずだと本当に信じています:)
rds

7
素敵な答え。ただし、複数のパラメーターを取得する場合、真のID関数は何を返しますか?
Marcin

5
@Marcin:どちらも、彼が彼の質問で尋ねたものをそのまま通ります。
イーサンファーマン、2012年

4
はい、ありがとうございますlambda x: x。1つの文字列パラメーターに対して機能する簡単なID関数があります。私は私が行うことがしたい@Marcin lambda *args: *args:-)
RDS

28

アイデンティティ関数は、https://en.wikipedia.org/wiki/Identity_functionで定義されているように、単一の引数を取り、変更せずに返します。

def identity(x):
    return x

署名def identity(*args)が必要だと言うときに要求するのは厳密には恒等関数ではなく、複数の引数を取る必要があるためです。それは問題ありませんが、Python関数は複数の結果を返さないため、問題が発生します。そのため、これらの引数をすべて1つの戻り値に詰め込む方法を見つける必要があります。

Pythonで「複数の値」を返す通常の方法は、値のタプルを返すことです。技術的には1つの戻り値ですが、ほとんどのコンテキストで、複数の値のように使用できます。しかし、ここでそれを行うと、

>>> 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値を持ち、引数を変更せずに返す関数であり、その機能と使用場所を説明するために明確に名前が付けられています。ここでパフォーマンスが重要な考慮事項である場合、私はかなりショックを受けます。


「その問題を修正すると、他の問題が発生するので、あなたがどの回答に言及しているかはわかりません。具体的には、を使用するだけで十分id= lambda *args: args if len(args)>1 else args[0]です。
最大

21

あなたはうまくいきます。パラメータの数が決まったら、次のような無名関数を使用できます。

lambda x: x

8
varargsでもこれを行うことができますlambda *args: args。それは本当にスタイルの選択です。

引数はいくつでも取ることができるので、私は2番目のほうが好きです。
rds

4
@delnan @rds- *argsバージョンの戻り値の型が異なるため、単一引数の場合でも同等ではありません。
Marcin、2012年

8
@delnan:あなたはそれが文体の選択だと言っていましたが、それは誤って2つの形式の意味に違いがないことを意味します。
Marcin、2012年

1
@Marcin:私がそれを暗示するのは残念です。私はそのような単純な関数の間defでの選択を意味しましたlambda

7

PythonにはID関数が組み込まれていません。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

@ 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以下の注を参照)、そのパフォーマンスをラムダソリューションと比較できます。

$ 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ピクセルの画像でチャネルごとに1回(おそらく毎日ではないが、確実に珍しくはない)実行される関数の「デフォルトフィルター」として動作する場合、それは25と9の違いです。実行時間の秒!とにかく、Cythonの例をありがとう。
9999

@ 9999年同意する。ふさわしいコメントを削除しました。また、答えを改善してくれてありがとう。私はあなたの上にいくつかの小さな変更を加えました。
saaj

10,000x10,000ピクセルの画像がある場合は、numpyなどのベクトル化演算を使用することを強くお勧めします。これは、はるかに高速で、使用するメモリが少なく、cythonコードを記述する必要がありません。
アンソニーベル

-2

スレッドはかなり古いです。しかし、まだこれを投稿したかった。

引数とオブジェクトの両方のIDメソッドを構築することが可能です。以下の例では、ObjOutはObjInのIDです。上記の他のすべての例は、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}

これは参照のように見えます、もしそうなら、それはどこから来たのですか?
Jeff Puckett

@JeffPuckettII私はあなたの質問に従わなかった。新しいオブジェクトが参照かどうか尋ねていますか?
Sud

別のソースからの参照を意味する「アイデンティティを構築することができます...」のブロック引用ハイライトを使用しました。これらがあなた自身の言葉である場合、私はそれを引用として強調表示しないことをお勧めします。たいしたことではありません。しかし、これが別のソースからの引用である場合は、それへの参照を含める必要があります。
Jeff Puckett

元の質問にどのように答え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]、)
Sud
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.