Apakah ada fungsi identitas bawaan di python?

145

Saya ingin menunjukkan fungsi yang tidak melakukan apa-apa:

def identity(*args)
    return args

kasus penggunaan saya adalah sesuatu seperti ini

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

Tentu saja, saya bisa menggunakan yang identitydidefinisikan di atas, tetapi built-in pasti akan berjalan lebih cepat (dan menghindari bug yang diperkenalkan oleh saya sendiri).

Tampaknya, mapdan filterdigunakan Noneuntuk identitas, tetapi ini khusus untuk implementasi mereka.

>>> _=None
>>> _("hello")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
rds
sumber
6
Apa maksudmu map and filter use None for the identity?
Matt Fenwick
15
@MattFenwick:map(None, [1, 2, 3])
Greg Hewgill
6
Lihat nilai kembali. Variabel args Anda akan menjadi urutan (dalam skenario ini) satu nilai, jadi hilangkan tanda bintang dalam deklarasi, atau buka paket sebelum kembali.
Dirk
11
@GregHewgill: Sedihnya, itu tidak berfungsi di Python 3.x.
Ethan Furman
6
@GregHewgill Yang buruk. Saya mengambilnya dari dokter setelah googling. Tapi dokumen Python2.x selalu lebih dulu ...
rds

Jawaban:

99

Melakukan penelitian lebih lanjut, tidak ada, fitur ditanya dalam edisi 1673203 Dan dari Raymond Hettinger mengatakan tidak akan ada :

Lebih baik membiarkan orang menulis pass-through sepele mereka sendiri dan berpikir tentang tanda tangan dan biaya waktu.

Jadi cara yang lebih baik untuk melakukannya sebenarnya adalah (lambda menghindari penamaan fungsi):

_ = lambda *args: args
  • Keuntungan: mengambil sejumlah parameter
  • Kerugian: hasilnya adalah versi kotak parameter

ATAU

_ = lambda x: x
  • Keuntungan: tidak mengubah jenis parameter
  • Kerugian: mengambil tepat 1 parameter posisi
rds
sumber
13
Perhatikan bahwa ini bukan fungsi identitas.
Marcin
1
@ Marsin Terima kasih atas komentarnya. Saya telah menambahkan kelebihan / kekurangan dari keduanya agar tidak menyesatkan siapa pun. Dan sekarang, saya benar-benar percaya seharusnya ada fungsi builtin yang menerima sejumlah parameter dan merupakan identitas yang sebenarnya :)
rds
7
Jawaban bagus. Namun, apa yang akan kembali fungsi identitas sejati ketika mengambil beberapa parameter?
Marcin
5
@ Marsin: Baik, hanya dengan apa yang dia tanyakan dalam pertanyaannya.
Ethan Furman
4
Ya terima kasih, saya memiliki lambda x: xfungsi identitas sepele yang berfungsi untuk satu parameter string. @ Marscin Saya berharap saya bisa melakukan lambda *args: *args:-)
rds
28

Fungsi identitas, sebagaimana didefinisikan dalam https://en.wikipedia.org/wiki/Identity_function , mengambil satu argumen dan mengembalikannya tidak berubah:

def identity(x):
    return x

Apa yang Anda minta ketika Anda mengatakan Anda ingin tanda tangan def identity(*args)itu bukan semata-mata fungsi identitas, karena Anda ingin mengambil beberapa argumen. Itu bagus, tapi kemudian Anda menemukan masalah karena fungsi Python tidak mengembalikan banyak hasil, jadi Anda harus menemukan cara untuk menjejalkan semua argumen itu menjadi satu nilai balik.

Cara biasa mengembalikan "nilai ganda" dengan Python adalah dengan mengembalikan tupel nilai - secara teknis itu adalah satu nilai balik tetapi dapat digunakan dalam sebagian besar konteks seolah-olah nilai ganda. Tetapi melakukan itu di sini berarti Anda mendapatkannya

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

Dan memperbaiki yang masalah cepat memberikan isu-isu lain, seperti berbagai jawaban di sini telah menunjukkan.

Jadi, secara ringkas, tidak ada fungsi identitas yang didefinisikan dalam Python karena:

  1. Definisi formal (fungsi argumen tunggal) tidak begitu berguna, dan sepele untuk ditulis.
  2. Memperluas definisi ke beberapa argumen tidak secara umum didefinisikan dengan baik, dan Anda jauh lebih baik mendefinisikan versi Anda sendiri yang berfungsi seperti yang Anda inginkan untuk situasi khusus Anda.

Untuk kasus Anda yang tepat,

def dummy_gettext(message):
    return message

hampir pasti apa yang Anda inginkan - fungsi yang memiliki konvensi pemanggilan dan pengembalian yang sama dengan gettext.gettext, yang mengembalikan argumennya tidak berubah, dan secara jelas dinamai untuk menggambarkan apa yang dilakukannya dan di mana ia dimaksudkan untuk digunakan. Saya akan sangat terkejut jika kinerja menjadi pertimbangan penting di sini.

Paul Moore
sumber
Saya tidak melihat jawaban apa yang Anda rujuk dengan "memperbaiki masalah itu memberikan masalah lain, seperti yang ditunjukkan oleh jawaban". Secara khusus, itu sudah cukup untuk digunakan id= lambda *args: args if len(args)>1 else args[0].
Maks
21

milikmu akan bekerja dengan baik. Ketika jumlah parameter diperbaiki, Anda dapat menggunakan fungsi anonim seperti ini:

lambda x: x
tback
sumber
8
Anda dapat melakukan ini dengan varargs juga: lambda *args: args. Ini benar-benar pilihan gaya.
Saya suka yang kedua lebih baik, karena butuh sejumlah argumen.
rds
4
@delnan @rds - *argsversi ini memiliki tipe pengembalian yang berbeda, sehingga tidak setara bahkan untuk kasus argumen tunggal.
Marcin
8
@delnan: Anda mengatakan bahwa ini adalah pilihan gaya, yang secara tidak benar menyiratkan bahwa tidak ada perbedaan dalam semantik dari dua bentuk.
Marcin
1
@ Marsin: Sangat disayangkan jika saya menyiratkan itu. Maksud saya adalah pilihan antara defdan lambdauntuk fungsi-fungsi sederhana tersebut.
7

Tidak ada fungsi identitas bawaan di Python. Tiruan dari Haskell idfungsi akan menjadi:

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

Contoh penggunaan:

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

Karena identitytidak melakukan apa pun kecuali mengembalikan argumen yang diberikan, saya tidak berpikir bahwa itu lebih lambat daripada implementasi asli.

SergiyKolesnikov
sumber
Ini adalah konstruksi dari panggilan itu sendiri yang membutuhkan waktu, terlepas dari apa yang Anda lakukan setelah setup selesai.
chepner
@ chepner Bisakah Anda menjelaskan secara lebih rinci apa yang Anda maksud? Panggilan ke fungsi asli harus dibangun juga, bukan? Apakah konstruksi ini dilakukan lebih cepat daripada konstruksi panggilan ke fungsi non-asli?
SergiyKolesnikov
1
Panggilan ke fungsi yang ditentukan pengguna setidaknya sama mahalnya dengan panggilan ke fungsi bawaan, dan mungkin lebih dari itu karena begitu Anda memanggil fungsi yang ditentukan pengguna, apa pun itu mungkin akan meminta lebih banyak fungsi yang ditentukan atau dibangun oleh pengguna. dalam fungsi.
chepner
6

Tidak, tidak ada.

Perhatikan bahwa Anda identity:

  1. setara dengan lambda * args: args
  2. Akan kotak argumennya - yaitu

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

Jadi, Anda mungkin ingin menggunakan lambda arg: argjika Anda ingin fungsi identitas yang sebenarnya.

NB: Contoh ini akan membayangi idfungsi bawaan (yang mungkin tidak akan Anda gunakan).

Marcin
sumber
1
Perhatikan bahwa id adalah fungsi bawaan, dan cuplikan ini akan menimpanya.
Arnie97
@ Arnie97 Adil! Saya lupa tentangid
Marcin
4

Jika kecepatan tidak masalah, ini harus menangani semua kasus:

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

Contoh penggunaan:

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)
Binyan Hu
sumber
1

Rintisan fungsi argumen tunggal

gettext.gettext(contoh penggunaan OP kasus) menerima satu argumen message,. Jika seseorang membutuhkan sebuah rintisan untuk itu, tidak ada alasan untuk kembali [message]daripada message( def identity(*args): return args). Demikian keduanya

_ = lambda message: message

def _(message):
    return message

pas sekali.

... tapi built-in pasti akan berjalan lebih cepat (dan menghindari bug yang diperkenalkan oleh saya sendiri).

Bug dalam kasus sepele seperti itu hampir tidak relevan. Untuk argumen tipe standar, katakanlah str, kita dapat menggunakan str()dirinya sebagai fungsi identitas (karena string yang menggunakannya bahkan mempertahankan identitas objek, lihat idcatatan di bawah) dan membandingkan kinerjanya dengan solusi 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

Optimalisasi mikro dimungkinkan. Misalnya, kode Cython berikut :

test.pyx

cpdef str f(str message):
    return message

Kemudian:

$ 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

Fungsi identitas objek bawaan

Jangan bingung fungsi identitas dengan idfungsi bawaan yang mengembalikan 'identitas' suatu objek (yang berarti pengidentifikasi unik untuk objek tertentu daripada nilai objek itu, dibandingkan dengan ==operator), alamat memorinya dalam CPython.

saaj
sumber
Speedup 40% "sepertinya tidak terlalu sepadan"? Dalam kasus di mana identitas beroperasi sebagai "filter default" untuk fungsi yang berjalan, katakanlah, sekali per saluran pada gambar 10.000x10.000 piksel (mungkin tidak setiap hari tetapi tentu tidak jarang), itulah perbedaan antara 25 dan 9 detik waktu eksekusi! Apapun, terima kasih atas contoh Cython.
9999tahun
@ 9999tahun saya setuju. Saya telah menghapus komentar kelayakan. Juga terima kasih telah meningkatkan jawabannya. Saya telah membuat beberapa perubahan kecil di atas Anda.
Saaj
Jika Anda memiliki gambar 10.000x10.000 piksel maka saya akan sangat menyarankan menggunakan operasi vektor menggunakan sesuatu seperti numpy. Ini akan menjadi cara yang lebih cepat, menggunakan memori yang lebih sedikit, dan tidak perlu menulis kode cython.
anthonybell
-2

Utasnya cukup lama. Tapi tetap ingin memposting ini.

Dimungkinkan untuk membangun metode identitas untuk argumen dan objek. Dalam contoh di bawah ini, ObjOut adalah identitas untuk ObjIn. Semua contoh lain di atas belum membahas dikt ** 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}
Sud
sumber
ini terlihat seperti referensi, jika demikian, lalu dari mana asalnya?
Jeff Puckett
@ JeffPuckettII Saya tidak mengikuti pertanyaan Anda. Apakah Anda bertanya apakah objek baru adalah referensi?
Sud
Anda menggunakan highlight blockquote untuk "Dimungkinkan untuk membangun identitas ..." yang menyiratkan referensi dari sumber lain. Jika ini adalah kata-kata Anda sendiri, maka saya sarankan untuk tidak menyorotinya sebagai kutipan. benar-benar bukan masalah besar. tetapi jika ini adalah kutipan dari sumber lain, maka Anda harus menyertakan referensi untuk itu.
Jeff Puckett
Bagaimana Anda menjawab pertanyaan asli map(identity, [1, 2, 3])kembali [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())-> Hasil: ([1, 2, 3],)
Sud