用Pythonic方法对变量中可能为非的类进行排序
我有一个类看起来或多或少像这样:用Pythonic方法对变量中可能为非的类进行排序,python,Python,我有一个类看起来或多或少像这样: class Something(): def __init__(self,a=None,b=None): self.a = a self.b = b def __lt__(self,other): return (self.a, self.b) < (other.a, other.b) 我希望能够在列表中对其进行排序,通常我只需实现如下方法: class Something(): def __in
class Something():
def __init__(self,a=None,b=None):
self.a = a
self.b = b
def __lt__(self,other):
return (self.a, self.b) < (other.a, other.b)
我希望能够在列表中对其进行排序,通常我只需实现如下方法:
class Something():
def __init__(self,a=None,b=None):
self.a = a
self.b = b
def __lt__(self,other):
return (self.a, self.b) < (other.a, other.b)
而我希望None
值被视为大于或等于以下输出:
[Something(1,1),Something(1,None)]
我想到的第一件事是把\ult\uuuu
改成:
def __lt__(self,other):
if self.a and other.a:
if self.a != other.a:
return self.a < other.a
elif self.a is None:
return True
elif other.a is None:
return False
if self.b and other.b:
if self.b != other.b:
return self.b < other.b
elif self.b is None:
return True
return False
def\uu lt\uuu(自身、其他):
如果self.a和other.a:
如果是self.a!=其他.a:
返回自我a
这将给我正确的结果,但它只是丑陋的,python通常有一个更简单的方法,我真的不想对我在对整个类进行排序时使用的每个变量都这样做(为了让问题更清楚,从这里省略)
那么,解决这个问题的pythonic方法是什么呢
注
我也尝试过以下方法,但我认为更好的方法是可能的:
这将:
def __lt__(self,other):
sorting_attributes = ['a', 'b']
for attribute in sorting_attributes:
self_value = getattr(self,attribute)
other_value = getattr(other,attribute)
if self_value and other_value:
if self_value != other_value:
return self_value < other_value
elif self_value is None:
return True
elif self_value is None:
return False
def\uu lt\uuu(自身、其他):
排序_属性=['a',b']
对于排序_属性中的属性:
self\u value=getattr(self,属性)
其他值=getattr(其他,属性)
如果自我价值和其他价值:
如果自我价值!=其他价值:
返回自身值<其他值
elif self_值为无:
返回真值
elif self_值为无:
返回错误
我真的想把Pyhton的禅宗内化,我知道我的代码很难看,那么我该如何修复它呢?一个简单的方法是将
None
转换为无穷大,即float('inf')
:
def\uu lt\uuu(自身、其他):
def转换(i):
如果i不是其他i,则返回float('inf')
返回[将(i)转换为(self.a,self.b中的i)]<[将(i)转换为(其他.a,其他.b中的i)]
针对一般情况的解决方案(可能没有方便的“大于任何值”的解决方案,并且您不希望代码随着属性数量的增加而变得更复杂),在假定无无值的常见情况下,该解决方案仍然尽可能快地运行。它确实假设TypeError
意味着None
,因此,如果除了None
之外,还可能有不匹配的类型,这会变得更加复杂,但坦率地说,像这样的类设计很难想象。这适用于具有两个或多个键的任何场景(因此attrgetter
返回一个元组
),只需要更改用于构造attrgetter
的名称即可添加或删除要比较的字段
def __lt__(self, other, _key=operator.attrgetter('a', 'b')):
# Get the keys once for both inputs efficiently (avoids repeated lookup)
sattrs = _key(self)
oattrs = _key(other)
try:
return sattrs < oattrs # Fast path for no Nones or only paired Nones
except TypeError:
for sattr, oattr in zip(sattrs, oattrs):
# Only care if exactly one is None, because until then, must be equal, or TypeError
# wouldn't occur as we would have short-circuited
if (sattr is None) ^ (oattr is None):
# Exactly one is None, so if it's the right side, self is lesser
return oattr is None
# TypeError implied we should see a mismatch, so assert this to be sure
# we didn't have a non-None related type mismatch
assert False, "TypeError raised, but no None/non-None pair seen
定义(self,other,_key=operator.attrgetter('a','b')):
#有效地为两个输入获取一次键(避免重复查找)
sattrs=_键(自)
oattrs=_键(其他)
尝试:
返回sattrs
此设计的一个有用特性是,在任何情况下,都不会对任何给定属性多次调用丰富的比较;在快速路径上的失败尝试证明了这一点(假设类型的不变量是兼容的或None
golds)可以运行零个或多个具有相等值的属性对,然后是None
/non-None
不匹配。因为我们关心的一切都是已知的相等或None
/non-None
不匹配,所以我们不需要再次调用可能代价高昂的丰富比较,我们只需要进行廉价的身份测试来找到N一个
/non-None
不匹配,然后根据哪一方是None
返回,这是我后来想到的一个完全不同的设计(单独发布,因为它非常不同,应该单独评估):
将所有属性映射到元组
s,其中每个元组
的第一个元素是基于属性的None
-ness的bool
,第二个元素是属性值本身。None
/non-None
不匹配会在表示Nonebool
上短路de>-ness防止了类型错误
,其他一切都将退回到比较好的类型:
def __lt__(self, other):
def _key(attr):
# Use attr is not None to make None less than everything, is None for greater
return (attr is None, attr)
return (_key(self.a), _key(self.b)) < (_key(other.a), _key(other.b))
def\uu lt\uuu(自身、其他):
def_键(属性):
#“使用属性”不是“无”表示“不少于一切”,而是“无”表示“更大”
返回(attr为None,attr)
返回(_键(self.a),_键(self.b))<(_键(other.a),_键(other.b))
可能比无None
/non-None
对出现的情况稍微慢一点,但代码要简单得多。它还有一个优点,就是当出现除None
/non-None
以外的不匹配类型时,它会继续引发TypeError
s,而不是潜在的行为异常。我肯定会调用它这是我的Pythonic解决方案,即使它在普通情况下稍微慢一点。您的代码会短路,并且会提前返回,而不会识别多个排序属性。这是有意的吗?为什么要比较每个元素,同时使用布尔值?使用两个属性的总和如何?预期的排序属性是什么两个值都为a时的顺序