एक लाम्बा अभिव्यक्ति को परिभाषित करें जो एक अपवाद उठाती है


137

मैं एक लैम्ब्डा अभिव्यक्ति कैसे लिख सकता हूं जो इसके बराबर है:

def x():
    raise Exception()

निम्नलिखित की अनुमति नहीं है:

y = lambda : raise Exception()

2
तो आप ऐसा नहीं कर सकते। सामान्य कार्यों का उपयोग करें।
DrTyrsa

1
किसी अनाम फ़ंक्शन को नाम देने की बात क्या है?
जॉन ला रोय

2
@gnibbler आप फ़ंक्शन का उल्लेख करने के लिए नाम का उपयोग कर सकते हैं। REPL में y () का उपयोग करना आसान है (लंबोदर: 0) ()।
थॉमस जंग

तो फिर y=lambda...ओवर का क्या फायदा def y:?
जॉन ला रोय

@gnibbler कुछ संदर्भ: मैं एक फ़ंक्शन डिफ जी (एफ, ई) को परिभाषित करना चाहता था जो कि एक खुशहाल स्थिति में ई और कॉल करता है यदि कोई त्रुटि पाई गई थी। परिदृश्य पर निर्भर करता है कि ई एक अपवाद बढ़ा सकता है या कुछ वैध मूल्य लौटा सकता है। जी का उपयोग करने के लिए मैं जी (लैम्ब्डा x: x * 2, लैम्ब्डा ई: राइज़ ई) या वैकल्पिक रूप से जी (लैम्ब्डा एक्स: एक्स * 2, लैम्ब्डा ई: 0) लिखना चाहता था।
थॉमस जुंग

जवाबों:


169

पायथन को स्किन करने का एक से अधिक तरीका है:

y = lambda: (_ for _ in ()).throw(Exception('foobar'))

लंबोदर बयान स्वीकार करते हैं। चूंकि raise exएक बयान है, आप एक सामान्य प्रयोजन के लिए लिख सकते हैं:

def raise_(ex):
    raise ex

y = lambda: raise_(Exception('foobar'))

लेकिन अगर आपका लक्ष्य एक से बचना है def, तो यह स्पष्ट रूप से नहीं कटता है। हालाँकि, यह आपको अपवादों को सशर्त रूप से बढ़ाने की अनुमति देता है, जैसे:

y = lambda x: 2*x if x < 10 else raise_(Exception('foobar'))

वैकल्पिक रूप से आप एक नामित फ़ंक्शन को परिभाषित किए बिना एक अपवाद उठा सकते हैं। आप सभी की जरूरत है एक मजबूत पेट (और दिए गए कोड के लिए 2.x):

type(lambda:0)(type((lambda:0).func_code)(
  1,1,1,67,'|\0\0\202\1\0',(),(),('x',),'','',1,''),{}
)(Exception())

और एक पायथन 3 मजबूत पेट समाधान:

type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())

यदि आप अपवाद नहीं उठाते हैं, तो एक बहुत ही सरल उत्तर को इंगित करने के लिए धन्यवाद @WarrenSpencer y = lambda: 1/0:।


117
OMG यह कौन सी डार्क आर्ट है?
कोडकर्नलिस्ट

11
यदि आपको परवाह नहीं है कि किस प्रकार का अपवाद फेंका गया है, तो निम्नलिखित भी काम करता है lambda: 1 / 0:। आप बस एक नियमित अपवाद के बजाय एक ज़ीरोविजनशन के साथ फेंक दिए जाएंगे। इस बात को ध्यान में रखें कि यदि अपवाद को प्रचारित करने की अनुमति दी जाती है, तो किसी को आपके कोड को डिबग करने के लिए यह अजीब लग सकता है कि ZeroDivisionErrors का एक गुच्छा देखना शुरू करें।
वॉरेन स्पेंसर

महान समाधान @WarrenSpencer। अधिकांश कोड में बहुत अधिक शून्य विभाजन त्रुटियां नहीं होती हैं, इसलिए यह उतना ही विशिष्ट है जितना कि आप स्वयं प्रकार चुन सकते हैं।
jwg

2
y = 1/0सुपर स्मार्ट समाधान है यदि अपवाद प्रकार अप्रासंगिक है
साहेर अहवाल

3
क्या कोई हमसे बात कर सकता है कि वास्तव में 'डार्क-आर्ट / स्ट्रॉन्ग पेट' समाधान क्या है?
decvalts

56

कैसा रहेगा:

lambda x: exec('raise(Exception(x))')

12
यह काफी हैकिंग है लेकिन टेस्ट लिखने के लिए जहां आप फंक्शन को मॉक करना चाहते हैं, यह साफ-सुथरा है!
कन्नन एकनाथ

8
काम करता है लेकिन आपको ऐसा नहीं करना चाहिए।
अगस्तुर

1
यह मेरे लिए काम नहीं करता है, मुझे SyntaxErrorपायथन 2.7.11 पर मिलता है ।
निक स्वीटिंग

मुझे भी उपरोक्त त्रुटि (सिंटैक्सएयर) पायथन 2.7.5 पर मिल रही है
दिनेश

1
यह अजगर 3 के लिए विशिष्ट है, लेकिन मुझे नहीं लगता कि अजगर 2 की अनुमति देता है।
साहिर अहवाल

16

वास्तव में, एक तरीका है, लेकिन यह बहुत ही वंचित है।

आप compile()बिल्ट-इन फ़ंक्शन का उपयोग करके एक कोड ऑब्जेक्ट बना सकते हैं । यह आपको raiseकथन (या उस मामले के लिए किसी अन्य कथन) का उपयोग करने की अनुमति देता है , लेकिन यह एक और चुनौती उठाता है: कोड ऑब्जेक्ट को निष्पादित करना। सामान्य तरीका यह होगा execकि आप कथन का उपयोग करें , लेकिन यह आपको मूल समस्या की ओर ले जाता है, अर्थात आप उस lambda(या eval()उस मामले के लिए) कथनों को निष्पादित नहीं कर सकते ।

समाधान एक हैक है। किसी lambdaकथन के परिणाम जैसे कॉलबल्स में एक विशेषता होती है __code__, जिसे वास्तव में प्रतिस्थापित किया जा सकता है। इसलिए, यदि आप एक कॉल करने योग्य बनाते हैं और __code__ऊपर से कोड ऑब्जेक्ट के साथ इसका मूल्य बदल देते हैं, तो आपको कुछ ऐसा मिलता है, जिसे बयानों का उपयोग किए बिना मूल्यांकन किया जा सकता है। हालांकि, यह सब प्राप्त करना बहुत ही अस्पष्ट कोड का परिणाम है:

map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()

उपरोक्त निम्नलिखित है:

  • compile()कॉल एक कोड उद्देश्य यह है कि अपवाद को जन्म देती है बनाता है;

  • lambda: 0एक कॉल करने योग्य रिटर्न जो कुछ नहीं करता है, लेकिन मान 0 लौटाता है - इसका उपयोग बाद में उपरोक्त कोड ऑब्जेक्ट को निष्पादित करने के लिए किया जाता है;

  • lambda x, y, zएक समारोह है कि कॉल बनाता __setattr__शेष तर्क के साथ पहला तर्क की विधि, और पहला तर्क देता है! यह आवश्यक है, क्योंकि __setattr__स्वयं लौटता है None;

  • map()कॉल का परिणाम लेता है lambda: 0, और का उपयोग कर lambda x, y, zकी जगह यह __code__का परिणाम के साथ वस्तु compile()कॉल। इस नक्शे के संचालन का परिणाम एक प्रविष्टि के साथ एक सूची है lambda x, y, z, जिसके द्वारा लौटा गया है , यही कारण है कि हमें इसकी आवश्यकता है lambda: यदि हम __setattr__तुरंत उपयोग करेंगे , तो हम lambda: 0ऑब्जेक्ट का संदर्भ खो देंगे !

  • अंत में, map()कॉल द्वारा लौटाए गए सूची के पहले (और केवल) तत्व को निष्पादित किया जाता है, जिसके परिणामस्वरूप कोड ऑब्जेक्ट को बुलाया जा रहा है, अंततः वांछित अपवाद को बढ़ा रहा है।

यह काम करता है (पायथन 2.6 में परीक्षण किया गया), लेकिन यह निश्चित रूप से सुंदर नहीं है।

एक अंतिम नोट: यदि आपके पास typesमॉड्यूल तक पहुंच है (जिसे importआपके सामने कथन का उपयोग करने की आवश्यकता होगी eval), तो आप इस कोड को थोड़ा नीचे types.FunctionType()कर सकते हैं : आप एक फ़ंक्शन बना सकते हैं जो दिए गए कोड ऑब्जेक्ट को निष्पादित करेगा, इसलिए आप जीत गए lambda: 0इसकी __code__विशेषता के मान के साथ एक डमी फ़ंक्शन बनाने की हैक की आवश्यकता नहीं है।


15

लंबोदर रूपों के साथ बनाए गए कार्यों में कथन नहीं हो सकते


2
यह सच है, लेकिन यह वास्तव में इस सवाल का जवाब नहीं देता है; लैम्ब्डा अभिव्यक्ति से एक अपवाद उठाना आसान है (और इसे पकड़ना कभी-कभी बहुत ही कंट्रोल्ड ट्रिक का उपयोग करके प्राप्त किया जा सकता है, देखें stackoverflow.com/a/50916686/2560053 या stackoverflow.com/a/50961836-2560053 )।
थॉमस बरूचेल

15

यदि आप चाहते हैं कि एक लैम्ब्डा अभिव्यक्ति है जो एक मनमाना अपवाद उठाती है, तो आप इसे एक अवैध अभिव्यक्ति के साथ पूरा कर सकते हैं। उदाहरण के लिए, lambda x: [][0]खाली सूची में पहले तत्व तक पहुँचने का प्रयास करेंगे, जो एक IndexError को बढ़ाएगा।

कृपया ध्यान दें : यह एक हैक है, एक सुविधा नहीं है। किसी भी (गैर-कोड-गोल्फ) कोड में इसका उपयोग करें जिसे कोई अन्य मानव देख या उपयोग कर सकता है।


मेरे मामले में मुझे TypeError: <lambda>() takes exactly 1 positional argument (2 given):। क्या आप IndexError के बारे में सुनिश्चित हैं?
जोविक

4
हां। क्या आपने शायद गलत तर्क दिए हैं? यदि आपको एक लंबो फ़ंक्शन की आवश्यकता है जो किसी भी संख्या में तर्क ले सकता है, उपयोग करें lambda *x: [][0]। (मूल संस्करण केवल एक तर्क लेता है; बिना किसी तर्क के, उपयोग के लिए lambda : [][0]; दो के लिए, उपयोग lambda x,y: [][0]; आदि)
काइल स्ट्रैंड

3
मैंने इसे थोड़ा विस्तार दिया है lambda x: {}["I want to show this message. Called with: %s" % x] : KeyError: 'I want to show this message. Called with: foo'
प्रोडक्शंस

@ErlVolton चतुर! हालांकि एक बार की स्क्रिप्ट को छोड़कर कहीं भी इसका उपयोग करना एक भयानक विचार की तरह लगता है ...
काइल स्ट्रैंड

मैं एक प्रोजेक्ट के लिए यूनिट परीक्षणों में अस्थायी रूप से उपयोग कर रहा हूं, जहां मैंने अपने लकड़हारे का असली मजाक बनाने की जहमत नहीं उठाई। यदि आप किसी त्रुटि या क्रिटिकल को लॉग इन करने का प्रयास करते हैं तो यह बढ़ जाता है। इसलिए ... हां भयानक, हालांकि
संजीदा

10

मैं मार्सेलो कैंटोस द्वारा प्रदान किए गए उत्तर के अद्यतन 3 का विवरण देना चाहता हूं :

type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())

व्याख्या

lambda: 0builtins.functionवर्ग का एक उदाहरण है ।
type(lambda: 0)है builtins.functionवर्ग।
(lambda: 0).__code__एक codeवस्तु है।
एक codeवस्तु एक वस्तु है जो अन्य चीजों के बीच संकलित बायटेकोड रखती है। इसे यहां CPython https://github.com/python/cpython/blob/master/Include/code.h में परिभाषित किया गया है । इसके तरीके यहां लागू किए गए हैं https://github.com/python/cpython/blob/master/Objects/codeobject.c । हम कोड ऑब्जेक्ट पर मदद चला सकते हैं:

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__)कोड वर्ग है।
तो जब हम कहते हैं

type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')

हम निम्नलिखित तर्कों के साथ कोड ऑब्जेक्ट के निर्माता को बुला रहे हैं:

  • argcount = 1
  • kwonlyargcount = 0
  • nlocals = 1
  • stacksize = 1
  • झंडे = 67
  • codestring = b '| \ 0 \ 202 \ 1 \ 0'
  • स्थिरांक = ()
  • नाम = ()
  • varnames = ( 'एक्स',)
  • फ़ाइल नाम = ''
  • नाम = ''
  • firstlineno = 1
  • lnotab = ख ''

आप PyCodeObject https://github.com/python/cpython/blob/master/Include/code.h की परिभाषा में तर्क का क्या अर्थ है, इसके बारे में पढ़ सकते हैं । flagsतर्क के लिए 67 का मूल्य उदाहरण के लिए है CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE

सबसे अधिक आयात तर्क है codestringजिसमें अनुदेश opcodes शामिल हैं। आइए देखें कि उनका क्या मतलब है।

>>> import dis
>>> dis.dis(b'|\0\202\1\0')
          0 LOAD_FAST                0 (0)
          2 RAISE_VARARGS            1
          4 <0>

Https://cocs.python.org/3.8/library/dis.html#python-bytecode-instructions पर यहां से अफीम के दस्तावेज देखे जा सकते हैं । पहला बाइट के लिए ओपकोड है LOAD_FAST, दूसरा बाइट इसका तर्क है यानी 0।

LOAD_FAST(var_num)
    Pushes a reference to the local co_varnames[var_num] onto the stack.

इसलिए हम xस्टैक पर संदर्भ को आगे बढ़ाते हैं। varnamesकेवल 'एक्स' से युक्त स्ट्रिंग की एक सूची है। हम स्टैक को परिभाषित करने वाले फ़ंक्शन के एकमात्र तर्क को आगे बढ़ाएंगे।

अगले बाइट के लिए opcode है RAISE_VARARGSऔर अगला बाइट इसका तर्क है अर्थात 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)

टीओएस सबसे ऊपर वाला स्टैक है। चूंकि हमने xअपने फ़ंक्शन के पहले तर्क ( ) को स्टैक पर धकेल दिया है और argc1 है हम इसे उठाएंगे xयदि यह एक अपवाद उदाहरण है या इसका एक उदाहरण बनाते हैं xऔर अन्यथा इसे बढ़ाते हैं।

अंतिम बाइट अर्थात 0 का उपयोग नहीं किया जाता है। यह एक वैध ओपकोड नहीं है। यह भी नहीं हो सकता है।

कोड स्निपेट पर वापस जाना हम किसी भी तरह से कर रहे हैं:

type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())

हमने कोड ऑब्जेक्ट के निर्माता को बुलाया:

type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')

हम कोड ऑब्जेक्ट और फ़ंक्शन ऑब्जेक्ट के निर्माता के लिए एक खाली शब्दकोश पास करते हैं:

type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)

आइए एक फ़ंक्शन ऑब्जेक्ट पर मदद को देखने के लिए कॉल करें कि तर्कों का क्या मतलब है।

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

हम तब एक अपवाद के रूप में निर्मित फ़ंक्शन को एक तर्क के रूप में कहते हैं। नतीजतन हम एक लंबो फंक्शन कहते हैं जो एक अपवाद को जन्म देता है। चलो स्निपेट चलाते हैं और देखते हैं कि यह वास्तव में इरादा के अनुसार काम करता है।

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

सुधार

हमने देखा कि बाइटकोड का अंतिम बाइट बेकार है। आइए इस जटिल अभिव्यक्ति को सुस्पष्ट रूप से अव्यवस्थित न करें। चलिए उस बाइट को हटाते हैं। इसके अलावा, अगर हम थोड़ा गोल्फ चाहते हैं तो हम अपवाद की तात्कालिकता को छोड़ सकते हैं और इसके बजाय एक तर्क के रूप में अपवाद वर्ग को पारित कर सकते हैं। उन परिवर्तनों के परिणामस्वरूप निम्न कोड होंगे:

type(lambda: 0)(type((lambda: 0).__code__)(
    1,0,1,1,67,b'|\0\202\1',(),(),('x',),'','',1,b''),{}
)(Exception)

जब हम इसे चलाते हैं तो हमें पहले जैसा ही परिणाम मिलेगा। यह अभी छोटा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.