Python'da yerleşik bir kimlik işlevi var mı?


147

Hiçbir şey yapmayan bir işleve işaret etmek istiyorum:

def identity(*args)
    return args

benim kullanım durumum bunun gibi bir şey

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

Tabii ki, identityyukarıda tanımlananları kullanabilirim , ancak bir yerleşik kesinlikle daha hızlı çalışır (ve kendi başıma gelen hataları önler).

Görünüşe göre mapve kimlik için filterkullanın None, ancak bu onların uygulamalarına özgüdür.

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

6
Ne demek istiyorsun map and filter use None for the identity?
Matt Fenwick

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

6
Dönüş değerini kontrol edin. Args değişkeniniz (bu senaryoda) tek bir değer dizisi olacaktır, bu nedenle bildirimdeki yıldız işaretini atlayın veya geri dönmeden önce paketini açın.
Dirk

12
@GregHewgill: Ne yazık ki bu Python 3.x'te çalışmıyor.
Ethan Furman

6
@GregHewgill Benim hatam. Googling yaptıktan sonra bunu doktordan aldım. Ancak Python2.x belgesi her zaman önce gelir ...
rds

Yanıtlar:


101

Biraz daha araştırma yapmak, hiçbiri, bir özellik var istendi olan konuyla 1673203 Ve aralarından Raymond Hettinger olmayacak söyledi :

İnsanların kendi önemsiz geçişlerini yazmalarına ve imza ve zaman maliyetlerini düşünmelerine izin vermek daha iyidir.

Yani bunu yapmanın daha iyi bir yolu aslında (lambda işlevi adlandırmaktan kaçınır):

_ = lambda *args: args
  • avantaj: herhangi bir sayıda parametre alır
  • dezavantaj: sonuç, parametrelerin kutulu bir versiyonudur

VEYA

_ = lambda x: x
  • avantaj: parametrenin türünü değiştirmez
  • dezavantaj: tam olarak 1 konumsal parametre alır

13
Bunun bir kimlik işlevi olmadığını unutmayın.
Marcin

1
@Marcin Yorumlarınız için teşekkürler. Kimseyi yanıltmamak için ikisinin avantajlarını / dezavantajlarını ekledim. Ve şimdi, herhangi bir sayıda parametreyi kabul eden ve gerçek bir kimlik olan yerleşik bir işlev olması gerektiğine gerçekten inanıyorum :)
rds

7
Güzel cevap. Bununla birlikte, birden fazla parametre alırken gerçek bir kimlik işlevi ne döndürür?
Marcin

5
@Marcin: Ne de, sadece sorduğu soruyu takip ediyorum.
Ethan Furman

4
Evet teşekkürler, lambda x: xbir string parametresi için çalışan önemsiz bir kimlik fonksiyonum var. @Marcin keşke yapabilseydim lambda *args: *args:-)
rds

30

Https://en.wikipedia.org/wiki/Identity_function içinde tanımlandığı gibi bir kimlik işlevi, tek bir bağımsız değişken alır ve değiştirmeden döndürür:

def identity(x):
    return x

Ne derken için soruyorsunuz sen imzayı istediğiniz def identity(*args)olduğu değil bunu birden fazla argüman almak istiyor gibi, kesinlikle bir kimlik işlevi. Sorun değil, ama sonra Python fonksiyonları birden fazla sonuç döndürmediğinden bir problemle karşılaşırsınız, bu nedenle tüm bu argümanları tek bir dönüş değerine sıkıştırmanın bir yolunu bulmanız gerekir.

Python'da "çoklu değerleri" döndürmenin olağan yolu, değerlerin bir demetini döndürmektir - teknik olarak bu bir dönüş değeridir, ancak çoğu bağlamda sanki birden çok değermiş gibi kullanılabilir. Ama bunu burada yapmak demek

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

Ve sabitleme o burada çeşitli cevaplar göstermiştir olduğunca çabuk sorunu, diğer sorunları verir.

Özetle, Python'da tanımlanmış bir kimlik işlevi yoktur, çünkü:

  1. Biçimsel tanım (tek bir bağımsız değişken işlevi) o kadar kullanışlı değildir ve yazmak önemsizdir.
  2. Tanımın birden fazla argümana genişletilmesi genel olarak iyi tanımlanmış değildir ve kendi özel durumunuz için ihtiyaç duyduğunuz şekilde çalışan kendi versiyonunuzu tanımlamanız çok daha iyidir.

Kesin durumunuz için,

def dummy_gettext(message):
    return message

neredeyse kesinlikle istediğiniz şeydir - aynı çağırma kuralına ve dönüşüne gettext.gettextsahip olan, argümanını değiştirmeden döndüren ve ne yaptığını ve nerede kullanılması amaçlandığını açıklamak için açıkça adlandırılmış bir işlev . Burada performans çok önemli bir konu olsaydı çok şaşırırdım.


"Yanıtların da gösterdiği gibi, bu sorunu düzeltmek başka sorunlar verir" konusunda hangi yanıtlara başvurduğunuzu anlamıyorum. Spesifik olarak kullanmak yeterlidir id= lambda *args: args if len(args)>1 else args[0].
Maksimum

22

seninki iyi çalışacak. Parametrelerin sayısı sabitlendiğinde, bunun gibi anonim bir işlevi kullanabilirsiniz:

lambda x: x

8
Sen de varargs ile yapabilirsiniz: lambda *args: args. Gerçekten stilistik bir seçim.

İkinciyi daha çok seviyorum, çünkü herhangi bir sayıda argüman gerektiriyor.
RDS

5
@delnan @rds - *argssürümün farklı bir dönüş türü vardır, bu nedenle tek bağımsız değişkenli durum için bile eşdeğer değildirler.
Marcin

8
@delnan: Bunun stilistik bir seçim olduğunu söylediniz, bu da yanlış bir şekilde iki formun anlambiliminde hiçbir fark olmadığını ima ediyor.
Marcin

1
@Marcin: Bunu ima etsem talihsiz bir durum. Bu kadar basit işlevler arasında defve bunlar için seçim yapmayı kastettim lambda.

8

Python'da yerleşik bir kimlik işlevi yoktur. Haskell'in idişlevinin bir taklidi şöyle olacaktır:

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

Örnek kullanım:

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

Yana identityverilen argümanlar dönen başka birşey yapmaz, ben bir yerli uygulama olacağından daha yavaş olduğunu düşünmüyorum.


Kurulum tamamlandıktan sonra ne yaparsanız yapın, zaman alan çağrının kendisinin inşasıdır.
chepner

@chepner Ne demek istediğinizi daha ayrıntılı olarak açıklayabilir misiniz? Yerel bir işleve çağrı da inşa edilmelidir, değil mi? Bu yapı, yerel olmayan bir işleve çağrı oluşturmadan daha hızlı mı yapılır?
SergiyKolesnikov

1
Kullanıcı tanımlı bir işleve yapılan çağrı, en azından yerleşik bir işleve yapılan çağrı kadar pahalıdır ve muhtemelen daha da öyledir çünkü kullanıcı tanımlı işlevi bir kez çağırdığınızda, başka herhangi bir şey daha kullanıcı tanımlı veya yerleşik işlevlerde.
chepner

6

Hayır yok.

Unutmayın identity:

  1. lambda * args ile eşdeğerdir: args
  2. Bağımsız değişkenlerini kutulayacak - yani

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

Dolayısıyla, lambda arg: arggerçek bir kimlik işlevi istiyorsanız kullanmak isteyebilirsiniz.

Not: Bu örnek, yerleşik idişlevi (muhtemelen asla kullanmayacağınız) gölgeleyecektir.


1
İd'nin yerleşik bir işlev olduğunu ve bu kod parçacığının üzerine yazacağını unutmayın.
Arnie97

@ Arnie97 Fuarı! Unuttumid
Marcin

4

Hız önemli değilse, bu tüm durumları ele almalıdır:

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

Kullanım örnekleri:

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

Tek bağımsız değişkenli bir işlevin saplaması

gettext.gettext(OP'nin örnek kullanım durumu) tek bir argümanı kabul eder message,. Birinin bunun için bir saplamaya ihtiyacı varsa [message], message( def identity(*args): return args) yerine geri dönmek için bir neden yoktur . Böylece ikisi de

_ = lambda message: message

def _(message):
    return message

tam uydu.

... ancak yerleşik bir cihaz kesinlikle daha hızlı çalışır (ve benim yaptığım hataları önler).

Böylesine önemsiz bir durumdaki hatalar pek alakalı değildir. Önceden tanımlanmış türde bir argüman için, diyelim ki str, str()kendisini bir kimlik işlevi olarak kullanabiliriz ( dizge nedeniyle, nesne kimliğini korur, aşağıdaki idnota bakın) ve performansını lambda çözümüyle karşılaştırabiliriz:

$ 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

Mikro optimizasyon mümkündür. Örneğin, aşağıdaki Cython kodu:

test.pyx

cpdef str f(str message):
    return message

Sonra:

$ 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

Yerleşik nesne kimliği işlevi

Bir kimlik işlevini , CPython'daki bellek adresini bir nesnenin 'kimliğini' ( bu nesnenin değeri yerine o nesnenin değeri yerine benzersiz bir tanımlayıcı anlamına gelir) iddöndüren yerleşik işlevle karıştırmayın .==


% 40 hızlanma "buna değmez" mi? Kimliğin 10.000x10.000 piksellik bir görüntüde kanal başına bir kez çalışan bir işlev için "varsayılan filtre" olarak çalıştığı durumlarda (belki her gün değil ama kesinlikle nadir değildir), 25 ile 9 arasındaki fark budur. saniye yürütme süresi! Ne olursa olsun, Cython örneği için teşekkür ederim.
9999years

@ 9999years Kabul ediyorum. Değerlilik yorumunu kaldırdım. Ayrıca yanıtı geliştirdiğiniz için teşekkür ederiz. Sizinkinin üzerinde birkaç küçük değişiklik yaptım.
saaj

10.000x10.000 piksellik bir görüntünüz varsa, numpy gibi bir şey kullanarak vektörleştirilmiş işlemleri kullanmanızı şiddetle tavsiye ederim. Çok daha hızlı olacak, daha az bellek kullanacak ve cython kodu yazmayı gerektirmeyecek.
anthonybell

-2

Konu oldukça eski. Ama yine de bunu göndermek istedim.

Hem argümanlar hem de nesneler için bir kimlik yöntemi oluşturmak mümkündür. Aşağıdaki örnekte, ObjOut, ObjIn için bir kimliktir. Yukarıdaki diğer tüm örnekler dict ** kwargs ile ilgilenmemiştir.

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}

bu bir referans gibi görünüyor, öyleyse, o zaman nereden geliyor?
Jeff Puckett

@JeffPuckettII Sorunuzu takip etmedim. Yeni nesnenin bir referans olup olmadığını mı soruyorsunuz?
Sud

Başka bir kaynaktan bir referans anlamına gelen "Bir kimlik oluşturmak mümkündür ..." için bir blok alıntı vurgusu kullandınız. Bunlar kendi sözlerinizse, alıntı olarak vurgulamamanızı öneririm. gerçekten önemli değil. ancak bu başka bir kaynaktan bir alıntı ise, o zaman ona bir referans eklemelisiniz.
Jeff Puckett

Orijinal soru map(identity, [1, 2, 3])geri dönüşlerini nasıl yanıtlarsınız [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())-> Sonuç: ([1, 2, 3],)
Sud
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.