کلاسهای استثنای سفارشی ثابت در پایتون

x0 = “x0 =” x = “x =” x = “x =” x = “x =” x = “x =” x = “x =” x = “x =” x = “x =” x = “x =” x = “x =” x = “x =” x = “x”، “x__ =”، “” “” “” “” “”. “https://firebasestorage.googleapis.com/v0/b/hackernoon-app.appspot.com/o/images٪2FIzBcwKW22YbKX9ISDhGhrR1eFst1-hc03y3u.jpeg؟alt=media&token=1bc674e1-5f807-4” 7b7804 “2” alt58080-4 “a2” 7 “c74” 7 “c7b7b7b” 7 “c7bb07b0bb0aaaaaaaaaaaaaaaaaaaaaaaaa” عکس نمایه نویسنده “>

transifextransifex

# 1 بستر محلی سازی توسعه دهندگان.

استفاده از موارد استثنا در پایتون معمول است و همچنین تعریف موارد خاص خود نیز معمول است. با این حال ، من روش های رقابتی برای انجام این کار در پروژه های مختلف دیده ام. این ناسازگاری ناشی از موارد استثنایی است که به راحتی می توان آن را زیر طبقه و گسترش داد ، اما همچنین چیزی است که می تواند به راحتی نمونه سازی شود و در شکل پایه خود استفاده شود.

در اینجا یک روش معمول برای تعریف سفارشی استثنائات در پایتون:

class MyException < span> (استثنا) : def __init__ (self، msg) : self.msg = msg امتحان کنید : افزایش MyException ( “مشکلی پیش آمد” )
به جز MyException به عنوان e: print (e) # <<< مشکلی پیش آمد print (repr (e)) # <<< MyException ("مشکلی پیش آمد")

به طور کلی ، به نظر می رسد این خوب کار می کند. در واقع ، “بهتر از آنچه که باید” کار می کند.
به نوعی ، پایتون می داند که چگونه روش و rep را به درستی اجرا کند ، حتی اگر ما هیچ کدی برای آنها ننوشتیم.

بنابراین آیا این روش مشکلی دارد؟ بیایید کمی متفاوت را امتحان کنیم:

# همان روش قبلی
class MyException (استثنا) : def __init__ (self، msg) : self.msg = msg امتحان کنید : # اکنون ما از یک استدلال کلمه کلیدی استفاده می کنیم افزایش MyException (msg = “مشکلی پیش آمد” )
به جز MyException به عنوان e: print (e) # <<< print (repr (e)) # <<< MyException ()

اوه نه! به نظر می رسد که ما روش ها و repr را شکستیم. چگونه این اتفاق افتاد؟

اگرچه هیچ چیز مانع اختصاص دادن ویژگی به یک شی استثنا (قسمت self.msg = msg) نمی شود ، اما در قلب استثنای جایگاه خاصی برای سازنده وجود دارد آرگومان ها:

e = Exception ( 1 ، 2 ، 3 ) e .__ dict__ # <<< {}
e.args # <<< (1 ، 2 ، 3)
str (e) # <<< "(1 ، 2 ، 3)"
repr (e) # <<< "استثنا (1 ، 2 ، 3)" e.a = ‘b’
e .__ dict__ # <<< {'a': 'b'}
e.args # <<< (1 ، 2 ، 3)
str (e) # <<< "(1 ، 2 ، 3)"
repr (e) # <<< "استثنا (1 ، 2 ، 3)"

اما اینقدر برای استدلال های کلمات کلیدی نیست: < div class = "code-container" readability = "12"> e = Exception ( 1 ، b = 2 )
# <<< --------------------------------------------- --------------------------------
# <<< TypeError Traceback (آخرین تماس آخر)
# <<< در
# <<< ----> 1 e = استثنا (1 ، b = 2)
# <<<
# <<< TypeError: Exception () هیچ استدلال برای کلمه کلیدی ندارد

وقتی روش خود را تعریف کردیم __init__ ، این امکان را برای پذیرش استدلال کلمه کلیدی msg ، هنگامی که آرگومانهای موقعیتی را رد کردیم در مقایسه با آرگومانهای کلمه کلیدی ، تفاوت بین اشیا resulting حاصل وجود داشت. به طور خلاصه ، به نظر می رسد که موارد زیر باید یکسان باشند ، اما اینگونه نیستند:

MyException ( “مشکلی پیش آمد” ) .args # <<< ("مشکلی پیش آمد")
MyException (msg = “مشکلی پیش آمد” ) .args # <<< ()

(گمان می کنم وجود داشته باشد نوعی “مقدماتی” در کلاس استثنا ، احتمالاً یک روش __new__ ، که آرگومان های موقعیتی را به ویژگی args ضبط می کند و سپس روش __init__ ما را فراخوانی می کند)

یک چیز ما برای رفع این ناسازگاری می توان روشهای “شکست” ما را اجرا کرد:

class MyException (استثنا) : def __init__ (self، msg) : self.msg = msg def __str__ (خود) : بازگشت self.msg def __repr__ (خود) : بازگشت f “MyException ( {self.msg} )” e = MyException (msg = “مشکلی پیش آمد” ) str (e) # <<< "مشکلی پیش آمد"
repr (e) # <<< MyException ("مشکلی پیش آمد")

با این حال ، این پیشنهاد من نیست. اول از همه ، خسته کننده است. اما من همچنین احساس می کنم که برخلاف “روح” چگونگی ساختار استثناهای پایتون است. شاید بعداً برخی از کدهای مدیریت استثنا ویژگی args را مورد بازرسی قرار دهد و انتظار داشته باشد اطلاعات مربوطه در آن موجود باشد.

آنچه من پیشنهاد می کنم موارد زیر است:

class MyException (استثنا) : def __init__ (self، msg) : super () .__ init __ (msg) prospy def msg (خود) : Return self.args [ 0 ]

به این ترتیب می توانید استثنا را با استدلال های موقعیتی یا کلمات کلیدی مقداردهی اولیه کنید و این کار باعث می شود رفتار مشابهی داشته باشید:

e = MyException (msg = “مشکلی پیش آمد” ) e .__ dict__ # <<< {}
e.args # <<< ("مشکلی پیش آمد")
e.msg # <<< "مشکلی پیش آمد"
str (e) # <<< "مشکلی پیش آمد"
repr (e) # <<< "MyException (" مشکلی پیش آمد ")"

با این حال ، اکنون نمی توانید را تغییر دهید msgstr “ویژگی / خاصیت.

e.msg = ” اتفاق دیگری افتاده است “
# <<< --------------------------------------------- --------------------------------
# <<< AttributeError Traceback (آخرین تماس آخرین)
# <<< در
# <<< ----> 1 e.msg = “اتفاق دیگری رخ داد”
# <<<
# <<< AttributeError: نمی توان ویژگی را تنظیم کرد

به طور کلی ، نمی دانم چرا اشیا exception استثنا باید قابل تغییر باشند ، اما اگر می خواهید آنها باشند ، من پیشنهاد می کنم این کار را از طریق خواص نیز انجام دهید:

کلاس MyException (استثنا) : def __init__ (self، msg) : super () .__ init __ (msg) prospy def msg (خود) : بازگشت self.args [ 0 ] @ msg.setter def msg (خود ، ارزش) : self.args = (مقدار ،) e = MyException (msg = “مشکلی پیش آمد” )
e.msg = “اتفاق دیگری رخ داد”
repr (e) # <<< "MyException (" Something something اشتباهی رخ داد ")"

با چندین استدلال (کلمه کلیدی) ، می توانید انجام دهید: < / p>

class MyException (استثنا) : def __init__ (self، msg، status_code = None) : super () .__ init __ (msg ، وضعیت_کد) def _set (خود ، موقعیت ، ارزش) : args = list (self.args) args [موقعیت] = مقدار self.args = تاپل (args) msg = خاصیت ( lambda self: self.args [ 0 ]) ، lambda self، value: self._set ( 0 ، value)) status_code = خاصیت ( lambda self: self.args [ 1 ]) ، lambda self، value: self._set ( 1 ، value))

این کمی بویلر است اما به طور کلی من فکر کنید ارزش آن را دارد که اطمینان حاصل کنید که اشیا
استثنا ثابت باقی می مانند. حدس می زنم با یک ابزار بهتر می توان کارها را انجام داد:

# utils.py
def _set (خود ، موقعیت ، ارزش) : args = list (self.args) args [موقعیت] = مقدار self.args = tuple (args) def exc_property (موقعیت) : بازگشت خاصیت ( lambda self: self.args [موقعیت] ، lambda خود ، ارزش: _ مجموعه (خود ، موقعیت ، ارزش)) # exception.py
از .utils import exc_property class MyException (استثنا) : def __init__ (self، msg، status_code = None) : super () .__ init __ (msg، status_code) msg = exc_property ( 0 ) status_code = exc_property ( 1 )

به این ترتیب روش و rep را بصورت رایگان پیاده سازی می کنید (اگر همچنان می توانید آنها را نادیده بگیرید) می خواهم) ، ویژگی args بدون توجه به اینکه شما استثنا خود را با استدلال های موقعیتی یا کلمات کلیدی مقداردهی اولیه می کنید ، رفتار یکسانی خواهد داشت و می توانید به ویژگی ها دسترسی داشته باشید گویا ویژگی های معمولی هستند که از طریق انتساب به خود تنظیم می شوند.

این به نظر می رسد مانند بسیاری از کارها ، اما:

  • همانطور که گفتم ، استثنائات به طور کلی دلیلی برای تغییرپذیری ندارند ، بنابراین شما نیازی نیست که تنظیم کنندگان را اجرا کنید
  • با استفاده از ترفند exc_property ، شما فقط یک بار کد مخفیانه و نامناسب را می نویسید ، زیر کلاس های استثنایی کوتاه و شیرین باقی می مانند

    این داستان توسط Konstantinos Bairaktaris ، مهندس ارشد نرم افزار تولید شده است در Transifex.