【问题标题】:len(object) or hasattr(object, __iter__)?len(object) 还是 hasattr(object, __iter__)?
【发布时间】:2009-11-19 14:07:29
【问题描述】:

(以下是与python3相关的(如果有的话)。)

这是我编写的代码(简化):

class MyClass:

    def __init__(self):
        self.__some_var = []

    @property
    def some_var(self):
        return self.__some__var

    @some_var.setter
    def some_var(self, new_value):
        if hasattr(new_value, '__iter__'):
            self.__some_var = new_value
        else:
            self.__some_var.append(new_value)

我想在设置它们是否是多个“值”时替换(即,如果 new_value 是可迭代的,在我的情况下,是不可迭代的对象)并且如果它们只是一个“值”则追加。 我担心 hasattr 的性能,所以我想知道我是否不应该使用这个 setter:

    @some_var.setter
    def some_var(self, *args):
        if len(args) > 1:
            self.__some_var = args
        else:
            self.__some_var.append(args)

感谢您的关注!

【问题讨论】:

  • 过早的优化有人吗?做一个简单的基准测试,看看差异是否可以测量。如果有的话,我希望hasattr 更快。
  • @thomas,在您的第一种形式中,您是否确定new_value"thomas" 时要“替换”?字符串有__iter__,并且是序列,但在代码中几乎总是我们希望将它们视为标量。 (当然,第二种形式的语义完全不同,因此它总是会“替换”,即使使用列表调用也是如此)。
  • 事实上,如果 new_value 的 values() 元素有一些方法,它们只是在做作之前的一个测试,你可以将特定行为的对象传递给这个函数,否则会引发错误。

标签: python


【解决方案1】:

hasattr 的“性能成本”并不重要。它足够快,你很难测量它。

请不要将__(双下划线)用于您自己的属性。这让我们其他人感到困惑。

通常最好使用collections 抽象基类成员资格来处理这类事情。

if isinstance( arg, collections.Sequence ):
    self._some_var = list(arg)
else:
    self._some_var.append( arg )

这为您提供了一些可能会更好地工作的东西,因为它更清楚地表达了语义。

【讨论】:

  • 我相信并且我可能会误解,isinstance 通常是一种不好的做法,Python 的方式是鸭式打字。此外,我曾经因为不同的导入而遇到过 isinstance 和 issubclass 的问题,所以我更愿意避免使用它们。
  • 在你的情况下,你需要知道它是否是一个集合。 len('aaa') > 1,也是。
  • 既然你是根据类型(是序列还是不是序列)来区分,那么你别无选择,只能问isinstance。另一种方法是定义您自己的 collections.Sequence 子类,使用您自己的更新 MyClass 的方法——这是正确的“鸭子类型”解决方案。比检查 Sequence 的子类更复杂。
【解决方案2】:

另一种可能是鸭式打字:

这里我假设您想从可迭代对象中评估(=制作一个列表),因为它可能是一个迭代器,您需要立即展开以保存。

@some_var.setter
def some_var(self, new_value):
    try:
        self.__some_var = list(new_value)
    except TypeError:
        self.__some_var.append(new_value)

如果new_value 不可迭代,我们期望list() 提升ValueError。作为对您对 S.Lott 回答的评论的回应,我认为使用 hasattr 也不是真正的鸭式打字风格。

【讨论】:

  • 扩展可迭代对象是一种非常糟糕的做法。
【解决方案3】:

在效率方面:

  • hasattr() 的最坏情况是 O(1)(常数)。
  • append() 也是 O(1)(常数)的最坏情况。

【讨论】:

  • 我怀疑是个小问题。 Hasattr 原则上是一个 dict 查找(或多个),所以它是 O(1)
  • @kaizer.se 对 - 我在想整个列表正在被检查以查看一个元素是否已经在列表中。如果元素不在列表中,或者它在列表的末尾,那将是 O(n) 最坏的情况。我重新阅读了代码,他实际上在做的是检查 new_value 是否是一个集合本身。现在更有意义了。
猜你喜欢
  • 2011-11-04
  • 1970-01-01
  • 2020-04-23
  • 1970-01-01
  • 2015-01-20
  • 2019-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多