“请求原谅而不是请求许可”-解释[关闭]

2024-12-02 08:42:00
admin
原创
181
摘要:问题描述:我并不是在询问关于这种哲学的个人“宗教”观点,而是询问一些更技术性的东西。我理解这句话是检验你的代码是否“pythonic”的几个试金石之一。但对我来说,“pythonic”意味着干净、简单和直观,没有加载用于糟糕编码的异常处理程序。那么,实际的例子。我定义一个类:class foo(object)...

问题描述:

我并不是在询问关于这种哲学的个人“宗教”观点,而是询问一些更技术性的东西。

我理解这句话是检验你的代码是否“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

现在,从程序背景来看,在另一个函数中我想这样做:

if foo.bar:
    # do stuff

如果我没有耐心,没有执行初始的 foo = None,我将得到一个属性异常。那么,“请求原谅而不是许可”是否意味着我应该这样做?

try:
    if foo.bar:
        # do stuff
except:
    # this runs because my other code was sloppy?

为什么我最好在 try 块中添加其他逻辑,这样我就可以让类定义更加模糊?为什么不先定义所有内容,然后明确授予权限

(不要因为我使用 try/except 块而责怪我...我到处都使用它们。我只是认为使用它们来捕获我自己的错误是不对的,因为我并不是一个彻底的程序员。)

或者...我完全误解了“请求原谅”的口头禅?


解决方案 1:

“请求原谅,而不是许可” 反对两种编程风格。“请求许可”是这样的:

if can_do_operation():
    perform_operation()
else:
    handle_error_case()

“请求原谅”是这样的:

try:
    perform_operation()
except Unable_to_perform:
    handle_error_case()

在这种情况下,尝试执行操作可能会失败,您必须以某种方式处理操作无法执行的情况。例如,如果操作正在访问文件,则该文件可能不存在。

请求原谅最好有两个主要原因:

  • 在并发世界中(在多线程程序中,或者如果操作涉及程序外部的对象,如文件、其他进程、网络资源等),情况可能会在运行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()

你可以缩写if foo.bar is not None:if foo.bar:仅当bar在解释为布尔值时始终为真时 — 如果bar可以是 0、[]{}任何其他具有假真值的对象,则需要。如果你正在测试可选部分(而不是在和之间进行测试),is not None那么它也更清晰。True`False`

此时你可能会问:为什么不省略bar不存在时的初始化,而是用处理程序测试它的存在hasattr或捕获它AttributeError?因为你的代码只有在两种情况下才有意义:

  • 该对象没有bar字段;

  • 该对象有一个bar字段,其含义就是你所认为的含义。

因此,在编写或决定使用对象时,您需要确保它没有具有bar不同含义的字段。如果您需要使用没有bar字段的其他对象,这可能不是您需要适应的唯一事情,因此您可能需要创建一个派生类或将对象封装在另一个类中。

解决方案 2:

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)

这里说明了可能发生的异常 ( KeyError),这样您就不必为可能发生的每个错误请求原谅,而只需为自然发生的错误请求原谅。为了进行比较,“先征求许可”方法可能如下所示:

if name in names:
    print names[name]
else:
    print "Sorry, don't know this '{}' person".format(name)

或者

real_name = names.get(name, None)
if real_name:
    print real_name
else:
    print "Sorry, don't know this '{}' person".format(name)

try这种“请求原谅”的例子往往过于简单。在我看来, /块本质上是否比/except更好并不十分清楚。在执行可能以各种方式失败的操作时,真正的价值要清晰得多——例如解析;使用;访问操作系统、中间件、数据库或网络资源;或执行复杂的数学运算。当存在多种潜在故障模式时,做好获得原谅的准备是非常有价值的。if`else`eval()

有关您的代码示例的其他说明:

您不需要在每个变量使用周围都加上try/except块。那太可怕了。而且您不需要self.bar在您的中设置,__init__()因为它已在class上面的定义中设置。通常在类中定义它(如果它是可能在类的所有实例之间共享的数据)或在__init__()(如果它是实例数据,则特定于每个实例)。

None顺便说一句,的值不是未定义的,也不是错误。它是一个特定且合法的值,表示无、零、空或无。许多语言都有这样的值,因此程序员不会“重载” 0-1''(空字符串)或类似的有用值。

解决方案 3:

这里有很多很好的答案,我只是想补充一点我到目前为止还没有提到的观点。

经常请求原谅而不是请求许可可以提高绩效。

  • 当你请求权限的时候,每次都要执行额外的操作来请求权限。

  • 当请求原谅时,有时您只需执行额外的操作,即当它失败时。

通常失败的情况很少见,这意味着如果您只是请求权限,那么您几乎不需要执行任何额外操作。是的,当它失败时它会抛出异常,并执行额外操作,但 python 中的异常非常快。您可以在此处查看一些时间:https://jeffknupp.com/blog/2013/02/06/write-cleaner-python-use-exceptions/

解决方案 4:

try你说得对——和的目的except不是为了掩盖你草率的编码。这只会导致更草率的编码。

异常处理应该用于处理异常情况(粗心的编码不是异常情况)。但是,通常很容易预测哪些异常情况可能发生。(例如,您的程序接受用户输入并使用它来访问字典,但用户的输入不是字典中的键...)

解决方案 5:

我个人的非宗教观点是,上述咒语主要适用于有记录易于理解的退出条件和边缘情况(例如 I/O 错误),绝不能被用作草率编程的逃脱惩罚的卡片。

尽管如此,try/except当有“更好”的替代方案时,通常会使用。例如:

# Do this    
value = my_dict.get("key", None)

# instead of this
try:
  value = my_dict["key"]
except KeyError:
  value = None

至于您的示例,if hasattr(foo, "bar")如果您无法控制foo并且需要检查是否符合您的期望,请使用,否则只需使用foo.bar并让产生的错误成为您识别和修复草率代码的指南。

解决方案 6:

虽然已经有许多高质量的答案,但大多数主要从风格的角度而不是功能的角度来讨论这个问题。

在某些情况下,我们需要请求原谅,而不是请求许可以确保代码正确(在多线程程序之外)。

一个典型的例子是,

if file_exists: 
    open_it()

在此示例中,文件可能在检查和尝试实际打开文件之间被删除。可以使用以下命令避免这种情况try

try:
    open_it()
except FileNotFoundException:
    pass # file doesn't exist 

这出现在各种地方,通常与文件系统或外部 API 一起使用。

解决方案 7:

在 Python 上下文中,“请求原谅而不是请求许可”意味着一种编程风格,即您不会事先检查事物是否符合您的预期,而是处理不符合预期时产生的错误。经典示例是不检查字典是否包含给定的键,如下所示:

d = {}
k = "k"
if k in d.keys():
  print d[k]
else:
  print "key \"" + k + "\" missing"

但如果密钥丢失,则处理由此产生的错误:

d = {}
k = "k"
try:
  print d[k]
except KeyError:
  print "key \"" + k + "\" missing"

但是重点不是将if代码中的每个都替换为try/ except;这会使你的代码变得更加混乱。相反,你应该只捕获那些你真正可以处理的错误。理想情况下,这会减少代码中总体错误处理的数量,使其实际目的更加明显。

解决方案 8:

.bar请求原谅而不是请求许可是为了简化代码。当有合理的预期可能会触发 AttributeError时,代码应该这样写。

 try:
     print foo.bar
 except AttributeError as e
     print "No Foo!" 

您的代码似乎既请求许可,又请求宽恕:)

问题是,如果您合理地预期某事会失败,请使用 try/catch。如果您不预期某事会失败,但它还是失败了,那么抛出的异常就相当于其他语言中的失败断言。您可以查看意外异常发生的位置,并相应地调整您的代码/假设。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用