在这种情况下,如何平衡“Pythonic”和“方便”?

时间:2011-08-18 04:07:30

标签: python duck-typing

我有一个“接口”,将由客户端代码实现:

class Runner:
    def run(self):
        pass

run一般应归还docutils node,但因为距离最远 常见的情况是纯文本,调用者允许run返回一个字符串,这将是 使用type()进行检查,然后转为node

然而,我理解“Pythonic”的方式,这不是“Pythonic”,因为 检查某事物的type()不会让它“成为”一种“行动”的类型 一个 - 即“Pythonic”代码应该使用duck typing。

我考虑了

def run_str(self):
    pass

def run_node(self):
    return make_node(self.run_str())

但是我并不关心这个,因为它提供了不那么有趣的返回类型 就在那里;这令人分心。

我有什么想法错过了吗?此外,我可能遇到的问题 我的“坏”系统在路上(对我来说似乎或多或少)?

5 个答案:

答案 0 :(得分:5)

我认为这是一个有点欺骗性的例子;你有没有说过的话。我猜你说当你说“有一个界面”时,你的意思是你有一些代码接受一个对象并调用它的run方法。

如果您在调用run方法之前没有测试该对象的类型,那么您使用的是鸭子打字,简单明了! (在这种情况下,如果它有run方法,那么它就是Runner。)只要您不在对象上使用typeisinstance run方法,然后你就是Pythonic。

是否应接受普通字符串或仅接受节点对象的问题是一个微妙的不同问题。字符串和node对象可能根本不实现相同的接口!字符串从根本上不像<{1}}那样嘎嘎,所以你不必像对待它一样对待它们。这就像一只大象,如果你想让它像鸭子一样嘎嘎叫,你必须给大象一个磁带播放器并训练大象首先使用它。

所以这不再是“鸭子打字”的问题,而是界面设计。您正在尝试确定您希望界面的严格程度。

为了给你一个答案,那么,在这个级别上,我认为假设node返回一个run对象是最Pythonic的。没有必要使用nodeisinstance来测试它。只是假装它是一个type对象,如果程序员使用你的界面出错,并看到异常,那么他们必须阅读你的文档字符串,这将告诉他们node应该传递{ {1}}对象。

然后,如果你想要也接受字符串,或像字符串那样嘎嘎叫的东西,你可以这样做。因为字符串是相当原始的类型,我会说使用run(但 node并不恰当,因为它拒绝unicode字符串等)。从本质上讲,这是对你的程序的懒惰用户非常自由和善良;通过接受大象以及像鸭子一样嘎嘎叫的东西,你已经超越了。

(更具体地说,我会说这有点像在函数开头的参数上调用isinstance(obj, basestring),你想接受生成器和序列。)

答案 1 :(得分:2)

您不一定需要有方法来处理每种类型,特别是如果只发生一个简单的操作。一个常见的Pythonic方法是做类似的事情:

def run(self):
    try:
        ...assume it's a str   
    except TypeError:
        ...oops, not a str, we'll address that

这遵循Easier to ask for forgiveness than permission (EAFP)编码风格,通常更快更简单。

答案 2 :(得分:1)

查看Errors and Exceptions.您可以执行以下操作:

def run(self,arg):
    try:
        return make_node(arg)
    except AlreadyNodeError:
        pass

在make_node函数内部,如果参数已经是节点,则引发AlreadyNodeError。

答案 3 :(得分:1)

使用type()检测变量的类型实际上是一种不好的做法,因为它不允许对象继承所需类型(在您的情况下为str),更好的方法是使用isinstance()

if isinstance(my_var, str):
    my_code_here()

另外,如你所提到的,这样做的pythonic方法就是鸭子打字,你为什么不把代码放在try / except块中呢?因此,只有当值不像预期的那样行为时才会捕获异常(如果它像鸭子一样嘎嘎叫走路,那就是鸭子)。

答案 4 :(得分:0)

class Node(object):
  def __new__(cls, contents):
    return contents if isinstance(contents, cls) else object.__new__(cls)
  def __init__(self, contents):
    # construct from string...

class Manager(object):
  def do_something(self, runner, *args):
    do_something_else(Node(runner(*args)))

现在,如果跑步者返回一个节点或一个字符串并不重要。