让我们举一个API的例子
def get_abs_directory(self, path):
if os.path.isdir(path):
return path
else:
return os.path.split(os.path.abspath(path))[0]
我的问题是验证参数的pythonic方法是什么,我应该忽略任何类型的验证(我观察到所有python代码都没有验证)
此问题并非特定于文件IO,而是仅将FileIO用作示例
答案 0 :(得分:6)
正如此处的文档所述,Python遵循EAFP方法。这意味着我们通常使用更多try
和catch
块而不是尝试验证参数。让我演示一下:
import os
def get_abs_directory(path):
try:
if os.path.isdir(path):
return path
else:
return os.path.split(os.path.abspath(path))[0]
except TypeError:
print "You inserted the wrong type!"
if __name__ == '__main__':
get_abs_directory(1) # Using an int instead of a string, which is caught by TypeError
你可以然而,希望在LBYL(Look Before You Leap)风格中编码,这看起来像这样:
import os
def get_abs_directory(path):
if not isinstance(path, str):
print "You gave us the wrong type, you big meany!"
return None
if os.path.isdir(path):
return path
else:
return os.path.split(os.path.abspath(path))[0]
if __name__ == '__main__':
get_abs_directory(1)
答案 1 :(得分:3)
即使已经回答,这对评论来说太长了,所以我只想添加另一个答案。
通常,类型检查有两个原因:确保您的功能实际完成,并避免因输出错误而导致难以调试的下游故障。
对于第一个问题,答案总是合适的 - EAFP是常规方法。而且你不担心糟糕的投入。
对于第二个......答案取决于您的正常使用情况,并且您担心输入/错误不良。当糟糕的输入总是产生异常时,EAFP仍然是合适的(并且它更容易,也更容易调试)(其中'坏输入'可能限于应用程序期望产生的坏输入类型)。但如果输入错误可能会产生有效输出,那么LYBL可能会让您的生活更轻松。
示例:假设您调用square(),将此值放入字典中,然后(很多)稍后从字典中提取此值并将其用作索引。当然,索引必须是整数。
square(2)== 4,是一个有效的整数,所以是正确的。 square('a')将始终失败,因为'a'*'a'无效,并且将始终抛出异常。如果这是唯一的两种可能性,那么您可以安全地使用EAFP。如果你确实得到了错误的数据,它会抛出一个异常,产生一个追溯,你可以用pdb重新启动,并很好地指出错误。
然而......让我们说你的应用程序使用了一些FP。并且它可能(假设你有一个错误!当然不是正常操作)你不小心叫方(1.43)。这将返回一个有效值 - 2.0449左右。你不会在这里得到例外,因此你的应用程序将很乐意接受2.0449并将其放入字典中。很久以后,你的应用程序会将这个值从字典中拉回来,将其用作列表的索引并且 - 崩溃。你将获得一个追溯,你将重新启动pdb,并意识到这根本没有帮助你,因为这个值是很久以前计算出来的,你不再有输入,或者知道这些数据是如何获得的那里。那些调试并不好玩。
在这些情况下,您可以使用断言(LYBL的特殊形式)来更早地检测这些类型的错误,或者您可以明确地执行此操作。如果你没有调用该函数的bug,那么任何一个都可以工作。但是,如果你这样做......那么你真的很高兴你在人工接近失败时检查了输入,而不是在你的应用程序中自然地随后出现一些随机的地方。
答案 2 :(得分:2)
EAFP是Python在这种情况下的事实上的标准,同时,如果您愿意的话,没有任何阻碍您完全遵循LBYL的事情。
但是,EAFP适用时会有保留。当代码仍然能够处理异常情况,在某个时候中断或使调用者能够验证可能的错误时,那么最好遵循EAFP原则。
使用EAFP会导致无提示错误,而最好进行显式检查/验证(LBYL)。
关于此,有一个Python模块parameters-validation,可在需要时简化对函数参数的验证:
@validate_parameters
def register(
token: strongly_typed(AuthToken),
name: non_blank(str),
age: non_negative(int),
nickname: no_whitespaces(non_empty(str)),
bio: str,
):
# do register
免责声明:我是项目维护者。
答案 3 :(得分:1)
代码确实"陷阱"如此测试代码所示的错误,引发了无异常
的异常import os.path
import os
class pathetic(unittest.TestCase):
def setUp(self):
if (not(os.path.exists("ABC"))):
os.mkdir("ABC")
else:
self.assert_(False, "ABC exists, can't make test fixture")
def tearDown(self):
if (os.path.exists("ABC")):
os.rmdir("ABC")
def test1(self):
mycwd = os.path.split(os.path.abspath(os.getcwd()))[0]
self.assertEquals("/", self.get_abs_directory("/abc"))
self.assertEquals(mycwd, self.get_abs_directory(""))
self.assertEquals("/ABC", self.get_abs_directory("/ABC/DEF"))
try:
self.get_abs_directory(None)
self.assert_(False, "should raise exception")
except TypeError:
self.assert_(True, "woo hoo, exception")
def get_abs_directory(self, path):
if os.path.isdir(path):
return path
else:
return os.path.split(os.path.abspath(path))[0]
if __name__ == '__main__':
unittest.main()