Python: ลองใช้คำสั่งในบรรทัดเดียว


97

มีวิธีใน python ในการเปลี่ยนลอง / ยกเว้นเป็นบรรทัดเดียวหรือไม่?

สิ่งที่ต้องการ...

b = 'some variable'
a = c | b #try statement goes here

bตัวแปรที่ประกาศอยู่ที่ไหนและcไม่ใช่ ... ดังนั้นcจะทำให้เกิดข้อผิดพลาดและaจะกลายเป็นb...

คำตอบ:


63

ไม่มีวิธีการบีบอัดไฟล์ try / exceptblock ลงในบรรทัดเดียวใน Python

นอกจากนี้ยังเป็นเรื่องเลวร้ายที่จะไม่ทราบว่ามีตัวแปรอยู่ใน Python หรือไม่เช่นเดียวกับที่คุณทำในภาษาไดนามิกอื่น ๆ วิธีที่ปลอดภัยกว่า (และรูปแบบที่แพร่หลาย) คือการตั้งค่าตัวแปรทั้งหมดเป็นบางสิ่ง หากอาจไม่ได้รับการตั้งค่าให้ตั้งค่าเป็นNoneอันดับแรก (หรือ0หรือ''หรือบางอย่างหากมีความเกี่ยวข้องมากกว่า)


ถ้าคุณ ทำกำหนดชื่อทั้งหมดที่คุณมีความสนใจในครั้งแรกที่คุณจะมีตัวเลือก

  • ตัวเลือกที่ดีที่สุดคือคำสั่ง if

    c = None
    b = [1, 2]
    
    if c is None:
        a = b
    else:
        a = c
    
  • อ็อพชันหนึ่งซับคือนิพจน์เงื่อนไข

    c = None
    b = [1, 2]
    a = c if c is not None else b
    
  • บางคนละเมิดพฤติกรรมการลัดวงจรของorการทำเช่นนี้ นี่เป็นข้อผิดพลาดได้ง่ายดังนั้นฉันจึงไม่เคยใช้มัน

    c = None
    b = [1, 2]
    a = c or b
    

    พิจารณากรณีต่อไปนี้:

    c = []
    b = [1, 2]
    a = c or b
    

    ในกรณีนี้aอาจจะควรจะเป็น[]แต่มันก็เป็น[1, 2]เพราะ[]เป็นเท็จในบริบทแบบบูล เนื่องจากมีค่ามากมายที่อาจเป็นเท็จฉันจึงไม่ใช้orเคล็ดลับ (นี่เป็นปัญหาเดียวกับที่ผู้คนพบเจอเมื่อพวกเขาพูดif foo:เมื่อพวกเขาหมายถึงif foo is not None:)


ขอบคุณ. ปัญหาคือจริงๆแล้วมันเป็นโมเดล django model.objects. รับแบบสอบถามที่ฉันพยายามทดสอบ . get ส่งคืนข้อผิดพลาดหากไม่พบข้อมูล ... มันไม่ส่งคืนไม่มี (ซึ่งทำให้ฉันรำคาญ)
Brant

@ แบรนต์โอเคสถานการณ์นั้นแตกต่างจากการตรวจสอบว่ามีการตั้งค่าตัวแปรหรือไม่ (ไม่มีการประกาศตัวแปรใน Python) รูปแบบทั่วไปใน Python คือต้องการเพิ่มข้อยกเว้นในการส่งคืนข้อผิดพลาดเป็นค่าซึ่งพวกเราหลายคนชอบมาก ต้องตรวจสอบโค้ดส่งคืนของการดำเนินการทุกครั้งและมีปัญหาในการติดตามข้อผิดพลาดหากฉันไม่เป็นสิ่งที่ฉันไม่พลาดเกี่ยวกับ C เมื่อเขียน Python ไม่ว่าในกรณีใดก็ตามแม้ว่าจะมีการพูดคุยกัน แต่ก็ไม่มีไวยากรณ์หนึ่งบรรทัดสำหรับ a try/ exceptblock โชคดีที่เส้นมีราคาถูกดังนั้นโซลูชัน 4 บรรทัดน่าจะเหมาะกับคุณ ;-)
Mike Graham

มันเป็นส่วนหนึ่งของ tuples ชุดใหญ่ภายใน dict ... ฉันแค่พยายามย่อสิ่งต่างๆให้สั้นลง
Brant

2
อย่าใช้getถ้าคุณไม่ต้องการข้อยกเว้น ใช้filterแทน
jcdyer

@MikeGraham คำตอบที่ดี - คำใบ้ (ลิงค์?) ทำไมการลัดวงจรจึงเกิดข้อผิดพลาดได้ง่ายจะดี
kratenko

85

นี่เป็นการแฮ็กอย่างมาก แต่ฉันเคยใช้ที่พรอมต์เมื่อฉันต้องการเขียนลำดับการดำเนินการสำหรับการดีบัก:

exec "try: some_problematic_thing()\nexcept: problem=sys.exc_info()"
print "The problem is %s" % problem[1]

ส่วนใหญ่แล้วฉันไม่ได้ใส่ใจกับข้อ จำกัด การไม่ลองบรรทัดเดียวเลย แต่เมื่อฉันเพิ่งทดลองและฉันต้องการให้ Readline เรียกคืนโค้ดทั้งหมดพร้อมกันในล่ามเชิงโต้ตอบดังนั้น ที่ฉันสามารถปรับได้เคล็ดลับเล็ก ๆ น้อย ๆ นี้มีประโยชน์

เพื่อวัตถุประสงค์ที่แท้จริงที่คุณกำลังพยายามที่จะบรรลุคุณอาจลองlocals().get('c', b); จะเป็นการดีกว่าถ้าใช้พจนานุกรมจริงแทนบริบทเฉพาะที่หรือเพียงแค่กำหนด c ให้กับ None ก่อนที่จะเรียกใช้สิ่งที่อาจหรือไม่ได้ตั้งค่าไว้


26
นี่ตอบคำถาม! :)
Steve Bennett

4
รักคำตอบนี้ยุ่งสุด ๆ แต่มีบรรทัดเดียวในแบบที่ฉันชอบ
Patrick Cook

นี่คือคำตอบ !! จะproblem[0]ส่งคืนฟังก์ชันนั้นส่งคืนอะไร?
SIslam

5
Exec เป็นกลิ่นรหัสและควรหลีกเลี่ยงเว้นแต่จะไม่มีผลอะไร หากรหัสบรรทัดเดียวมีความสำคัญมากสิ่งนี้จะได้ผล แต่คุณต้องถามตัวเองว่าทำไมบรรทัดเดียวจึงสำคัญ
Gewthen

4
ไม่ชัดเจนสำหรับการใช้งานจริง แต่เป็นสิ่งที่จำเป็นสำหรับเซสชันการดีบักที่น่าอึดอัดใจ
ThorSummoner


13

อีกวิธีหนึ่งคือการกำหนดผู้จัดการบริบท:

class trialContextManager:
    def __enter__(self): pass
    def __exit__(self, *args): return True
trial = trialContextManager()

จากนั้นใช้withคำสั่งเพื่อละเว้นข้อผิดพลาดในบรรทัดเดียว:

>>> with trial: a = 5      # will be executed normally
>>> with trial: a = 1 / 0  # will be not executed and no exception is raised
>>> print a
5

จะไม่มีข้อยกเว้นเกิดขึ้นในกรณีที่เกิดข้อผิดพลาดรันไทม์ มันเหมือนtry:ไม่มีexcept:.


1
นี่มันเยี่ยมมาก! เนื่องจากไม่มีการพยายาม / ยกเว้นอย่างชัดเจนคุณช่วยอธิบายสั้น ๆ ได้ไหมว่าตัวจัดการบริบทจัดการกับข้อผิดพลาดอย่างไร
Patrick

10

เวอร์ชันของคำตอบ poke53280 พร้อมข้อยกเว้นที่คาดไว้อย่าง จำกัด

def try_or(func, default=None, expected_exc=(Exception,)):
    try:
        return func()
    except expected_exc:
        return default

และสามารถใช้เป็น

In [2]: try_or(lambda: 1/2, default=float('nan'))
Out[2]: 0.5

In [3]: try_or(lambda: 1/0, default=float('nan'), expected_exc=(ArithmeticError,))
Out[3]: nan

In [4]: try_or(lambda: "1"/0, default=float('nan'), expected_exc=(ArithmeticError,))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
[your traceback here]
TypeError: unsupported operand type(s) for /: 'str' and 'int'

In [5]: try_or(lambda: "1"/0, default=float('nan'), expected_exc=(ArithmeticError, TypeError))
Out[5]: nan

เครื่องหมายจุลภาคใน "expected_exc = (Exception,)" คืออะไร คุณช่วยอธิบายได้ไหม?
ibilgen

1
@ibilgen เครื่องหมายจุลภาคแปลงแสดงออกไปยังtuple การเขียน(Exception)ก็เท่ากับทิ้งวงเล็บไว้ (Exception, )บอกล่ามว่านี่คือทูเปิล (คล้ายรายการ) ที่มีรายการเดียว ในตัวอย่างนี้ใช้ดังนั้นจึงexpected_excสามารถมีข้อยกเว้นได้มากกว่าหนึ่งข้อ
miile7


5

ปัญหาคือจริงๆแล้วมันเป็นโมเดล django model.objects. รับแบบสอบถามที่ฉันพยายามทดสอบ . get ส่งกลับข้อผิดพลาดหากไม่พบข้อมูล ... มันไม่ส่งคืนไม่มี (ซึ่งทำให้ฉันรำคาญ)

ใช้สิ่งนี้:

print("result:", try_or(lambda: model.objects.get(), '<n/a>'))

โดยที่ try_or เป็นฟังก์ชันยูทิลิตี้ที่คุณกำหนด:

def try_or(fn, default):
    try:
        return fn()
    except:
        return default

เลือกที่คุณสามารถ จำกัด ประเภทยกเว้นได้รับการยอมรับไปNameError, AttributeErrorฯลฯ


5

วิธีการใช้สองบรรทัด ไหวมั้ย?

>>> try: a = 3; b= 0; c = a / b
... except : print('not possible'); print('zero division error')
...
not possible
zero division error

4

คุณสามารถทำมันได้โดยการเข้าถึง Dict namespace ใช้vars(), locals()หรือglobals()แล้วแต่จำนวนใดจะเหมาะสมที่สุดสำหรับสถานการณ์ของคุณ

>>> b = 'some variable'
>>> a = vars().get('c', b)

3
สิ่งนี้ใช้ไม่ได้เหมือนกับการตรวจสอบว่าตัวแปรถูกตั้งค่าไว้หรือไม่ (แม้ว่าจะเป็นเช่นนั้นหากคุณสนใจในขอบเขตเฉพาะก็ตาม) นอกจากนี้ ewwwwwwww .....
Mike Graham

2

คุณบอกว่าคุณกำลังใช้ django หากเหมาะสมกับสิ่งที่คุณทำคุณอาจต้องการใช้:

my_instance, created = MyModel.objects.get_or_create()

createdจะเป็นจริงหรือเท็จ บางทีนี่อาจจะช่วยคุณได้


2

ทำงานบน Python3 ซึ่งได้รับแรงบันดาลใจจาก Walter Mundt

exec("try:some_problematic_thing()\nexcept:pass")

สำหรับหลายบรรทัดเป็นหนึ่งบรรทัด

exec("try:\n\tprint('FirstLineOk')\n\tsome_problematic_thing()\n\tprint('ThirdLineNotTriggerd')\nexcept:pass")

Ps: Exec ไม่ปลอดภัยที่จะใช้กับข้อมูลที่คุณไม่สามารถควบคุมได้


1

หากคุณต้องการจัดการข้อยกเว้นจริง:
(แก้ไขจากคำตอบของ poke53280)

>>> def try_or(fn, exceptions: dict = {}):
    try:
        return fn()
    except Exception as ei:
        for e in ei.__class__.__mro__[:-1]:
            if e in exceptions: return exceptions[e]()
        else:
            raise


>>> def context():
    return 1 + None

>>> try_or( context, {TypeError: lambda: print('TypeError exception')} )
TypeError exception
>>> 

โปรดทราบว่าหากไม่รองรับข้อยกเว้นจะเพิ่มขึ้นตามที่คาดไว้:

>>> try_or( context, {ValueError: lambda: print('ValueError exception')} )
Traceback (most recent call last):
  File "<pyshell#57>", line 1, in <module>
    try_or( context, {ValueError: lambda: print('ValueError exception')} )
  File "<pyshell#38>", line 3, in try_or
    return fn()
  File "<pyshell#56>", line 2, in context
    return 1 + None
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
>>> 

หากExceptionได้รับมันจะตรงกับสิ่งใดด้านล่าง
( BaseExceptionสูงกว่าจึงไม่ตรงกัน)

>>> try_or( context, {Exception: lambda: print('exception')} )
exception

0

นี่คือคำตอบในเวอร์ชันที่ง่ายกว่าจาก @surendra_ben

a = "apple"try: a.something_that_definitely_doesnt_exist
except: print("nope")

...

nope

0

ใช้withไวยากรณ์ในบรรทัดเดียว:

class OK(): __init__ = lambda self, *isok: setattr(self, 'isok', isok); __enter__ = lambda self: None; __exit__ = lambda self, exc_type, exc_value, traceback: (True if not self.isok or issubclass(exc_type, self.isok) else None) if exc_type else None

ละเว้นข้อผิดพลาดใด ๆ :

with OK(): 1/0

ละเว้นข้อผิดพลาดที่ระบุ:

with OK(ZeroDivisionError, NameError): 1/0
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.