5

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

class Runner:
    def run(self):
        pass

run通常应该返回 adocutils node但因为最常见的情况是纯文本,调用者允许run返回一个字符串,该字符串将使用检查type()并转换为 a node

然而,按照我理解“Pythonic”的方式,这不是“Pythonic”,因为检查type()某物的“行为”不会让它“成为”一种类型——即“Pythonic”代码应该使用鸭子类型。

我考虑过

def run_str(self):
    pass

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

但我不在乎这个,因为它把不那么有趣的返回类型放在名称中;这让人分心。

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

4

5 回答 5

5

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

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

你应该接受纯字符串还是只接受节点对象的问题是一个微妙的不同问题。字符串和node对象可能根本不实现相同的接口!字符串基本上不会像 a 那样嘎嘎作响node,因此您不必像对待它们一样对待它们。这就像一头大象来了,如果你想让它像鸭子一样嘎嘎叫,你必须给大象一个磁带播放器,并训练大象先使用它。

所以这不再是“鸭子打字”的问题,而是界面设计的问题。你试图决定你希望你的界面有多严格。

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

然后,如果你也接受字符串,或者像字符串一样嘎嘎作响的东西,你可以这样做。而且由于字符串是相当原始的类型,我会说它不适合使用isinstance(obj, basestring)(但不是 type(obj) == str因为它拒绝 unicode 字符串等)。从本质上讲,这是您对程序的懒惰用户非常自由和友善;你已经超越了接受大象以及像鸭子一样嘎嘎叫的东西。

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

于 2011-08-18T04:50:17.920 回答
2

您不一定需要拥有处理每种类型的方法,尤其是在只发生简单操作的情况下。一种常见的 Pythonic 方法是执行以下操作:

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

这遵循更容易请求宽恕而不是许可 (EAFP)编码风格,这通常更快更简单。

于 2011-08-18T04:21:58.317 回答
1

查看错误和异常。你可以这样做:

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

在你的 make_node 函数中,如果参数已经是一个节点,让它引发一个 AlreadyNodeError 。

于 2011-08-18T04:21:40.403 回答
1

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

if isinstance(my_var, str):
    my_code_here()

此外,正如您提到的那样,一种pythonic的方式是鸭子打字,您为什么不将代码放在try/except块中?因此,只有当值没有按预期运行时才会捕获异常如果它像鸭子一样嘎嘎叫和走路,那就是鸭子)。

于 2011-08-18T04:27:02.810 回答
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)))

现在,跑步者返回节点还是字符串都没有关系。

于 2011-08-18T04:55:29.203 回答