テスト開発のためのPythonコアノート(21):オブジェクトの比較とコピー

21.1変数とオブジェクトの関係

Pythonのすべては、すべてオブジェクトである変数、関数、クラスを含むオブジェクトです。式a='s'の文字列's'がオブジェクトです。言い換えると、変数aはオブジェクト's'を指します。変数は、オブジェクトを識別するラベルのようなものです。

各オブジェクトは、識別子(ID)、タイプ(type)、値(value)の3つの部分で構成されています。

識別子:各オブジェクトには、それ自体を識別するための一意の識別子があります。組み込み関数id()を使用して、オブジェクトの識別子を表示できます。たとえば、変数aの識別子を見ると、識別子はこの値がオブジェクトのメモリアドレスであると単純に考えることができます。

>>> id(a)
4310511984

タイプ:オブジェクトによって保存されるデータのタイプを識別します。データタイプは、オブジェクトで実行できる値と操作の範囲を制限します。組み込み関数type()を使用して、オブジェクトタイプをチェックできます。たとえば、変数aが指すオブジェクトのタイプを見てください。出力から、それが文字列型であるタイプstrのオブジェクトであることがわかります。

>>> type(a)
<class 'str'>

値:オブジェクトによって保存されたデータ情報を表します。print()関数を使用して印刷します。

>>> print(a)
's'

メモリ内のa='s'の表現は、次のグラフで表すことができます。
ここに画像の説明を挿入

b = aが実行されると、bはaが指すオブジェクトも指します。これは次のように表すことができます。
ここに画像の説明を挿入

21.2オブジェクトの比較

21.2.1'=='演算子は値を比較します

a == bを実行することは、を実行することと同等a.__eq__(b)であり、Pythonのほとんどのデータ型は__eq__この関数をオーバーロードし、その内部処理は通常、より複雑になります。たとえば、リストの場合、__eq__関数はリスト内の要素を反復処理し、それらの順序と値を比較して等しいかどうかを確認します。タプルについても同じことが言えます。


t1 = (1, 2, [3, 4])
t2 = (1, 2, [3, 4])
print(t1 == t2)
​
​
t1[-1].append(5)
print(t1 == t2)

21.2.2「is」演算子はIDを比較します

それらは同じオブジェクトであり、同じメモリアドレスを指していますか。

以下に2つのクラスがあります。1つはPersonクラスで、もう1つはシングルトンパターンのMyClassクラスです。オブジェクトのIDは両方とも4448078200であるため、出力からs1とs2が同じオブジェクトであることがわかります。2つのオブジェクトbとcの属性値は同じですが、オブジェクトIDが異なるため同じオブジェクトではありません。1つは4448078256で、もう1つは4448078312です。

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
​
​
class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance
​
​
class MyClass(Singleton):
    pass
​
​
if __name__ == '__main__':
    s1 = MyClass()
    s2 = MyClass()
    b = Person("Leon", 12)
    c = Person("Leon", 12)
    print(id(s1), id(s2))  # 相同的对象, 输出4448078200 4448078200
    assert s1 == s2
    print(id(b), id(c))  # 两个不同的对象, 输出4448078256、 4448078312
    assert b == c  # AssertionError

21.2.3カスタムオブジェクト比較ルール

__eq__オブジェクト==演算子の比較ルールをカスタマイズする

たとえば、次のコードは2つのPersonオブジェクトを比較します。名前と年齢の属性値が等しい場合、==操作を比較すると、2つのオブジェクトはTrueであると見なされます。

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
​
    def __eq__(self, obj):
        return self.name == obj.name and self.age == obj.age
​
​
if __name__ == '__main__':
    a = Person("Leon", 23)
    b = Person("Leon", 12)
    c = Person("Leon", 12)
    print(a, b, c)  # 三个不同的对象
    assert b == c  # 这两个对象==操作符结果是False
    assert a == b  # 这两个对象的==操作符结果是True

21.3オブジェクトのコピー

21.3.1 浅拷贝(shallow copy)

浅いコピーとは、メモリの一部を再割り当てし、要素が元のオブジェクトの子オブジェクトへの参照である新しいオブジェクトを作成することを意味します。浅いコピーオブジェクトを作成するには、次の3つの方法があります。

  1. データ型自体のコンストラクターを使用して、元のオブジェクトの浅いコピーを取得します。
    l2はl1の浅いコピーであり、s2はs1の浅いコピーです。
l1 = [[1, 2], (30, 40)]
l2 = list(l1)print(l1 == l2) # Trueprint(l1 is l2)  # 创建了一个新的对象  False
​
​
s1 = set([1, 2, 3])
s2 = set(s1)
  1. リストスライスを使用して、元のオブジェクトの浅いコピーを取得します。
l1 = [1, 2, 3]
l2 = l1[:]print(l1 == l2) # Trueprint(l1 is l2)  # 创建了一个新的对象 # False
  1. 関数copy.copy()は、浅いコピーを実行します
import copy
l1 = [1, 2, 3]
l2 = copy.copy(l1) 

元のオブジェクトの要素が変更可能である場合、元のオブジェクトを変更すると、コピーされたオブジェクトにも影響します。例えば:

l1 = [[1, 2], (30, 40)]
l2 = list(l1)
​
l1.append(100)  # 原对象增加一个元素100,不会影响浅拷贝对象l2
l1[0].append(3)  # 影响l2
print(l1)  # 输出 [[1, 2, 3], (30, 40), 100]
print(l2)  # 输出 [[1, 2, 3], (30, 40)]
​
l1[1] += (50, 60)  # 通过+号,l1第二个元素新建了一个列表
print(l1)  # 输出 [[1, 2, 3], (30, 40, 50, 60), 100]
print(l2)  # 输出 [[1, 2, 3], (30, 40)]  ,没受l1变化的影响

なぜなら、シャローコピーオブジェクトの要素は、元のオブジェクトの子オブジェクトへの参照であるためです。したがって、元のオブジェクト(l1)の変数要素の変更はl2に影響します。

l1.append(100)は、要素100をl1のリストに追加することを意味します。l2とl1は全体として2つの異なるオブジェクトであり、メモリアドレスを共有しないため、この操作はl2に影響を与えません。

l1の最初の要素[1,2]は要素3を追加し、l2の最初の要素も[1、2、3]になります。l1の2番目の要素はタプルです。タプルは不変であるため、このタプルは2つの要素(50、60)を追加します。これは、l1の2番目のタプルをスプライスしてから、新しいタプルを再作成することを意味します。新しいタプルはl1の2番目の要素です。 、および新しいタプルはl2で参照されないため、l2は影響を受けません。

21.3.2ディープコピー

ディープコピーは、元のオブジェクトのすべてのサブオブジェクトを再帰的にコピーします。したがって、ディープコピー後のオブジェクトと元のオブジェクトは相互に関連していません。

deepcopied = copy.deepcopy(origin)は、ディープコピーオブジェクトを取得します。

import copy
l1 = [[1, 2], (30, 40)]
l2 = copy.deepcopy(l1)   # l1 和 l2 互不相干,各自改变不影响对方
l1.append(100)
l1[0].append(3)
​
l1
[[1, 2, 3], (30, 40), 100]
​
l2 
[[1, 2], (30, 40)]

21.3.3コピーではない

タプルコンストラクターとスライスは、元のオブジェクトと同じオブジェクトになります。コピーは別のオブジェクトを取得するためのものであるため、コピーではありません。

タプルのコンストラクターはコピーではなく、同じオブジェクトへの参照を追加するだけです。

t1 = (1, 2, 3)
t2 = tuple(t1)print(t1 == t2)  # True
print(t1 is t2)  # True 指向的是同一个对象

タプルのスライスはコピーではなく、同じオブジェクトへの参照を追加するだけです

t1 = (1, 2, 3)
t2 = t1[:]print(t1 == t2) # True
print(t1 is t2) # True  指向的是同一个对象

おすすめ

転載: blog.csdn.net/liuchunming033/article/details/107939749