14

我知道这听起来可能是一个愚蠢的问题,尤其是对于了解 python 本质的人来说,但我只是想知道,有没有办法知道一个对象是否“实现了一个接口”以便说?

举一个我想说的例子:

假设我有这个功能:

def get_counts(sequence):
     counts = {}
     for x in sequence:
         if x in counts:
             counts[x] += 1
         else:
             counts[x] = 1
     return counts

我的问题是:有没有办法确保传递给函数的对象是iterable我知道在 Java 或 C# 中,我可以通过让方法接受任何实现特定接口的对象来做到这一点,比如说(例如)iIterable这样的:void get_counts(iIterable sequence)

我的猜测是,在 Python 中,我将不得不使用先发制人的内省检查(decorator 也许?),exception如果对象没有__iter__属性,则抛出自定义)。但是有没有更蟒蛇的方式来做到这一点?

4

4 回答 4

11

Python(自 2.6 起)具有抽象基类(也称为虚拟接口),它比 Java 或 C# 接口更灵活。要检查对象是否可迭代,请使用collections.Iterable

if isinstance(obj, collections.Iterable):
    ...

但是,如果您的else块只会引发异常,那么大多数 Python 答案是:不要检查!由您的调用者传入适当的类型;你只需要记录你期待一个可迭代的对象。

于 2012-12-17T18:23:07.550 回答
9

isinstance()在或接口之前使用多态性和鸭子类型

您通常定义要对对象执行的操作,然后使用多态性来调整每个对象对您想要执行的操作的响应方式,或者使用鸭子类型;首先测试手头的对象是否可以做你想做的事情。这是调用与自省的权衡,传统观点认为调用优于自省,但在 Python 中,鸭式打字优于isinstance测试。

因此,您首先需要弄清楚为什么需要过滤某些东西是否是可迭代的;为什么你需要知道这个?只需使用try: iter(object),except TypeError: # not iterable进行测试。

或者,如果传递的任何内容都不是可迭代的,您可能只需要抛出异常,因为这会发出错误信号。

基础知识

使用鸭式打字,您可能会发现您必须测试多种方法,因此isinstance()测试可能看起来是更好的选择。在这种情况下,也可以选择使用抽象基类 (ABC) ;例如,使用 ABC 让您“绘制”几个不同的类作为给定操作的正确类型。使用 ABC 让您专注于需要执行的任务,而不是使用的具体实现;你可以有一个PaintableABC,一个PrintableABC等。

Zope 接口和组件架构

如果你发现你的应用程序使用了非常多的 ABC,或者你不得不在类中添加多态方法来处理各种不同的情况,那么下一步就是考虑使用成熟的组件架构,例如Zope 组件架构(ZCA)

zope.interface接口是类固醇上的 ABC,尤其是与 ZCA 适配器结合使用时。接口记录类的预期行为:

if IFrobnarIterable.providedBy(yourobject):
    # it'll support iteration and yield Frobnars.

但它也让你查找适配器;您无需将每次使用形状的所有行为都放在类中,而是实现适配器来为特定用例提供多态行为。您可以将对象调整为可打印、可迭代或可导出为 XML:

class FrobnarsXMLExport(object):
    adapts(IFrobnarIterable)
    provides(IXMLExport)

    def __init__(self, frobnariterator):
        self.frobnars = frobnariterator

    def export(self):
        entries = []
        for frobnar in self.frobnars:
            entries.append(
                u'<frobnar><width>{0}</width><height>{0}</height></frobnar>'.format(
                    frobnar.width, frobnar.height)
        return u''.join(entries)

并且您的代码只需查找每个形状的适配器:

for obj in setofobjects:
    self.result.append(IXMLExport(obj).export())
于 2012-12-17T18:27:20.757 回答
2

Pythonic 的方式是使用鸭子类型和“请求宽恕,而不是许可”。这通常意味着在一个try块中执行一个操作,假设它的行为方式与您期望的一样,然后在一个except块中处理其他情况。

于 2012-12-17T18:24:17.860 回答
1

我认为这是社区建议您这样做的方式:

import sys

def do_something(foo):
    try:
        for i in foo:
            process(i)
    except:
        t, ex, tb = sys.exc_info()
        if "is not iterable" in ex.message:
            print "Is not iterable"

do_something(True)

或者,您可以使用类似zope.interface.

于 2012-12-17T18:27:11.630 回答