2

オブジェクト (Foo) のリストがあります。Foo オブジェクトにはいくつかの属性があります。Foo オブジェクトのインスタンスは、すべての属性が等しい場合 (その場合にのみ)、Foo オブジェクトの別のインスタンスと同等 (等しい) です。

次のコードがあります。

class Foo(object):
    def __init__(self, myid):
        self.myid=myid

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            print 'DEBUG: self:',self.__dict__ 
            print 'DEBUG: other:',other.__dict__ 
            return self.__dict__ == other.__dict__
        else:
            print 'DEBUG: ATTEMPT TO COMPARE DIFFERENT CLASSES:',self.__class__,'compared to:', other.__class__
            return False    


import copy

f1 = Foo(1)
f2 = Foo(2)
f3 = Foo(3)
f4 = Foo(4)
f5 = copy.deepcopy(f3) # overkill here (I know), but needed for my real code

f_list = [f1,f2,f3,f4,f5]

# Surely, there must be a better way? (this dosen't work BTW!)
new_foo_list = list(set(f_list))

単純な型 (int、float、string、そして驚くべきことに datetime.datetime 型) を扱う場合、上記の (set と back に変換する) この小さな (アンチ?) 'パターン' をよく使用しましたが、より多くの関連するデータ型 - 上記の Foo のように。

では、上記のリスト f1 を一意のアイテムのリストに変更するにはどうすればよいでしょうか。各アイテムをループして一時キャッシュなどに既に存在するかどうかを確認する必要はありません。

これを行うための最もpythonicな方法は何ですか?

4

5 に答える 5

8

最初に強調しておきたいのは、使用setは確かにアンチパターンではないということです。setO(n) 時間で重複を排除します。これは、実行できる最善の方法であり、すべてのアイテムを他のすべてのアイテムと比較する単純な O(n^2) ソリューションよりもはるかに優れています。並べ替えよりも優れています。実際、データ構造自然な順序でさえない可能性があるようです。その場合、並べ替えはあまり意味がありません。

この場合にセットを使用する際の問題は、カスタム__hash__メソッドを定義する必要があることです。他の人はこう言っています。しかし、それが簡単にできるかどうかは未解決の問題です。それは、あなたが教えてくれなかった実際のクラスの詳細に依存します。たとえば、Foo上記のオブジェクトのいずれかの属性がハッシュ可能でない場合、カスタム ハッシュ関数の作成は困難になります。Fooオブジェクトのカスタム ハッシュを作成するだけでなく、カスタム ハッシュも作成する必要があるためです。他のすべてのタイプのオブジェクトに!

したがって、決定的な答えが必要な場合は、クラスがどのような種類の属性を持っているかについて詳しく説明する必要があります. しかし、私はいくつかの推測を提供することができます.

Niklas B. が指摘するように、オブジェクトに対してハッシュ関数を作成できると仮定Fooしますが、そのFooオブジェクトは変更可能であり、実際にはメソッドを持つべきではないと仮定すると、実行可能なアプローチの 1 つがここにあります。の可変インスタンスを指定すると、 のデータの不変コレクションを返す__hash__関数を作成します。たとえば、Foo に aと aが含まれているとします。s ( を表す) と別の( を表す)を含む を返します。関数には次のプロパティが必要です。freezeFooFoodictlistfreezetupletupletupledicttuplelistfreeze

freeze(a) == freeze(b)

場合に限り

a == b

次のコードを使用してリストを渡します。

dupe_free = dict((freeze(x), x) for x in dupe_list).values()

これで、O(n) 時間で重複のないリストが作成されました。(実際、この提案を追加した後、fraxelが同様のことを提案しているのを見ました。しかし、彼のように(x.freeze(), x)依存するよりも、カスタム関数 (またはメソッド) を使用する方が良い方法だと思います。__dict__同じことがあなたのカスタム__eq__メソッド、IMOにも当てはまります -__dict__私がここに入ることができないさまざまな理由から、常に安全なショートカットではありません.)

もう 1 つのアプローチは、最初から不変オブジェクトのみを使用することです。たとえば、namedtuples を使用できます。python docs から盗んだ例を次に示します。

>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
>>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
33
>>> x, y = p                # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y               # fields also accessible by name
33
>>> p                       # readable __repr__ with a name=value style
Point(x=11, y=22)
于 2012-05-10T14:08:27.710 に答える
3

set(または)を使用してみましたfrozensetか? 一意のアイテム セットを保持するためのものです。

__hash__ただし、適切なメソッドを作成する必要があります。set(およびfrozenset)__hash__メソッドを使用してオブジェクトをハッシュします。__eq__AFAIK、衝突でのみ使用されます。したがって、 のようなハッシュを使用する必要がありますhash(frozenset(self.__dict__.items()))

于 2012-05-10T13:55:55.680 に答える
3

ドキュメントによると、カスタム クラスがまたはで正しく動作するには、__hash__()とを定義する必要があります。どちらも CPython のハッシュ テーブルを使用して実装されているためです。__eq__()setfrozenset

を実装する場合は__hash__、 if a == b, thenは にhash(a)等しくなければならないことに注意してくださいhash(b)。s全体を比較するのではなく__dict__、単純なクラスに対して次のようなより直接的な実装をお勧めします。

class Foo(object):
    def __init__(self, myid):
        self.myid = myid

    def __eq__(self, other):
        return isinstance(other, self.__class__) and other.myid == self.myid

    def __hash__(self):
        return hash(self.myid)

オブジェクトに変更可能な属性が含まれている場合、それをセット内に配置したり、辞書のキーとして使用したりしないでください。

于 2012-05-10T14:01:07.467 に答える
1

__dict__.items()インスタンスをキーにした辞書を作成するだけです。

f_list = [f1,f2,f3,f4,f5]
f_dict = dict([(tuple(i.__dict__.items()), i) for i in f_list])
print f_dict
print f_dict.values()
#output:
{(('myid', 1),): <__main__.Foo object at 0xb75e190c>, 
 (('myid', 2),): <__main__.Foo object at 0xb75e184c>, 
 (('myid', 3),): <__main__.Foo object at 0xb75e1f6c>, 
 (('myid', 4),): <__main__.Foo object at 0xb75e1cec>}
[<__main__.Foo object at 0xb75e190c>, 
 <__main__.Foo object at 0xb75e184c>, 
 <__main__.Foo object at 0xb75e1f6c>, 
 <__main__.Foo object at 0xb75e1cec>]

このようにして、ディクショナリに属性に基づいた一意性を処理させ、値を取得することでオブジェクトを簡単に取得できます。

于 2012-05-10T14:22:24.687 に答える
-1

許可されている場合は、セットhttp://docs.python.org/library/sets.htmlを使用できます

list = [1,2,3,3,45,4,45,6]
print set(list)
set([1, 2, 3, 4, 6, 45])
x = set(list)
print x
set([1, 2, 3, 4, 6, 45])
于 2012-05-10T13:57:47.427 に答える