2013-10-21 139 views
2

让作为API参数验证,在Python最佳实践

def get_abs_directory(self, path): 
    if os.path.isdir(path): 
     return path 
    else: 
     return os.path.split(os.path.abspath(path))[0] 

我的问题是什么是验证参数的Python的方式,我应该忽略任何类型验证的(我认为,所有的例子在Python代码做根本没有验证)

  1. 我应该检查“路径”是空的,而不是空
  2. 我应该检查路径的“类型”是字符串总是
  3. 一般来说,我应该检查参数的类型? (我想不是作为蟒在动态类型)

这个问题不是特定于文件IO,而不是FileIO专注仅作为一个例子

回答

6

正如这里的文件所提到的,Python的如下一个EAFP做法。这意味着我们通常使用更多的trycatch块,而不是尝试验证参数。让我证明:

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(三思而后行)的风格,这将是这个样子:

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) 
+0

感谢您的答案,但我的问题是更通用的。可以说我的API只是返回一个数字的square()。现在问题是我应该在方法中进行类型验证? – Bhushan

+2

@Bhushan不,只是让它*尝试*做计算。如果失败,异常会自动向呼叫者冒泡。除非它是实际的用户输入,否则假定人们正在正确使用您的API。 – poke

+2

@Bhushan当我谈到EAFP时,我想我回答了你的问题。这意味着你必须找到一些东西,如果失败了,你就要处理它。当用Python进行编程时,这是一种工作方式。因为,如果你仔细想想,你通常不会得到错误的输入,所以每次调用它时都要检查它是否浪费资源。 –

0

代码没有“陷阱“错误,因为这个测试代码所示,将引发异常的传入无

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() 
+0

请参阅我对Brainiac的回复,我正在寻找一些关于处理方法参数的Pythonic方法的指南。 – Bhushan

3

即使已经回答了,这是太长了评论,所以我就广告d另一个答案。

一般来说,类型检查有两个原因:确保您的函数实际完成,并避免由于错误输出而导致难以调试的下游故障。

对于第一个问题,答案总是合适的 - EAFP是正常的方法。你不用担心输入不好。

对于第二个......答案取决于您的正常使用情况,并且您担心错误的输入/错误。当错误的输入总是产生异常(其中“错误输入”可能仅限于应用程序期望生成的错误输入类型)时,EAFP仍然适用(并且更容易且更易于调试)。但是如果有可能输入错误会产生有效的输出,那么LYBL可能会让你的生活更轻松。例如:假设您调用square(),将此值放入字典中,然后(很多)稍后从字典中提取此值并将其用作索引。索引当然必须是整数。

square(2)== 4,是一个有效的整数,所以是正确的。 square('a')总是会失败,因为'a'*'a'是无效的,并且总会抛出异常。如果这是唯一的两种可能性,那么你可以安全地使用EAFP。如果确实收到了错误的数据,它会抛出一个异常,产生一个回溯,并且您可以使用pdb重新启动,并很好地指出错误。

但是,让我们说你的应用程序使用一些FP。这是可能的(假设你有一个bug!当然不是正常操作)让你无意中调用square(1.43)。这将返回一个有效值 - 2.0449左右。你不会在这里得到异常,所以你的应用程序会很高兴地把这个2.0449放在字典中。很久以后,您的应用程序会将此值从字典中取出,并将其用作列表中的索引,并且 - 崩溃。你会得到一个回溯,你会用pdb重新启动,并且意识到它根本没有帮助你,因为这个值是很久以前计算出来的,你不再有输入,或者不知道数据是如何得到的那里。而那些调试并不好玩。

在这些情况下,您可以使用断言(LYBL的特殊形式)来提前检测这些类型的错误,或者您可以明确地执行它。如果你从未有过调用该函数的错误,那么任何一个都可以工作。但是如果你这样做......那么你真的会很高兴你在接近失败的情况下人为地检查输入,而不是自然而然地在你的应用程序中随机选择一些输入。