Làm cách nào tôi có thể viết biểu thức lambda tương đương với:
def x():
raise Exception()
Những điều sau đây không được phép:
y = lambda : raise Exception()
y=lambda...hơn def y:là gì?
Làm cách nào tôi có thể viết biểu thức lambda tương đương với:
def x():
raise Exception()
Những điều sau đây không được phép:
y = lambda : raise Exception()
y=lambda...hơn def y:là gì?
Câu trả lời:
Có nhiều hơn một cách để tạo một Python:
y = lambda: (_ for _ in ()).throw(Exception('foobar'))
Lambdas chấp nhận tuyên bố. Vì raise exlà một tuyên bố, bạn có thể viết một trình tăng mục đích chung:
def raise_(ex):
raise ex
y = lambda: raise_(Exception('foobar'))
Nhưng nếu mục tiêu của bạn là tránh a def, điều này rõ ràng là không cắt giảm. Tuy nhiên, nó cho phép bạn đưa ra các ngoại lệ một cách có điều kiện, ví dụ:
y = lambda x: 2*x if x < 10 else raise_(Exception('foobar'))
Ngoài ra, bạn có thể đưa ra một ngoại lệ mà không cần xác định chức năng được đặt tên. Tất cả bạn cần là một dạ dày mạnh mẽ (và 2.x cho mã đã cho):
type(lambda:0)(type((lambda:0).func_code)(
1,1,1,67,'|\0\0\202\1\0',(),(),('x',),'','',1,''),{}
)(Exception())
Và một giải pháp dạ dày mạnh python3 :
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Cảm ơn @WarrenSpencer đã chỉ ra một câu trả lời rất đơn giản nếu bạn không quan tâm ngoại lệ nào được nêu ra : y = lambda: 1/0.
lambda: 1 / 0. Bạn sẽ kết thúc với việc ZeroDivisionError bị ném thay vì một ngoại lệ thông thường. Hãy nhớ rằng nếu ngoại lệ được phép đưa ra, có vẻ lạ khi ai đó gỡ lỗi mã của bạn để bắt đầu nhìn thấy một loạt ZeroDivisionErrors.
y = 1/0là giải pháp siêu thông minh nếu loại Ngoại lệ không liên quan
Làm thế nào về:
lambda x: exec('raise(Exception(x))')
SyntaxErrortrên Python 2.7.11.
Trên thực tế, có một cách, nhưng nó rất giả tạo.
Bạn có thể tạo một đối tượng mã bằng hàm compile()dựng sẵn. Điều này cho phép bạn sử dụng raisecâu lệnh (hoặc bất kỳ câu lệnh nào khác, cho vấn đề đó), nhưng nó đặt ra một thách thức khác: thực thi đối tượng mã. Cách thông thường sẽ là sử dụng execcâu lệnh, nhưng điều đó dẫn bạn trở lại vấn đề ban đầu, cụ thể là bạn không thể thực thi các câu lệnh trong một lambda(hoặc một eval(), cho vấn đề đó).
Giải pháp là hack. Các hàm gọi như kết quả của một lambdacâu lệnh đều có một thuộc tính __code__, mà thực sự có thể được thay thế. Vì vậy, nếu bạn tạo một __code__giá trị có thể gọi được và thay thế giá trị của nó bằng đối tượng mã từ phía trên, bạn sẽ có được thứ gì đó có thể được đánh giá mà không cần sử dụng câu lệnh. Đạt được tất cả điều này, mặc dù, dẫn đến mã rất tối nghĩa:
map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Trên đây là như sau:
các compile()cuộc gọi tạo ra một đối tượng mã mà làm tăng ngoại lệ;
các lambda: 0lợi nhuận một callable mà không làm gì nhưng trả về giá trị 0 - điều này được sử dụng để thực hiện các đối tượng mã trên sau;
các lambda x, y, ztạo ra một chức năng mà các cuộc gọi __setattr__phương pháp của đối số đầu tiên với các đối số còn lại, và trả về đối số đầu tiên! Điều này là cần thiết, bởi vì __setattr__chính nó trở lại None;
các map()cuộc gọi có kết quả của lambda: 0, và sử dụng lambda x, y, zthay thế nó của __code__đối tượng với kết quả của các compile()cuộc gọi. Kết quả của hoạt động bản đồ này là một danh sách với một mục nhập, mục được trả về lambda x, y, z, đó là lý do tại sao chúng ta cần điều này lambda: nếu chúng ta sử dụng __setattr__ngay, chúng ta sẽ mất tham chiếu đến lambda: 0đối tượng!
cuối cùng, phần tử đầu tiên (và duy nhất) của danh sách được trả về bởi lệnh map()gọi được thực thi, dẫn đến đối tượng mã được gọi, cuối cùng đưa ra ngoại lệ mong muốn.
Nó hoạt động (được thử nghiệm trong Python 2.6), nhưng nó chắc chắn không đẹp.
Một lưu ý cuối cùng: nếu bạn có quyền truy cập vào typesmô-đun (sẽ yêu cầu sử dụng importcâu lệnh trước eval), thì bạn có thể rút ngắn mã này xuống một chút: sử dụng types.FunctionType()bạn có thể tạo một hàm sẽ thực thi đối tượng mã đã cho, vì vậy bạn đã thắng Không cần hack tạo hàm giả lambda: 0và thay thế giá trị __code__thuộc tính của nó .
Các hàm được tạo bằng biểu mẫu lambda không thể chứa các câu lệnh .
Nếu tất cả những gì bạn muốn là một biểu thức lambda đưa ra một ngoại lệ tùy ý, bạn có thể thực hiện điều này với một biểu thức bất hợp pháp. Chẳng hạn, lambda x: [][0]sẽ cố gắng truy cập phần tử đầu tiên trong một danh sách trống, điều này sẽ làm tăng IndexError.
XIN LƯU Ý : Đây là một hack, không phải là một tính năng. Không sử dụng mã này trong bất kỳ mã (không phải mã golf) nào mà người khác có thể nhìn thấy hoặc sử dụng.
TypeError: <lambda>() takes exactly 1 positional argument (2 given). Bạn có chắc chắn về IndexError?
lambda *x: [][0]. (Phiên bản gốc chỉ có một đối số; không có đối số, sử dụng lambda : [][0]; đối với hai, sử dụng lambda x,y: [][0]; v.v.)
lambda x: {}["I want to show this message. Called with: %s" % x] Sản xuất: KeyError: 'I want to show this message. Called with: foo'
Tôi muốn đưa ra lời giải thích về CẬP NHẬT 3 của câu trả lời được cung cấp bởi Marcelo Cantos:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
lambda: 0là một ví dụ của builtins.functionlớp.
type(lambda: 0)là builtins.functionlớp học
(lambda: 0).__code__là một codeđối tượng.
Một codeđối tượng là một đối tượng chứa mã byte được biên dịch trong số những thứ khác. Nó được định nghĩa ở đây trong CPython https://github.com/python/cpython/blob/master/Include/code.h . Các phương thức của nó được triển khai tại đây https://github.com/python/cpython/blob/master/Objects/codeobject.c . Chúng ta có thể chạy trợ giúp trên đối tượng mã:
Help on code object:
class code(object)
| code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,
| constants, names, varnames, filename, name, firstlineno,
| lnotab[, freevars[, cellvars]])
|
| Create a code object. Not for the faint of heart.
type((lambda: 0).__code__)là lớp mã.
Vì vậy, khi chúng ta nói
type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')
chúng ta đang gọi hàm tạo của đối tượng mã với các đối số sau:
Bạn có thể đọc về ý nghĩa của các đối số trong định nghĩa của PyCodeObject
https://github.com/python/cpython/blob/master/Include/code.h . Giá trị 67 choflags đối số là ví dụ CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE.
Đối số nhập khẩu nhiều nhất là đối số codestringchứa opcodes lệnh. Hãy xem ý nghĩa của chúng.
>>> import dis
>>> dis.dis(b'|\0\202\1\0')
0 LOAD_FAST 0 (0)
2 RAISE_VARARGS 1
4 <0>
Tài liệu về opcodes có thể được tìm thấy ở đây https://docs.python.org/3.8/l Library / dis.html.htmlpypy-bytecode-in thi
. Byte đầu tiên là opcode cho LOAD_FAST, byte thứ hai là đối số của nó tức là 0.
LOAD_FAST(var_num)
Pushes a reference to the local co_varnames[var_num] onto the stack.
Vì vậy, chúng tôi đẩy tham chiếu xlên ngăn xếp. Cácvarnames là danh sách các chuỗi chỉ chứa 'x'. Chúng ta sẽ đẩy đối số duy nhất của hàm mà chúng ta đang xác định vào ngăn xếp.
Byte tiếp theo là opcode cho RAISE_VARARGSvà byte tiếp theo là đối số của nó tức là 1.
RAISE_VARARGS(argc)
Raises an exception using one of the 3 forms of the raise statement, depending on the value of argc:
0: raise (re-raise previous exception)
1: raise TOS (raise exception instance or type at TOS)
2: raise TOS1 from TOS (raise exception instance or type at TOS1 with __cause__ set to TOS)
ĐKDV là đỉnh cao nhất. Vì chúng ta đã đẩy đối số đầu tiên ( x) của hàm của chúng ta lên ngăn xếp và argclà 1, chúng ta sẽ nâng lên
xnếu đó là một trường hợp ngoại lệ hoặc tạo một thể hiện xvà nâng nó theo cách khác.
Byte cuối cùng tức là 0 không được sử dụng. Nó không phải là một opcode hợp lệ. Nó cũng có thể không ở đó.
Quay trở lại đoạn mã chúng tôi đang cố gắng:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Chúng tôi đã gọi hàm tạo của đối tượng mã:
type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')
Chúng ta truyền đối tượng mã và một từ điển trống cho hàm tạo của đối tượng hàm:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)
Chúng ta hãy gọi trợ giúp về một đối tượng hàm để xem các đối số có nghĩa là gì.
Help on class function in module builtins:
class function(object)
| function(code, globals, name=None, argdefs=None, closure=None)
|
| Create a function object.
|
| code
| a code object
| globals
| the globals dictionary
| name
| a string that overrides the name from the code object
| argdefs
| a tuple that specifies the default argument values
| closure
| a tuple that supplies the bindings for free variables
Sau đó, chúng ta gọi hàm được xây dựng đi qua một thể hiện Ngoại lệ làm đối số. Do đó, chúng tôi đã gọi một hàm lambda làm tăng ngoại lệ. Hãy chạy đoạn trích và thấy rằng nó thực sự hoạt động như dự định.
>>> type(lambda: 0)(type((lambda: 0).__code__)(
... 1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
... )(Exception())
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "", line 1, in
Exception
Chúng tôi đã thấy rằng byte cuối cùng của mã byte là vô dụng. Chúng ta đừng lộn xộn biểu hiện phức tạp này. Hãy xóa byte đó. Ngoài ra, nếu chúng ta muốn chơi gôn một chút, chúng ta có thể bỏ qua phần khởi tạo của Ngoại lệ và thay vào đó vượt qua lớp Ngoại lệ làm đối số. Những thay đổi đó sẽ dẫn đến mã sau đây:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1',(),(),('x',),'','',1,b''),{}
)(Exception)
Khi chúng tôi chạy nó, chúng tôi sẽ nhận được kết quả như trước đây. Nó chỉ ngắn hơn.