我还有另一个问题。
我有一个带有'metainfo'列表的python类。此列表包含我的类可能包含的变量名称。我写了一个__eq__
方法,如果self
和other
具有来自metainfo
的相同变量并且这些变量具有相同的值,则返回True。
这是我的实施:
def __eq__(self, other):
for attr in self.metainfo:
try:
ours = getattr(self, attr)
try:
theirs = getattr(other, attr)
if ours != theirs:
return False
except AttributeError:
return False
except AttributeError:
try:
theirs = getattr(other, attr)
return False
except AttributeError:
pass
return True
有没有人有任何关于如何让这个代码更容易上手的建议?尽可能无情。
答案 0 :(得分:9)
我会添加一个文档字符串来解释它所比较的内容,就像你在问题中所做的那样。
答案 1 :(得分:9)
使用getattr
的第三个参数设置不同的默认值:
def __eq__(self, other):
return all(getattr(self, a, Ellipsis) == getattr(other, a, Ellipsis)
for a in self.metainfo)
作为默认值,设置永远不会是实际值的内容,例如Ellipsis
†。因此,只有当两个对象都包含某个属性或的相同值(如果两者都没有所述属性)时,这些值才会匹配。
修改:正如Nadia指出的那样,NotImplemented
可能是更合适的常量(除非您存储了丰富的比较结果......)。
编辑2:确实,正如Lac指出的那样,仅使用hasattr
会产生更易读的解决方案:
def __eq__(self, other):
return all(hasattr(self, a) == hasattr(other, a) and
getattr(self, a) == getattr(other, a) for a in self.metainfo)
†:对于额外的默默无闻,你可以写...
而不是Ellipsis
,因此getattr(self, a, ...)
等等。不,不要这样做:)
答案 2 :(得分:5)
def __eq__(self, other):
"""Returns True if both instances have the same variables from metainfo
and they have the same values."""
for attr in self.metainfo:
if attr in self.__dict__:
if attr not in other.__dict__:
return False
if getattr(self, attr) != getattr(other, attr):
return False
continue
else:
if attr in other.__dict__:
return False
return True
答案 3 :(得分:3)
使用“Flat比嵌套更好”我会删除嵌套的try语句。相反,getattr应该返回一个仅等于自己的标记。然而,与Stephan202不同,我更喜欢保持for循环。我也会自己创建一个标记,而不是重用一些现有的Python对象。这可以保证即使在最奇特的情况下也不存在误报。
def __eq__(self, other):
if set(metainfo) != set(other.metainfo):
# if the meta info differs, then assume the items differ.
# alternatively, define how differences should be handled
# (e.g. by using the intersection or the union of both metainfos)
# and use that to iterate over
return False
sentinel = object() # sentinel == sentinel <=> sentinel is sentinel
for attr in self.metainfo:
if getattr(self, attr, sentinel) != getattr(other, attr, sentinel):
return False
return True
此外,该方法应该有一个doc字符串来解释它的 eq 行为;同样适用于应该有一个解释元信息属性使用的文档字符串的类。
最后,还应该对这种平等行为进行单元测试。一些有趣的测试用例是:
答案 4 :(得分:3)
因为它很容易理解,而不是简短或非常快:
class Test(object):
def __init__(self):
self.metainfo = ["foo", "bar"]
# adding a docstring helps a lot
# adding a doctest even more : you have an example and a unit test
# at the same time ! (so I know this snippet works :-))
def __eq__(self, other):
"""
This method check instances equality and returns True if both of
the instances have the same attributs with the same values.
However, the check is performed only on the attributs whose name
are listed in self.metainfo.
E.G :
>>> t1 = Test()
>>> t2 = Test()
>>> print t1 == t2
True
>>> t1.foo = True
>>> print t1 == t2
False
>>> t2.foo = True
>>> t2.bar = 1
>>> print t1 == t2
False
>>> t1.bar = 1
>>> print t1 == t2
True
>>> t1.new_value = "test"
>>> print t1 == t2
True
>>> t1.metainfo.append("new_value")
>>> print t1 == t2
False
"""
# Then, let's keep the code simple. After all, you are just
# comparing lists :
self_metainfo_val = [getattr(self, info, Ellipsis)
for info in self.metainfo]
other_metainfo_val = [getattr(other, info, Ellipsis)
for info in self.metainfo]
return self_metainfo_val == other_metainfo_val
答案 5 :(得分:1)
我会将逻辑划分为更容易理解的单独块,每个块检查一个不同的条件(并且假设前一个事项被检查,每个块)。最简单的只是显示代码:
# First, check if we have the same list of variables.
my_vars = [var for var in self.metainf if hasattr(self, var)]
other_vars = [var for var in other.metainf if hasattr(other, var)]
if my_vars.sorted() != other_vars.sorted():
return False # Don't even have the same variables.
# Now, check each variable:
for var in my_vars:
if self.var != other.var:
return False # We found a variable with a different value.
# We're here, which means we haven't found any problems!
return True
编辑:我误解了这个问题,这是一个更新版本。我仍然认为这是编写这种逻辑的一种明确方式,但它比我想要的更加丑陋而且根本没有效率,所以在这种情况下我可能会采用不同的解决方案。
答案 6 :(得分:1)
try / excepts使您的代码更难阅读。我会使用getattr,默认值保证不存在。在下面的代码中,我只是制作一个临时对象。这样,如果对象没有给定的值,它们都将返回“NOT_PRESENT”并因此被视为相等。
def __eq__(self, other):
NOT_PRESENT = object()
for attr in self.metainfo:
ours = getattr(self, attr, NOT_PRESENT)
theirs = getattr(other, attr, NOT_PRESENT)
if ours != theirs:
return False
return True
答案 7 :(得分:1)
这是一个非常容易阅读IMO的变体,不使用sentinel对象。它首先会比较是否有属性,然后比较值。
可以使用all()和生成器表达式在一行中完成,但是我觉得这更具可读性。
def __eq__(self, other):
for a in self.metainfo:
if hasattr(self, a) != hasattr(other, a):
return False
if getattr(self, a, None) != getattr(other, a, None):
return False
return True
答案 8 :(得分:0)
我喜欢Stephan202的回答,但我认为他的代码并没有让平等条件足够清晰。这是我的看法:
def __eq__(self, other):
wehave = [attr for attr in self.metainfo if hasattr(self, attr)]
theyhave = [attr for attr in self.metainfo if hasattr(other, attr)]
if wehave != theyhave:
return False
return all(getattr(self, attr) == getattr(other, attr) for attr in wehave)