我怎样才能平衡“Pythonic”和“Pythonic”?和“方便”在这种情况下?

发布于 2024-11-30 13:03:24 字数 620 浏览 2 评论 0原文

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

class Runner:
    def run(self):
        pass

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

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

我考虑过

def run_str(self):
    pass

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

,但我不关心这个,因为它给出了不太有趣的返回类型 就在名字里;这很分散注意力。

有什么我错过的想法吗?另外,我可能会遇到什么问题吗 带着我的“坏”系统走下去(对我来说似乎或多或少安全)?

I have an "interface" that will be implemented by client code:

class Runner:
    def run(self):
        pass

run should in general return a docutils node but because the far far most
common case is plain text, the caller allows run to return a string, which will be
checked using type() and turned into a node.

However, the way I understand "Pythonic", this is not "Pythonic" because
checking the type() of something doesn't let it "be" a type by "acting" like
one -- ie "Pythonic" code should use duck typing.

I considered

def run_str(self):
    pass

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

but I don't care for this because it puts the not-so-interesting return type
right there in the name; it's distracting.

Are there any ideas I've missed? Also, are there problems I might encounter
down the road with my "bad" system (it seems more or less safe to me)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

迷你仙 2024-12-07 13:03:24

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

如果您在调用其 run 方法之前没有测试该对象的类型,那么您正在使用鸭子类型,简单明了! (在本例中,如果它具有 run 方法,那么它就是 Runner。)只要您不使用 type 或 <使用 run 方法在对象上使用 code>isinstance ,那么你就是 Pythonic 了。

是否应该接受纯字符串或仅接受节点对象的问题是一个略有不同的问题。字符串和 node 对象可能根本不实现相同的接口!从根本上来说,字符串不会节点一样嘎嘎作响,因此您不必将它们视为节点。这就像一头大象走过来,如果你想让它像鸭子一样嘎嘎叫,你就必须给大象一个录音机,并先训练大象使用它。

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

为了给你一个答案,在这个层面上,我认为假设 run 返回一个 node 对象是最 Pythonic 的。无需使用 isinstancetype 来测试这一点。假设它是一个 node 对象,如果使用您的界面的程序员出错并看到异常,那么他们将不得不读取您的文档字符串,这将告诉他们 run 应该传递一个 node 对象。

然后,如果您还想接受字符串或像字符串一样嘎嘎作响的东西,您可以这样做。由于字符串是相当原始的类型,我想说使用 isinstance(obj, basestring) 并没有什么不妥(但是不是 type(obj) == str< /code> 因为它拒绝 unicode 字符串等)。本质上,这是你对程序的懒惰用户非常自由和友善;你已经超越了接受大象和像鸭子一样嘎嘎叫的东西。

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

I think this is a slightly deceptive example; there's something you haven't stated. I'm guessing that when you say you "have an interface," what you mean is that you have some code that accepts an object and calls its run method.

If you aren't testing for the type of that object before calling its run method, the you're using duck typing, plain and simple! (In this case, if it has a run method, then it's a Runner.) As long as you don't use type or isinstance on the object with a run method, then you're being Pythonic.

The question of whether you should accept plain strings or only node objects is a subtly different question. Strings and node objects probably don't implement the same interface at all! Strings fundamentally don't quack like a node, so you don't have to treat them like one. This is like an elephant that comes along, and if you want it to quack like a duck, you have to give the elephant a tape player and train the elephant to use it first.

So this isn't a matter of "duck typing" any more, but of interface design. You're trying to decide how strict you want your interface to be.

To give you an answer, then, at this level, I think it's most Pythonic to assume that run returns a node object. There's no need to use isinstance or type to test for that. Just pretend it's a node object and if the programmer using your interface gets that wrong, and sees an exception, then they'll have to read your docstring, which will tell them that run should pass a node object.

Then, if you want to also accept strings, or things that quack like strings, you can do so. And since strings are rather primitive types, I would say it's not inappropriate to use isinstance(obj, basestring) (but not type(obj) == str because that rejects unicode strings, etc.). Essentially, this is you being very liberal and kind to lazy users of your program; you're already going above and beyond by accepting elephants as well as things that quack like ducks.

(More concretely, I'd say this is a bit like calling iter on an argument at the beginning of a function that you want to accept both generators and sequences.)

计㈡愣 2024-12-07 13:03:24

您不一定需要有方法来处理每种类型,特别是如果只发生简单的操作。一种常见的 Pythonic 方法是执行以下操作:

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

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

You don't necessarily need to have methods to handle each type, especially if a simple operation is all that will occur. A common Pythonic approach would be to do something like:

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

This follows the Easier to ask for forgiveness than permission (EAFP) style of coding, which is generally faster and simpler.

强者自强 2024-12-07 13:03:24

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

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

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

Check out Errors and Exceptions. You could do something like this:

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

Inside your make_node function, have it raise a AlreadyNodeError if the argument is already a node.

摘星┃星的人 2024-12-07 13:03:24

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

if isinstance(my_var, str):
    my_code_here()

另外,正如您提到的,执行此操作的 Python 方法是鸭子类型,为什么不将代码放在 try 中code>/except 块?因此,只有当该值没有按预期行为时才会捕获异常(如果它像鸭子一样嘎嘎叫并且走路,那么它就是鸭子)。

Using type() to detect a variable's type is really a bad practice, as it wouldn't allow an object inheriting from the desired type (str in your case), a better way is to use isinstance():

if isinstance(my_var, str):
    my_code_here()

Also, a pythonic way of doing this would be duck-typing as you mentionned, why don't you just put the code in a try/except block? So an exception would be catched only if the value doesn't act as expected (if it quacks and walks like a duck, it's a duck).

长发绾君心 2024-12-07 13:03:24
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)))

现在,运行程序返回 Node 还是字符串并不重要。

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

Now it doesn't matter if the runner returns a Node or a string.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文