Notas básicas de Python para el desarrollo de pruebas (21): comparación y copia de objetos

21.1 La relación entre variables y objetos

Todo en Python es un objeto, incluidas las variables, funciones y clases, que son todos objetos. La cadena 's' en la expresión a='s' es el objeto. En otras palabras, la variable a apunta al objeto 's'. Una variable es como una etiqueta, que identifica un objeto.

Cada objeto se compone de tres partes: identificador (identidad), tipo (tipo), valor (valor).

Identificador: cada objeto tiene un identificador único para identificarse a sí mismo. Puede usar la función integrada id() para ver el identificador del objeto. Por ejemplo, mirando el identificador de la variable a, el identificador simplemente puede pensar que este valor es la dirección de memoria del objeto:

>>> id(a)
4310511984

Tipo: Identifica el tipo de datos almacenados por el objeto.El tipo de datos limita el rango de valores y operaciones que se pueden realizar sobre el objeto. Puede usar la función incorporada type() para verificar el tipo de objeto. Por ejemplo, mire el tipo de objeto al que apunta la variable a. De la salida, se puede ver que es un objeto de tipo str, que es un tipo de cadena:

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

Valor: representa la información de datos almacenada por el objeto. Utilice la función print() para imprimirlo.

>>> print(a)
's'

La representación de a='s' en la memoria se puede representar mediante este gráfico:
inserte la descripción de la imagen aquí

Cuando se ejecuta b=a, b también apuntará al objeto señalado por a, que se puede expresar así:
inserte la descripción de la imagen aquí

21.2 Comparación de objetos

21.2.1 El operador '==' compara valores

Ejecutar a == b es equivalente a ejecutar a.__eq__(b), y la mayoría de los tipos de datos en Python sobrecargarán __eq__esta función, y su procesamiento interno suele ser más complicado. Por ejemplo, para las listas, la __eq__función iterará sobre los elementos de la lista y comparará su orden y valores para la igualdad. Lo mismo ocurre con las tuplas.


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

21.2.2 El operador 'es' compara identidades

¿Son el mismo objeto y apuntan a la misma dirección de memoria?

Hay dos clases a continuación, una es la clase Person y la otra es la clase MyClass en el patrón singleton. Puede ver en la salida que s1 y s2 son el mismo objeto, porque las identificaciones de los objetos son 4448078200. Aunque los valores de atributo de los dos objetos b y c son los mismos, no son el mismo objeto porque sus id de objeto son diferentes, uno es 4448078256 y el otro es 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 Reglas de comparación de objetos personalizados

Personalizando las reglas de __eq__comparación del objeto == operador.

Por ejemplo, el siguiente código compara dos objetos Person, si sus valores de atributo name y age son iguales, se considera que los dos objetos son True cuando se compara la operación ==.

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 Copia de objetos

21.3.1 copia superficial

Copia superficial significa reasignar una parte de la memoria y crear un nuevo objeto cuyos elementos son referencias a los objetos secundarios en el objeto original. Hay tres formas de crear un objeto de copia superficial:

  1. Utilice el constructor del propio tipo de datos para obtener una copia superficial del objeto original.
    l2 es una copia superficial de l1 y s2 es una copia superficial de s1.
l1 = [[1, 2], (30, 40)]
l2 = list(l1)  
print(l1 == l2) # True
print(l1 is l2)  # 创建了一个新的对象  False
s1 = set([1, 2, 3])
s2 = set(s1)
  1. Use el corte de lista para obtener una copia superficial del objeto original.
l1 = [1, 2, 3]
l2 = l1[:]
print(l1 == l2) # True
print(l1 is l2)  # 创建了一个新的对象 # False
  1. La función copy.copy() realiza una copia superficial
import copy
l1 = [1, 2, 3]
l2 = copy.copy(l1) 

Si los elementos del objeto original son mutables, cambiar el objeto original también afectará al objeto copiado. por ejemplo:

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变化的影响

Porque los elementos en el objeto de copia superficial son referencias a los objetos secundarios en el objeto original. Por tanto, el cambio de los elementos variables en el objeto original (l1) afecta a l2.

l1.append(100), lo que significa agregar el elemento 100 a la lista de l1. Esta operación no tiene ningún efecto sobre l2, porque l2 y l1 en conjunto son dos objetos diferentes y no comparten direcciones de memoria.

El primer elemento [1,2] de l1 agrega un elemento 3, y el primer elemento de l2 también se convierte en [1, 2, 3]. El segundo elemento de l1 es una tupla, esta tupla agrega dos elementos (50, 60), porque la tupla es inmutable, aquí significa empalmar la segunda tupla en l1, y luego vuelve a crear una La nueva tupla es el segundo elemento en l1 , y no se hace referencia a la nueva tupla en l2, por lo que l2 no se ve afectado.

21.3.2 Copia profunda

Una copia profunda copia recursivamente cada subobjeto del objeto original. Por lo tanto, el objeto posterior a la copia en profundidad y el objeto original no están relacionados entre sí.

deepcopied = copy.deepcopy(origen) para obtener un objeto de copia profunda.

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 No es una copia

El constructor de tuplas y el segmento dan como resultado el mismo objeto que el objeto original. No una copia, porque copiar es obtener un objeto diferente.

El constructor de una tupla no es una copia, solo agrega una referencia al mismo objeto

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

Una porción de una tupla no es una copia, solo agrega una referencia al mismo objeto

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

Supongo que te gusta

Origin blog.csdn.net/liuchunming033/article/details/107939749
Recomendado
Clasificación