【问题标题】:Duck typing trouble. Duck typing test for "i-am-like-a-list"鸭打字麻烦。 “i-am-like-a-list”的鸭子打字测试
【发布时间】:2015-08-01 00:27:05
【问题描述】:

在末尾添加使用上下文

我经常想对列表等抽象对象进行操作。例如

def list_ish(thing):
    for i in xrange(0,len(thing)):
        print thing[i]

现在,如果 thing 是一个列表,这是合适的,但如果 thing 是一个 dict 则将失败。 pythonic 为什么要问“你表现得像一个列表吗?”

注意:

hasattr('__getitem__') and not hasattr('keys')

这适用于我能想到的所有情况,但我不喜欢消极地定义鸭子类型,因为我预计可能会有它无法捕捉到的情况。

我真正想问的是。
“嘿,你是否像我期望的列表那样对整数索引进行操作?”例如

  thing[i],  thing[4:7] = [...],   etc.

注意:我不想简单地在大型 try/except 中执行我的操作,因为它们具有破坏性。在这里尝试失败并不酷......

使用背景 --“点列表”是一个类似列表的东西,它包含类似字典的东西作为它的元素。 -- 一个“矩阵”是一个类似列表的东西,包含类似列表的东西

-- 我有一个函数库,可以在点列表上运行,也可以在类似矩阵的东西上以类似的方式运行。

——例如,从用户的角度来看,破坏性操作,如“电子表格”操作“列切片”可以以类似的方式对矩阵对象和点列表对象进行操作——结果东西和原来的一样,但只有指定的列。

-- 因为这个特定的操作是破坏性的,所以如果一个对象是一个矩阵来进行,只是在操作的一部分中发现,它实际上是一个点列表或者不是--以上。

-- 我希望我的 'is_matrix' 和 'is_point_list' 测试是高效的,因为它们有时会发生在内部循环中。因此,我会对仅调查零元素的测试感到满意。

-- 我更喜欢不涉及构建临时对象的测试,只是为了确定对象的类型,但也许这不是 python 的方式。

总的来说,我发现整个鸭子打字有点乱,并且充满了错误和缓慢,但也许我还没有像真正的 Pythonista 那样思考

很高兴喝更多的酷爱...

【问题讨论】:

  • 在 Python 中进行 Duck Typing 的公认方法是将其视为鸭子,然后使用 try/except 来发现它不起作用。您能否更详细地说明一下您认为这对您的情况不起作用的原因?
  • 只检查thing的类型?例如。 stackoverflow.com/questions/1835018/…
  • 你只是想快速失败吗?或者你真的需要确定类型吗?如果有人传入了一个没有派生 list 的鸭式“列表”,那么您需要多少 list 功能,或者期望他们会实现多少?
  • 马上开始,使用for item in thing: print item 比迭代一系列索引更符合Pythonic。
  • @dagetz 就像 'sort' 不返回任何值,而是直接修改它正在排序的列表,我的 column_slice 操作修改了点列表中的所有点,或者它修改了矩阵中的内部列表。所以它是破坏性的——我的用例是对非常大的数据集进行串行操作,所以就像 numpy 模块一样,我允许用户就地修改这些结构,并要求他们显式复制它们,当他们想要分离效果时从另一个地方的动作中放置。

标签: python list


【解决方案1】:

您可以做的一件事是,应该在正常的 list 上快速工作,而在正常的 dict 上失败,是从前面获取零长度切片:

try:
    thing[:0]
except TypeError:
    # probably not list-like
else:
    # probably list-like

切片在dicts 上失败,因为切片不可散列。

但是,strunicode 也通过了此测试,并且您提到您正在进行破坏性编辑。这意味着您可能还想检查__delitem____setitem__

def supports_slices_and_editing(thing):
    if hasattr(thing, '__setitem__') and hasattr(thing, '__delitem__'):
        try:
            thing[:0]
            return True
        except TypeError:
            pass
    return False

我建议您组织您对输入的要求,以及您希望您的函数处理的可能输入的范围,比您迄今为止在您的问题中更明确。如果你真的只想处理lists 和dicts,你会使用isinstance,对吧?也许您的方法所做的只能删除项目,或者只能替换项目,因此您不需要检查其他功能。记录这些要求以供将来参考。

【讨论】:

    【解决方案2】:

    在处理内置类型时,可以使用Abstract Base Classes。在您的情况下,您可能希望针对 collections.Sequencecollections.MutableSequence 进行测试:

    if isinstance(your_thing, collections.Sequence):
        # access your_thing as a list
    

    2.6 之后(包括)的所有 Python 版本都支持此功能。

    如果您使用自己的类来构建your_thing,我建议您也从这些抽象基类继承(直接或间接)。这样就可以保证序列接口的正确实现,避免所有的打字乱码。

    对于第三方库,如果第三方类没有从内置类型或抽象类继承,则没有简单的方法来检查序列接口。在这种情况下,您必须检查您将要使用的每个界面,并且检查您使用的界面。比如你的list_ish函数使用了__len____getitem__,所以只检查这两个方法是否存在。 __getitem__ 的错误行为(例如 dict)应该引发异常。

    【讨论】:

    • 不过,依赖那些 ABC 是危险的;它们缺少 __subclasshook__s,例如,issubclass(shelve.Shelf, collections.Mapping)False,即使 shelve.Shelf 是标准库代码。
    • @user2357112 那么这是库中的一个错误。而且我相信它已在 3.4 中修复
    • 它不固定。 shelve.Shelf 继承自 Python 3.4 中的 MutableMapping,因此该示例不再重现该错误,但是对于不从 ABC 继承且未显式注册为的任何类,您仍然会遇到相同的问题一个子类。 There's an open bug report on the bug tracker about it.
    【解决方案3】:

    也许他们在这里不是理想的pythonic答案,所以我提出了一个'hack'解决方案,但对python的类结构了解不够,无法知道我是否正确:

    def is_list_like(thing):
        return hasattr(thing, '__setslice__')
    
    def is_dict_like(thing):
        return hasattr(thing, 'keys')
    

    我的 reduce 目标是简单地进行性能测试:

    • (1) 永远不要将 dict-thing 或类似字符串的东西称为 list 列表项
    • (2) 返回python类型的正确答案
    • (3) 如果有人为列表/字典实现“完整”的核心方法集,将返回正确答案
    • (4) 速度快(理想情况下不会在测试期间分配对象)

    编辑:结合@DanGetz 的想法

    【讨论】:

    • 看起来更快,是的。您是否打算在序列的每个元素上调用它?我以为你有所有元素都属于同一类型的序列,在这种情况下,速度不会是什么大问题。你应该知道官方文档没有提到__setslice__这个特殊方法。 “官方”方式似乎是使用 slice() 值调用 __setitem__
    • @DanGetz:感谢您的耐心等待。我采用了您的“官方”方法,但仅以执行 hasattr 的方式。 (是的,在数据​​军事例程中,当我通过结构递归时,我将对每个元素执行一个大的“type_case”,以便为这些结构执行正确的专门转储/加载。)
    • 对不起,我应该更具体一点:我说的不是list.slice() 方法;那不存在。我指的是内置的slice objects,可以传入__setitem__
    • @DanGetz,明白了。是的,我刚刚验证了 'slice' 不是 basestring 的属性,也不是 dict 的属性,但没有验证它是 list 的属性!好的,我会回复我的答案。 (作为记录,我通过在 dir() 上为各个类设置差异来得出我的答案。整个事情很老套,但它会达到我的目的。非常感谢你的奉献!
    猜你喜欢
    • 2011-11-25
    • 2011-03-23
    • 1970-01-01
    • 2012-10-26
    • 2011-05-11
    • 2020-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多