Có một chức năng nhận dạng dựng sẵn trong python?


145

Tôi muốn chỉ đến một chức năng không có gì:

def identity(*args)
    return args

trường hợp sử dụng của tôi là một cái gì đó như thế này

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

Tất nhiên, tôi có thể sử dụng identityđịnh nghĩa ở trên, nhưng tích hợp sẵn chắc chắn sẽ chạy nhanh hơn (và tránh các lỗi do chính tôi giới thiệu).

Rõ ràng, mapfiltersử dụng Nonecho danh tính, nhưng điều này là cụ thể cho việc thực hiện của họ.

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

6
Bạn có ý nghĩa map and filter use None for the identitygì?
Matt Fenwick

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

6
Kiểm tra giá trị trả lại. Biến args của bạn sẽ là một chuỗi gồm (trong kịch bản này) một giá trị, do đó, bỏ qua dấu hoa thị trong khai báo hoặc giải nén nó trước khi trả về.
Dirk

11
@GregHewgill: Đáng buồn thay, điều đó không hoạt động trong Python 3.x.
Ethan Furman

6
@GregHewgill Xấu của tôi. Tôi lấy nó từ tài liệu sau khi googling. Nhưng tài liệu Python2.x luôn xuất hiện đầu tiên ...
rds

Câu trả lời:


99

Thực hiện thêm một số nghiên cứu, không có gì, một tính năng đã được hỏi trong số 1673203 Và từ Raymond Hettinger cho biết sẽ không có :

Tốt hơn là để mọi người viết những thông tin tầm thường của riêng họ và suy nghĩ về chữ ký và chi phí thời gian.

Vì vậy, một cách tốt hơn để làm điều đó là thực sự (một lambda tránh đặt tên hàm):

_ = lambda *args: args
  • lợi thế: có bất kỳ số lượng tham số
  • Nhược điểm: kết quả là một phiên bản đóng hộp của các tham số

HOẶC LÀ

_ = lambda x: x
  • lợi thế: không thay đổi loại tham số
  • nhược điểm: mất chính xác 1 tham số vị trí

13
Lưu ý rằng đây không phải là một chức năng nhận dạng.
Marcin

1
@Marcin Cảm ơn bạn đã nhận xét. Tôi đã thêm các ưu điểm / nhược điểm của cả hai để không đánh lừa bất cứ ai. Và bây giờ, tôi thực sự tin rằng nên có một hàm dựng sẵn chấp nhận bất kỳ số lượng tham số nào và là một danh tính thực sự :)
rds

7
Câu trả lời tốt đẹp. Tuy nhiên, chức năng nhận dạng thực sự sẽ trả về điều gì khi lấy nhiều tham số?
Marcin

5
@Marcin: Không, chỉ đi theo những gì anh ấy hỏi trong câu hỏi của anh ấy.
Ethan Furman

4
Vâng cảm ơn, tôi có một lambda x: xhàm nhận dạng tầm thường hoạt động cho một tham số chuỗi. @Marcin Tôi ước mình có thể làm được lambda *args: *args:-)
rds

28

Một hàm nhận dạng, như được định nghĩa trong https://en.wikipedia.org/wiki/Identity_function , nhận một đối số duy nhất và trả về nó không thay đổi:

def identity(x):
    return x

Những gì bạn đang yêu cầu khi bạn nói rằng bạn muốn chữ ký def identity(*args)không đúng chức năng nhận diện, như bạn muốn nó mất nhiều tranh cãi. Điều đó tốt, nhưng sau đó bạn gặp phải một vấn đề vì các hàm Python không trả về nhiều kết quả, vì vậy bạn phải tìm cách nhồi nhét tất cả các đối số đó vào một giá trị trả về.

Cách thông thường để trả về "nhiều giá trị" trong Python là trả về một tuple của các giá trị - về mặt kỹ thuật đó là một giá trị trả về nhưng nó có thể được sử dụng trong hầu hết các bối cảnh như thể nó là nhiều giá trị. Nhưng làm điều đó ở đây có nghĩa là bạn nhận được

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

Và khắc phục vấn đề đó nhanh chóng đưa ra các vấn đề khác, như các câu trả lời khác nhau ở đây đã chỉ ra.

Vì vậy, tóm lại, không có chức năng nhận dạng được xác định trong Python vì:

  1. Định nghĩa chính thức (một hàm đối số duy nhất) không hữu ích và không quan trọng để viết.
  2. Nói chung, việc mở rộng định nghĩa cho nhiều đối số không được xác định rõ ràng và tốt hơn hết là bạn nên xác định phiên bản của chính mình hoạt động theo cách bạn cần cho tình huống cụ thể của mình.

Đối với trường hợp chính xác của bạn,

def dummy_gettext(message):
    return message

gần như chắc chắn là những gì bạn muốn - một hàm có cùng quy ước gọi và trả về như gettext.gettext, nó trả về đối số của nó không thay đổi và được đặt tên rõ ràng để mô tả những gì nó làm và nơi nó dự định sẽ được sử dụng. Tôi sẽ khá sốc nếu hiệu suất là một sự cân nhắc quan trọng ở đây.


Tôi không thấy câu trả lời nào mà bạn đề cập với "việc khắc phục vấn đề đó đưa ra các vấn đề khác, như câu trả lời đã được hiển thị". Cụ thể, nó đủ để sử dụng id= lambda *args: args if len(args)>1 else args[0].
Tối đa

21

của bạn sẽ làm việc tốt. Khi số lượng tham số được khắc phục, bạn có thể sử dụng một hàm ẩn danh như thế này:

lambda x: x

8
Bạn cũng có thể làm điều này với varargs : lambda *args: args. Đó thực sự là một sự lựa chọn phong cách.

Tôi thích cái thứ hai tốt hơn, vì nó cần bất kỳ số lượng đối số.
rds

4
@delnan @rds - *argsphiên bản có kiểu trả về khác nhau, vì vậy chúng không tương đương ngay cả đối với trường hợp đối số đơn.
Marcin

8
@delnan: Bạn nói rằng đó là một sự lựa chọn phong cách, điều này ngụ ý không chính xác rằng không có sự khác biệt về ngữ nghĩa của hai hình thức.
Marcin

1
@Marcin: Thật không may nếu tôi ngụ ý điều đó. Tôi có nghĩa là sự lựa chọn giữa deflambdacho các chức năng đơn giản như vậy.

7

Không có chức năng nhận dạng tích hợp trong Python. Việc bắt chước chức năng của Haskellid sẽ là:

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

Ví dụ sử dụng:

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

identitykhông có gì ngoài việc trả về các đối số đã cho, tôi không nghĩ rằng nó chậm hơn so với triển khai gốc.


Đó là việc xây dựng cuộc gọi cần nhiều thời gian, bất kể bạn làm gì sau khi thiết lập đó hoàn tất.
chepner

@chepner Bạn có thể giải thích chi tiết hơn về ý của bạn không? Một cuộc gọi đến một chức năng bản địa cũng phải được xây dựng, phải không? Việc xây dựng này được thực hiện nhanh hơn so với xây dựng cuộc gọi đến một chức năng không phải là bản địa?
SergiyKolesnikov

1
Cuộc gọi đến chức năng do người dùng xác định ít nhất cũng tốn kém như cuộc gọi đến chức năng tích hợp sẵn và có lẽ còn hơn thế bởi vì một khi bạn đã gọi chức năng do người dùng xác định, bất kỳ điều gì khác có thể gọi thêm do người dùng xác định hoặc được xây dựng trong các chức năng.
chepner

6

Không, không có.

Lưu ý rằng identity:

  1. tương đương với lambda * args: args
  2. Sẽ đóng hộp các đối số của nó - tức là

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

Vì vậy, bạn có thể muốn sử dụng lambda arg: argnếu bạn muốn một chức năng nhận dạng thực sự.

NB: Ví dụ này sẽ tạo bóng cho idhàm tích hợp (mà bạn có thể sẽ không bao giờ sử dụng).


1
Lưu ý rằng id là một hàm tích hợp và đoạn mã này sẽ ghi đè lên nó.
Arnie97

@ Hội chợ Arnie97! Tôi quên mấtid
Marcin

4

Nếu tốc độ không thành vấn đề, điều này sẽ xử lý tất cả các trường hợp:

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

Ví dụ về việc sử dụng:

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

Sơ khai của một hàm đối số

gettext.gettext(trường hợp sử dụng ví dụ của OP) chấp nhận một đối số duy nhất , message. Nếu một người cần sơ khai cho nó, không có lý do gì để quay lại [message]thay vì message( def identity(*args): return args). Như vậy cả

_ = lambda message: message

def _(message):
    return message

phù hợp hoàn hảo.

... nhưng một tích hợp chắc chắn sẽ chạy nhanh hơn (và tránh các lỗi do chính tôi giới thiệu).

Lỗi trong một trường hợp tầm thường như vậy hầu như không liên quan. Đối với một đối số của loại được xác định trước, giả sử str, chúng ta có thể sử dụng str()chính nó như là một hàm nhận dạng (do chuỗi thực hiện nó thậm chí giữ lại danh tính đối tượng, xem idghi chú bên dưới) và so sánh hiệu suất của nó với giải pháp 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

Một tối ưu hóa vi mô là có thể. Ví dụ : mã Cython sau :

test.pyx

cpdef str f(str message):
    return message

Sau đó:

$ 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

Hàm nhận dạng đối tượng tích hợp

Đừng nhầm lẫn một chức năng nhận dạng với idchức năng tích hợp trả về 'danh tính' của một đối tượng (có nghĩa là một định danh duy nhất cho đối tượng cụ thể đó thay vì giá trị của đối tượng đó, so với ==toán tử), địa chỉ bộ nhớ của nó trong CPython.


Tăng tốc 40% "dường như không quá đáng"? Trong trường hợp nhận dạng hoạt động như một "bộ lọc mặc định" cho một chức năng chạy, giả sử, một lần trên mỗi kênh trên hình ảnh 10.000x10.000 pixel (có thể không phải mỗi ngày nhưng chắc chắn không phải là hiếm), đó là sự khác biệt giữa 25 và 9 giây của thời gian thực hiện! Bất kể, cảm ơn bạn cho ví dụ Cython.
9999

@ 9999 năm tôi đồng ý. Tôi đã xóa bình luận xứng đáng. Cũng cảm ơn vì đã cải thiện câu trả lời. Tôi đã thực hiện một vài thay đổi nhỏ trên đầu trang của bạn.
saaj

Nếu bạn có một hình ảnh 10.000x10.000 pixel thì tôi thực sự khuyên bạn nên sử dụng các hoạt động được vector hóa bằng cách sử dụng một cái gì đó như numpy. Nó sẽ nhanh hơn, sử dụng ít bộ nhớ hơn và không yêu cầu viết mã cython.
vang

-2

Các chủ đề khá cũ. Nhưng vẫn muốn đăng bài này.

Có thể xây dựng một phương thức nhận dạng cho cả đối số và đối tượng. Trong ví dụ dưới đây, ObjOut là một danh tính cho ObjIn. Tất cả các ví dụ khác ở trên đã không xử lý 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}

Điều này trông giống như một tài liệu tham khảo, nếu vậy, nó đến từ đâu?
Jeff Puckett

@JeffPuckettII Tôi không theo dõi câu hỏi của bạn. Bạn đang hỏi nếu đối tượng mới là một tài liệu tham khảo?
Sud

bạn đã sử dụng một điểm nổi bật blockquote cho "Có thể xây dựng danh tính ..." trong đó ngụ ý một tham chiếu từ một nguồn khác. Nếu đây là những từ của riêng bạn, thì tôi khuyên bạn không nên nhấn mạnh nó như một trích dẫn. thực sự không phải là một vấn đề lớn nhưng nếu đây là một trích dẫn từ một nguồn khác, thì bạn nên bao gồm một tham chiếu đến nó.
Jeff Puckett

Làm thế nào để bạn trả lời câu hỏi ban đầu map(identity, [1, 2, 3])lợi nhuận [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())-> Kết quả: ([1, 2, 3],)
Sud
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.