And*_*zlo 26 python oop types duck-typing
我通常希望尽可能保持代码的通用性.我目前正在编写一个简单的库,并且能够在我的库中使用不同的类型,这次感觉非常重要.
一种方法是强制人们继承"接口"类.对我来说,这感觉更像是Java而不是Python,并且issubclass在每种方法中使用也听起来不太诱人.
我首选的方法是善意使用该物品,但这会引起一些AttributeErrors.我可以在try/except块中包装每个危险的调用.这也是一件很麻烦的事情:
def foo(obj):
...
# it should be able to sleep
try:
obj.sleep()
except AttributeError:
# handle error
...
# it should be able to wag it's tail
try:
obj.wag_tail()
except AttributeError:
# handle this error as well
Run Code Online (Sandbox Code Playgroud)
我是否应该跳过错误处理并期望人们只使用具有所需方法的对象?如果我做了一些愚蠢的事情,比如[x**2 for x in 1234]我实际上得到了一个TypeError而不是一个AttributeError(整数不可迭代)所以必须在某个地方进行一些类型检查 - 如果我想做同样的事情怎么办?
这个问题将是开放式的,但是以干净的方式处理上述问题的最佳方法是什么?有没有既定的最佳实践?例如,上面的可迭代"类型检查"是如何实现的?
虽然AttributeErrors很好,但TypeErrors本机函数引发的函数通常会提供有关如何解决错误的更多信息.以此为例:
>>> ['a', 1].sort()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()
Run Code Online (Sandbox Code Playgroud)
我希望我的图书馆尽可能有用.
Ped*_*iro 18
我不是一个python pro,但我相信除非你可以尝试一个替代方案,当参数没有实现给定的方法时,你不应该阻止抛出异常.让调用者处理这些异常.这样,你就会隐藏开发人员的问题.
正如我在" 清洁代码"中所读到的,如果要搜索集合中的项目,请不要使用ìssubclass(列表)测试参数,而是要调用getattr(l, "__contains__").这将使正在使用您的代码的人有机会传递一个不是列表但已__contains__定义方法的参数,并且事情应该同样有效.
因此,我认为您应该以抽象的,通用的方式编写代码,尽可能少地施加限制.为此,您必须尽可能减少假设.但是,当你面对一些你无法处理的事情时,提出异常并让程序员知道他犯了什么错误!
sen*_*rle 12
如果您的代码需要特定的接口,并且用户在没有该接口的情况下传递对象,那么十分之九的情况下,捕获异常是不合适的.大多数情况下,AttributeError当涉及到接口不匹配时,不仅合理而且预期.
偶尔,AttributeError出于两个原因之一捕获它可能是合适的.您希望接口的某些方面是可选的,或者您希望抛出更具体的异常,可能是特定于包的异常子类.当然,如果你没有诚实地处理错误和任何后果,你绝不应该阻止抛出异常.
所以在我看来,这个问题的答案必须是问题和领域特定的.从根本上说,使用Cow对象而不是Duck对象应该是否有效.如果是这样,你处理任何必要的界面捏造,那就没关系.另一方面,没有理由明确检查某人是否已经通过了您的Frog对象,除非这会导致灾难性的失败(即比堆栈跟踪更糟糕的事情).
也就是说,记录您的界面总是一个好主意 - 这就是文档字符串(以及其他内容)的用途.当你考虑它时,为大多数情况抛出一般错误并告诉用户在docstring中执行它的正确方法比尝试预测用户可能犯的每个可能错误并创建自定义错误消息要高效得多.
最后一个警告 - 你可能在这里考虑用户界面 - 我认为这是另一个故事.最好检查最终用户提供的输入以确保它不是恶意或可怕的错误,并提供有用的反馈而不是堆栈跟踪.但对于库或类似的东西,你真的必须相信程序员使用你的代码来智能和尊重地使用它,并理解Python生成的错误.
如果你只是希望未实现的方法什么都不做,你可以尝试这样的东西,而不是多行try/except结构:
getattr(obj, "sleep", lambda: None)()
Run Code Online (Sandbox Code Playgroud)
但是,这不一定是函数调用,因此可能:
hasattr(obj, "sleep") and obj.sleep()
Run Code Online (Sandbox Code Playgroud)
或者如果你想在调用它实际上可以被调用的东西之前更加确定:
hasattr(obj, "sleep") and callable(obj.sleep) and obj.sleep()
Run Code Online (Sandbox Code Playgroud)
这种"先行后移"模式通常不是Python中的首选方式,但它完全可读并且适用于单行.
另一种选择当然是将其抽象try/except为一个单独的函数.
好问题,而且非常开放.我相信典型的Python风格不是通过isinstance或捕获个别异常来检查.Cerainly,使用isinstance是相当糟糕的风格,因为它打败了鸭子打字的全部要点(虽然在原语上使用isinstance可以正常 - 确保检查整数输入的int和long,并检查字符串的basetring(base) str和unicode的类.如果你检查,你应该引发TypeError.
不检查通常是正常的,因为它通常会引发TypeError或AttributeError,这就是你想要的.(虽然它可以延迟那些使客户端代码难以调试的错误).
你看到TypeErrors的原因是原始代码引发了它,因为它确实是一个实例.如果某些东西不可迭代,则for循环被硬编码以引发TypeError.