我怎样才能平衡“Pythonic”和“Pythonic”?和“方便”在这种情况下?
我有一个将由客户端代码实现的“接口”:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我认为这是一个有点欺骗性的例子;有件事你还没有说出来。我猜当您说“有一个接口”时,您的意思是您有一些接受对象并调用其
run
方法的代码。如果您在调用其
run
方法之前没有测试该对象的类型,那么您正在使用鸭子类型,简单明了! (在本例中,如果它具有run
方法,那么它就是Runner
。)只要您不使用type
或 <使用run
方法在对象上使用 code>isinstance ,那么你就是 Pythonic 了。是否应该接受纯字符串或仅接受节点对象的问题是一个略有不同的问题。字符串和
node
对象可能根本不实现相同的接口!从根本上来说,字符串不会像节点
一样嘎嘎作响,因此您不必将它们视为节点。这就像一头大象走过来,如果你想让它像鸭子一样嘎嘎叫,你就必须给大象一个录音机,并先训练大象使用它。所以这不再是“鸭子打字”的问题,而是界面设计的问题。您正在尝试决定您希望界面的严格程度。
为了给你一个答案,在这个层面上,我认为假设
run
返回一个node
对象是最 Pythonic 的。无需使用isinstance
或type
来测试这一点。假设它是一个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 arun
method, then it's aRunner
.) As long as you don't usetype
orisinstance
on the object with arun
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 anode
, 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 anode
object. There's no need to useisinstance
ortype
to test for that. Just pretend it's anode
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 thatrun
should pass anode
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 nottype(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.)您不一定需要有方法来处理每种类型,特别是如果只发生简单的操作。一种常见的 Pythonic 方法是执行以下操作:
这遵循 请求宽恕比请求许可更容易(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:
This follows the Easier to ask for forgiveness than permission (EAFP) style of coding, which is generally faster and simpler.
查看 错误和异常。 你可以这样做:
在你的 make_node 函数中,有如果参数已经是一个节点,它会引发 AlreadyNodeError 。
Check out Errors and Exceptions. You could do something like this:
Inside your make_node function, have it raise a AlreadyNodeError if the argument is already a node.
使用
type()
来检测变量的类型确实是一种不好的做法,因为它不允许对象继承所需的类型(在您的情况下是str
),更好的方法是使用 isinstance() :另外,正如您提到的,执行此操作的 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 useisinstance()
: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).现在,运行程序返回 Node 还是字符串并不重要。
Now it doesn't matter if the runner returns a Node or a string.