我不是要求对这种哲学提出个人的"宗教"观点,而是要更具技术性.
我理解这句话是几个试金石中的一个,看你的代码是否是"pythonic".但对我来说,pythonic意味着干净,简单和直观,没有加载异常处理程序以进行错误的编码.
所以,实际的例子.我定义了一个类:
class foo(object):
bar = None
def __init__(self):
# a million lines of code
self.bar = "Spike is my favorite vampire."
# a million more lines of code
Run Code Online (Sandbox Code Playgroud)
现在,来自程序背景,在另一个函数中,我想这样做:
if foo.bar:
# do stuff
Run Code Online (Sandbox Code Playgroud)
如果我不耐烦并且没有做初始的foo = None,我会得到一个属性异常.所以,"请求宽恕不许可"表明我应该这样做吗?
try:
if foo.bar:
# do stuff
except:
# this runs because my other code was sloppy?
Run Code Online (Sandbox Code Playgroud)
为什么我在try块中添加额外的逻辑会更好,这样我可以让我的类定义更加模糊?为什么不先定义所有内容,然后明确授予权限?
(不要打扰我使用try/except块......我在任何地方都使用它们.我不认为用它们来捕捉我自己的错误是正确的,因为我不是一个彻底的程序员.)
或者......我是否完全误解了"请求原谅"的口头禅?
Gil*_*il' 59
"请求宽恕,而不是许可"反对两种编程风格."请求许可"是这样的:
if can_do_operation():
perform_operation()
else:
handle_error_case()
Run Code Online (Sandbox Code Playgroud)
"请求宽恕"是这样的:
try:
perform_operation()
except Unable_to_perform:
handle_error_case()
Run Code Online (Sandbox Code Playgroud)
在这种情况下,预计尝试执行操作可能会失败,并且您必须以某种方式处理无法进行操作的情况.例如,如果操作正在访问文件,则该文件可能不存在.
要求宽恕最好的原因主要有两个:
can_do_operation()
的时间和时间之间发生变化.你跑perform_operation()
.所以你无论如何都要处理错误.请求 - 原谅情况的共同点是您正在尝试执行操作,并且您知道操作可能会失败.
当你写作时foo.bar
,不存在bar
通常不被认为是对象的失败foo
.它通常是程序员错误:尝试以不是为其设计的方式使用对象.Python中程序员错误的结果是一个未处理的异常(如果你很幸运:当然,有些程序员错误无法自动检测到).因此,如果bar
是对象的可选部分,处理此问题的常规方法是使bar
字段初始化为None
,如果存在可选部分,则设置为其他值.要测试是否bar
存在,请写入
if foo.bar is not None:
handle_optional_part(foo.bar)
else:
default_handling()
Run Code Online (Sandbox Code Playgroud)
只有在解释为布尔值时才能将其缩写if foo.bar is not None:
为真 - 如果可能是0 ,或者任何其他具有错误真值的对象,则需要.如果您正在测试可选部件(而不是在和之间进行测试),那么它也更清晰.if foo.bar:
bar
bar
[]
{}
is not None
True
False
此时你可能会问:为什么不省略初始化bar
它何时不存在,并测试它的存在hasattr
或用AttributeError
处理程序捕获它?因为您的代码在两种情况下才有意义:
bar
字段;bar
字段,表示您认为它意味着什么.因此,在编写或决定使用该对象时,您需要确保它没有具有bar
不同含义的字段.如果你需要使用一些没有bar
字段的不同对象,那可能不是你需要适应的唯一东西,所以你可能想要创建一个派生类或将对象封装在另一个中.
Jon*_*ice 56
经典的"请求宽恕不允许"示例是从dict
可能不存在的值中访问值.例如:
names = { 'joe': 'Joe Nathan', 'jo': 'Jo Mama', 'joy': 'Joy Full' }
name = 'hikaru'
try:
print names[name]
except KeyError:
print "Sorry, don't know this '{}' person".format(name)
Run Code Online (Sandbox Code Playgroud)
这里KeyError
列出了可能发生的异常(),因此您不会对可能发生的每个错误请求宽恕,而只会请求自然发生的错误.为了比较,"首先询问权限"方法可能如下所示:
if name in names:
print names[name]
else:
print "Sorry, don't know this '{}' person".format(name)
Run Code Online (Sandbox Code Playgroud)
要么
real_name = names.get(name, None)
if real_name:
print real_name
else:
print "Sorry, don't know this '{}' person".format(name)
Run Code Online (Sandbox Code Playgroud)
这种"请求宽恕"的例子往往过于简单.IMO并不清楚try
/ except
块本质上比if
/ 更好else
.当执行可能以各种方式失败的操作时,真正的价值会更加清晰 - 例如解析; 使用eval()
; 访问操作系统,中间件,数据库或网络资源; 或者进行复杂的数学运算.当存在多种潜在的失败模式时,准备获得宽恕是非常有价值的.
关于代码示例的其他说明:
您不需要在每个变量用法周围使用钢包try
/ except
块.那太可怕了.而你并不需要设置self.bar
你的__init__()
,因为它在你的设置class
上面的定义.通常在类中定义它(如果它的数据可能在类的所有实例之间共享)或者在__init__()
(如果它是实例数据,特定于每个实例)中定义它.
None
顺便说一句,值不是未定义或错误.它是一个特定的合法值,意思是无,零,无或无.许多语言都有这样的值,以便程序员不"超载" 0
,-1
,''
(空字符串)或类似的实用价值.
你说得对-的目的try
,并except
不能覆盖你草率的编码.这只会导致编码更加草率.
应使用异常处理来处理特殊情况(草率编码不是特殊情况).但是,通常很容易预测可能发生的特殊情况.(例如,您的程序接受用户输入并使用它来访问字典,但用户的输入不是字典中的键...)
这里有很多好的答案,我只是想我会添加一个我到目前为止没有提到过的一点.
通常要求宽恕而不是许可会提高性能.
通常情况下,失败的情况很少见,这意味着如果您只是要求许可,那么您几乎不需要做任何额外的操作.是的,当它失败时会抛出异常,并执行额外的操作,但python中的异常非常快.你可以在这里看到一些时间:https://jeffknupp.com/blog/2013/02/06/write-cleaner-python-use-exceptions/
我个人的非宗教的看法是,说口头禅主要适用于记录和充分理解退出条件和优势的情况下(例如I / O错误),并且不应该被用来作为一个get-外的监狱卡草率的编程。
也就是说,try/except
当存在“更好”的替代方案时,通常会使用它。例如:
# Do this
value = my_dict.get("key", None)
# instead of this
try:
value = my_dict["key"]
except KeyError:
value = None
Run Code Online (Sandbox Code Playgroud)
就您的示例而言,if hasattr(foo, "bar")
如果您无法控制foo
并且需要检查是否符合期望,请使用,否则请简单使用foo.bar
并让产生的错误作为识别和修复草率代码的指南。
在 Python 上下文中,“请求宽恕而不是许可”意味着一种编程风格,在这种风格中,您不会预先检查事情是否符合您的预期,而是您处理导致错误的错误。经典示例不是检查字典是否包含给定的键,如下所示:
d = {}
k = "k"
if k in d.keys():
print d[k]
else:
print "key \"" + k + "\" missing"
Run Code Online (Sandbox Code Playgroud)
而是在缺少密钥时处理由此产生的错误:
d = {}
k = "k"
try:
print d[k]
except KeyError:
print "key \"" + k + "\" missing"
Run Code Online (Sandbox Code Playgroud)
然而,重点不是if
用try
/替换代码中的each except
;这将使您的代码明显更加混乱。相反,您应该只在您真正可以对其采取措施的地方捕获错误。理想情况下,这将减少代码中整体错误处理的数量,使其实际目的更加明显。
小智 5
虽然已经有许多高质量的答案,但大多数主要是从文体的角度来讨论这个问题,就像一个功能性的一样。
在某些情况下,我们需要请求原谅,而不是确保代码正确的许可(在多线程程序之外)。
一个典型的例子是,
if file_exists:
open_it()
Run Code Online (Sandbox Code Playgroud)
在此示例中,文件可能在检查和尝试实际打开文件之间已被删除。这可以通过使用来避免try
:
try:
open_it()
except FileNotFoundException:
pass # file doesn't exist
Run Code Online (Sandbox Code Playgroud)
这在很多地方都会出现,通常与文件系统或外部 API 一起使用。
归档时间: |
|
查看次数: |
19951 次 |
最近记录: |