質問

別の質問があります。

リスト「metainfo」を持つpythonクラスがあります。このリストには、 might クラスに含まれる変数名が含まれています。 self other の両方が metainfo とそれらからの同じ変数を持っている場合にTrueを返す __ eq __ メソッドを書きました変数の値は同じです。

これが私の実装です:

 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

このコードを目に見えるようにする方法について、誰か提案はありますか?できるだけ冷酷になりなさい。

役に立ちましたか?

解決

getattr を使用する個別のデフォルト値を設定する3番目の引数:

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

デフォルト値として、 省略記号 † 。したがって、値が一致するのは、両方のオブジェクトに特定の属性の同じ値が含まれている場合、または両方に同じ属性がない場合のみです。

編集 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、。 ..)など。いいえ、やらないでください:)

他のヒント

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

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

短いまたは非常に高速ではなく、理解しやすくするため:

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

" 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

また、メソッドには eq の動作を説明するドキュメント文字列が必要です。 metainfo属性の使用を説明するdocstringを持つクラスについても同様です。

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

  1. すべてのメタ情報属性に対して同じコンテンツを持つが、その他の属性に対して異なるコンテンツを持つオブジェクト(= gt;それらは等しい)
  2. 必要に応じて、等しいの可換性をチェックします。つまり、a == b:b == a
  3. メタ情報属性が設定されていないオブジェクト

私はロジックを理解しやすい別々のチャンクに分割し、それぞれが異なる条件をチェックします(そしてそれぞれが前のものがチェックされたと仮定します)。コードを表示するだけで簡単:

# 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

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

try / exceptsを使用すると、コードが読みにくくなります。私はgetattrをデフォルト値で使用しますが、それ以外の場合は存在しないことが保証されています。以下のコードでは、一時オブジェクトを作成します。そのようにして、オブジェクトに特定の値がない場合は、両方とも&quot; NOT_PRESENT&quot;を返します。したがって、等しいとカウントされます。


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

これは、センチネルオブジェクトを使用せずに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

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)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top