这个问题在这里已有答案:
有时需要在Python中检查参数。例如我有一个函数,它接受网络中其他节点的地址作为原始字符串地址或类封装其他节点信息的类Node。
我使用type()函数,如:
if type(n) == type(Node):
do this
elif type(n) == type(str)
do this
这是一个很好的方法吗?
更新1:Python 3具有函数参数的注释。这些可用于使用工具进行类型检查:http://mypy-lang.org/
使用isinstance()
。样品:
if isinstance(n, unicode):
# do this
elif isinstance(n, Node):
# do that
...
>>> isinstance('a', str)
True
>>> isinstance(n, Node)
True
不,Python中的类型检查参数不是必需的。这绝不是必要的。
如果您的代码接受地址为rawstring或Node
对象,则您的设计会被破坏。
这是因为如果你不知道自己程序中对象的类型,那么你已经做错了。
Typechecking会损害代码重用并降低性能。具有根据传递的对象的类型执行不同事物的函数是容易出错的,并且具有难以理解和维护的行为。
您有以下更健全的选择:
Node
对象构造函数,或者一个在Node
对象中转换字符串的函数。让你的函数假设传递的参数是一个Node
对象。这样,如果你需要将一个字符串传递给函数,你只需:
myfunction(Node(some_string))
这是您最好的选择,它干净,易于理解和维护。任何阅读代码的人都会立即理解正在发生的事情,而且您不必进行类型检查。Node
对象,另一个接受rawstrings。你可以用最方便的方式在内部调用另一个(myfunction_str
可以创建一个Node
对象并调用myfunction_node
,或者相反)。Node
对象具有__str__
方法,并在函数内部,在接收到的参数上调用str()
。这样你总是通过强制获得一个字符串。无论如何,请不要进行类型检查。这是完全没必要的,只有缺点。以您不需要进行类型检查的方式重构代码。无论是短期还是长期,您只能从中获益。
如有必要,您还可以使用try catch键入检查:
def my_function(this_node):
try:
# call a method/attribute for the Node object
if this_node.address:
# more code here
pass
except AttributeError, e:
# either this is not a Node or maybe it's a string,
# so behavior accordingly
pass
您可以在Beginning Python中看到第二个关于生成器的示例(我的版本中的第197页),我相信Python Cookbook。很多时候捕捉AttributeError
或TypeError
更简单,显然更快。此外,它可能以这种方式工作得最好,因为那时你不依赖于特定的继承树(例如,你的对象可能是一个Node
,或者它可能是与Node
具有相同行为的其他对象)。
听起来像是在追随“泛型函数” - 一个根据给定的参数表现不同的函数。这有点像你在另一个对象上调用一个方法时会得到一个不同的函数,而不是仅使用第一个参数(对象/ self)来查找函数,而是使用所有参数。
Turbogears使用类似的东西来决定如何将对象转换为JSON - 如果我没记错的话。
an article from IBM使用调度程序包进行此类操作:
从那篇文章:
import dispatch
@dispatch.generic()
def doIt(foo, other):
"Base generic function of 'doIt()'"
@doIt.when("isinstance(foo,int) and isinstance(other,str)")
def doIt(foo, other):
print "foo is an unrestricted int |", foo, other
@doIt.when("isinstance(foo,str) and isinstance(other,int)")
def doIt(foo, other):
print "foo is str, other an int |", foo, other
@doIt.when("isinstance(foo,int) and 3<=foo<=17 and isinstance(other,str)")
def doIt(foo, other):
print "foo is between 3 and 17 |", foo, other
@doIt.when("isinstance(foo,int) and 0<=foo<=1000 and isinstance(other,str)")
def doIt(foo, other):
print "foo is between 0 and 1000 |", foo, other