4

もう一つ質問があります。

リスト「metainfo」を持つpythonクラスがあります。このリストには、クラスに含まれる可能性のある変数名が含まれています。との両方が同じ変数を持ち、それらの変数が同じ値を持つ__eq__場合に True を返すメソッドを作成しました。selfothermetainfo

これが私の実装です:

 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

このコードを見やすくする方法について何か提案はありますか? 好きなだけ冷酷になってください。

4

9 に答える 9

9

質問で行ったように、比較対象を説明するdocstringを追加します。

于 2009-11-26T13:42:49.070 に答える
9

の3番目の引数を使用getattrして、個別のデフォルト値を設定します。

def __eq__(self, other):
    return all(getattr(self, a, Ellipsis) == getattr(other, a, Ellipsis)
               for a in self.metainfo)

デフォルト値として、 †など、実際の値にはならないものを設定します。したがって、値が一致するのは、両方のオブジェクトに特定の属性の同じ値が含まれている場合、または両方にその属性がない場合のみです。Ellipsis

編集ナディアが指摘している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, ...)など。いいえ、それをしないでください:)

于 2009-11-26T13:45:52.230 に答える
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
于 2009-11-26T13:46:29.953 に答える
3

「フラットはネストよりも優れている」を使用すると、ネストされた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

また、メソッドには、eqの動作を説明するdoc-stringが必要です。metainfo属性の使用を説明するdocstringが必要なクラスについても同じことが言えます。

最後に、この平等な振る舞いの単体テストも存在する必要があります。いくつかの興味深いテストケースは次のとおりです。

  1. すべてのmetainfo属性について同じコンテンツを持っているが、他のいくつかの属性については異なるコンテンツを持っているオブジェクト(=>それらは等しい)
  2. 必要に応じて、等しいの可換性をチェックします。つまり、a == bの場合:b == a
  3. metainfo-attributesが設定されていないオブジェクト
于 2009-11-26T14:15:03.547 に答える
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
于 2009-11-26T14:16:35.913 に答える
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

編集:私は質問を誤解しました、ここに更新されたバージョンがあります。これはこの種のロジックを書くための明確な方法だと思いますが、意図したよりも醜く、まったく効率的ではないため、この場合はおそらく別の解決策を使用します。

于 2009-11-26T13:45:06.587 に答える
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
于 2009-11-26T14:09:16.960 に答える
1

これは、センチネル オブジェクトを使用せずに、IMO を非常に読みやすいバリアントです。最初に両方が属性を持っているか持っていないかを比較し、次に値を比較します。

Stephen が行ったように all() とジェネレーター式を使用して 1 行で実行できますが、こちらの方が読みやすいと思います。

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
于 2009-11-26T14:16:58.770 に答える
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)
于 2009-11-26T14:16:38.457 に答える