从对象列表中获取属性的最大值

62

我有一个对象列表,其中包含x和y参数(还有其他一些东西)。

path.nodes = (
    <GSNode x=535.0 y=0.0 GSLINE GSSHARP>,
    <GSNode x=634.0 y=0.0 GSLINE GSSHARP>,
    <GSNode x=377.0 y=706.0 GSLINE GSSHARP>,
    <GSNode x=279.0 y=706.0 GSLINE GSSHARP>,
    <GSNode x=10.0 y=0.0 GSLINE GSSHARP>,
    <GSNode x=110.0 y=0.0 GSLINE GSSHARP>,
    <GSNode x=189.0 y=216.0 GSLINE GSSHARP>,
    <GSNode x=458.0 y=216.0 GSLINE GSSHARP>
)

我需要找到这个列表中的最大值y。但是,我已经尝试了以下方法:

print(max(path.nodes, key=y))

我遇到了这个错误:

NameError: name 'y' is not defined

我对Python有点陌生,而且文档没有给我任何线索。我认为我在关键字上做错了,因为如果像这样遍历节点:

for node in path.nodes:
    print(node.y)

我将获取y的值。 能否有人给我解释一下?

8个回答

110

要仅获取最大值而不是整个对象,可以使用生成器表达式:

print(max(node.y for node in path.nodes))

6
我会选择key=lambda x: x.y的方式,就像其他人一样。但是这个才是真正可以称之为pythonic的!+1 - Niklas R
1
这是有史以来最具Python风格的表达式! - Behdad

38

在何时使用“Pythonic”样式#1和lambda样式#2之间存在一个重要的区别:

max(node.y for node in path.nodes)  # (style #1)

对抗

max(path.nodes, key=lambda item: item.y)  # (style #2)
如果你仔细观察,可以发现样式#1返回属性 y 的最大值,而样式#2返回具有最大属性 ynode。这两者不同,如果想要迭代属性值或迭代包含该属性的对象,则代码用法很重要。
例子:
class node():
    def __init__(self,x):
        self.x = x
        self.y = self.x + 10

node_lst = [node(1), node(2), node(3), node(4), node(5)]
print([(e.x,e.y) for e in node_lst])

>>> [(1, 11), (2, 12), (3, 13), (4, 14), (5, 15)]

现在:

maxy = max(node.y for node in node_lst)
print(maxy)
>>> 15

max_node = max(node_lst, key=lambda node: node.y)
print(max_node.y)
>>> 15

35

有一个内置的功能可以帮助处理这种情况。

import operator

print(max(path.nodes, key=operator.attrgetter('y')))

或者:

print(max(path.nodes, key=lambda item: item.y))

编辑:但 Mark Byers 的回答最具有 Python 风格。

print(max(node.y for node in path.nodes))

1
在我的情况下,最符合Python风格的方式正是我所寻找的。但在其他示例中,使用attrgetter()是否比lambda风格更具优势? - PDXIII
1
“lambda”方法是我最喜欢的。它与“C#”和“JavaScript”很好地连接在一起。 - Sau001
1
Mark Byers的答案与您的前两个代码片段不等价。他的答案返回属性y的最大值,而不是与该值相关联的节点。 - pigi5
2
@PDXIII:两个优点:1)lambda可以做任何事情,因此维护者必须更仔细地查看它(lambda item: -item.y很容易被忽视;attrgetter只做一件事,因此知道它的维护者可以快速使用它(这是使用operatorfunctools东西而不是lambda的一般优势)。2)理论上,attrgetter可以更有效率(特别是如果它正在读取多个属性);在CPython参考解释器中,它是在C层面实现的,因此在大多数情况下,字节码解释器将不会针对每个项目调用。 - ShadowRanger

5
from operator import attrgetter
print(max(path.nodes, key=attrgetter("y")))

4

针对一个对象,实现__gt__比较运算符,并使用不带键函数的max,也是可以的。

class node:
    def __init__(self, y):
        self.y = y
    def __gt__(self, other):
        return self.y > other.y

然后类似这样:

ls = [node(3), node(5), node(11), node(0)]
print(max(ls).y)

应该输出11


0

他们已经回答了你,但如果你想获取具有最大值的对象:

max_val_object = lambda x: max(ob.value for ob in x)

0
如果y是一个属性特性,那么你甚至不需要导入operator.attrgetter。你可以使用fget方法代替:
my_node = max(path.nodes, key=Node.y.fget)

这将返回Node实例,从中获取最大的y值只需要使用my_node.y


0

y并没有被定义为一个变量;它是每个GSNode对象的属性;你不能单独使用它作为名称。

要访问单个属性,可以使用类似于key=lambda x: x.y或来自operator模块的attrgetter()的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接