len(object)还是hasattr(object, __iter__}?

0 投票
3 回答
1725 浏览
提问于 2025-04-15 16:10

(以下内容与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的性能有点担心,所以我在想是否应该使用这个设置器:

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

谢谢你的关注!

3 个回答

0

从效率的角度来看:

  • hasattr() 在最糟糕的情况下是 O(1),也就是常数时间。
  • append() 在最糟糕的情况下也是 O(1),同样是常数时间。
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 其实也不算是真正的鸭子类型风格。

4

使用 hasattr 并不会带来什么“性能损失”。它的速度非常快,你几乎很难去测量它的影响。

请不要为自己的属性使用 __(双下划线)。这样会让其他人感到困惑。

通常来说,使用 collections 中的抽象基类来处理这类事情是最好的选择。

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

这样做会让你的代码更有效,因为它能更清晰地表达你的意图。

撰写回答