getattr访问第三个参数,即使不满足条件

时间:2019-06-19 13:32:55

标签: python python-3.x

仅当属性存在时,我才尝试获取列表中类的属性的值。如果属性不存在,我希望它得到其他东西(我知道它是内部列表,并且想要在该内部列表中获得项目的属性)。

以下代码中的示例可以运行和调试:

class Block(object):
    def __init__(self, name):
        self.block_name = name


blocks_list = [Block("d"), Block("d"), Block("d")]
blocks = [Block("a"), Block("b"), blocks_list, Block("c")]

# this is ok
block_num = 2
name = getattr(blocks[block_num], 'block_name', blocks[block_num][0].block_name)

# this is raises exception
block_num = 0
name = getattr(blocks[block_num], 'block_name', blocks[block_num][0].block_name)

例外:

  

name = getattr(blocks [block_num],'block_name',   blocks [block_num] [0] .block_name)TypeError:“阻止”对象没有   支持索引

我不明白为什么getattr在不应该出现的情况下在第三个argumnet上引发异常。

我的最终目标是确定列表或内部列表中所有项目的名称。

谢谢。

2 个答案:

答案 0 :(得分:1)

您的第一种情况有效,因为block_num = 2是列表,blocks[2][0]<__main__.Block at 0x....>是对象blocks[2]

在第一种情况下,您为getattr(blocks[block_num], 'block_name', blocks[block_num][0].block_name)拥有block_num = 2blocks[block_num]是一个列表,并且该列表将不包含block_name,因此默认值{{ 1}}返回

在第二种情况下,对于blocks[block_num][0].block_name,调用函数时会评估默认值,但由于block_num=0是对象TypeError: 'Block' object is not subscriptable,最终会抛出blocks[0] ,并且您无法为对象编制索引

为保持一致,一个建议可能是改为<__main__.Block at 0x....>

然后更新的代码可能看起来像

blocks_list[block_num].block_name

哪个给你

class Block(object):
    def __init__(self, name):
        self.block_name = name


blocks_list = [Block("d"), Block("d"), Block("d")]
blocks = [Block("a"), Block("b"), blocks_list, Block("c")]

block_num = 2
name = getattr(blocks[block_num], 'block_name', blocks_list[block_num].block_name)
print(name)

block_num = 0
name = getattr(blocks[block_num], 'block_name', blocks_list[block_num].block_name)
print(name)

答案 1 :(得分:1)

我认为您理解为什么在第二个示例中,第三个参数引发错误-这是因为blocks[0]不是列表,因此无法对其进行索引。我还认为您期望不会出现错误,因为仅当blocks[0]没有属性'block_name'时才应评估第三个参数(因为getattr()的第三个参数是默认值)。

不幸的是,它不能那样工作。您给getattr()(即blocks[block_num][0])的第三个参数是在调用函数时计算的,而不是在执行时计算的。

但是,有一个解决方法:三元运算符,它可以执行与getattr()相同的操作(如果存在则返回一个属性,如果不存在则返回默认值),但是在执行时而不是在调用时评估其他参数:

name = blocks[block_num].block_name if hasattr(blocks[block_num], 'block_name') else blocks[block_num][0].block_name

在调用getattr()时,在执行函数之前,将同时评估三个参数。在我介绍的表达式中,它们按照以下顺序进行求值:

  1. if hasattr(blocks[block_num], 'block_name')
  2. blocks[block_num].block_name(如果1为真)
  3. blocks[block_num][0].block_name(如果1为假)

应该可以解决您的问题。