Mensajes de protobuf

Con el parámetro de configuración use_proto_plus, puedes especificar si deseas que la biblioteca devuelva mensajes proto-plus o mensajes de Protobuf. Para obtener detalles sobre cómo establecer este parámetro, consulta la documentación de configuración.

En esta sección, se describen las implicaciones en el rendimiento de elegir qué tipos de mensajes usar. Por lo tanto, te recomendamos que leas y comprendas las opciones para tomar una decisión fundamentada.

Comparación entre los mensajes de proto-plus y los de Protobuf

La canalización del generador de código integra proto-plus como una forma de mejorar la ergonomía de la interfaz de mensajes de protobuf, ya que hace que se comporten más como objetos nativos de Python. Sin embargo, esto significa que usar proto-plus introduce una sobrecarga de rendimiento.

Rendimiento de proto-plus

Uno de los principales beneficios de proto-plus es que convierte los mensajes de protobuf y los tipos conocidos en tipos nativos de Python a través de un proceso llamado serialización de tipos.

El serializado ocurre cuando se accede a un campo en una instancia de mensaje de proto-plus, específicamente cuando se lee o se establece un campo, por ejemplo, en una definición de protobuf:

syntax = "proto3";

message Dog {
  string name = 1;
}

Cuando esta definición se convierte en una clase de proto-plus, se ve de la siguiente manera:

import proto

class Dog(proto.Message):
    name = proto.Field(proto.STRING, number=1)

Luego, puedes inicializar la clase Dog y acceder a su campo name como lo harías con cualquier otro objeto de Python:

dog = Dog()
dog.name = "Scruffy"
print(dog.name)

Cuando se lee y se configura el campo name, el valor se convierte de un tipo str nativo de Python a un tipo string para que sea compatible con el tiempo de ejecución de Protobuf.

Según nuestros análisis de rendimiento, determinamos que el tiempo dedicado a realizar este tipo de conversiones tiene un impacto en el rendimiento lo suficientemente grande como para que los usuarios decidan, según sus necesidades, si usar o no mensajes de protobuf.

Casos de uso de mensajes de proto-plus y protobuf

Casos de uso de mensajes de Proto-plus
Proto-plus ofrece varias mejoras ergonómicas en comparación con los mensajes de protobuf, por lo que son ideales para escribir código legible y fácil de mantener. Dado que exponen objetos nativos de Python, son más fáciles de usar y comprender.
Casos de uso de mensajes de Protobuf
Usa protobufs para casos de uso sensibles al rendimiento, específicamente en apps que necesitan procesar informes grandes rápidamente o que compilan solicitudes de mutación con una gran cantidad de operaciones, por ejemplo, con BatchJobService o OfflineUserDataJobService.

Cambio dinámico de tipos de mensajes

Después de seleccionar el tipo de mensaje adecuado para tu app, es posible que necesites usar el otro tipo para un flujo de trabajo específico. En este caso, es fácil cambiar entre los dos tipos de forma dinámica con las utilidades que ofrece la biblioteca cliente. Con la misma clase de mensaje Dog del ejemplo anterior, se vería de la siguiente manera:

from google.ads.googleads import util

# Proto-plus message type
dog = Dog()

# Protobuf message type
dog = util.convert_proto_plus_to_protobuf(dog)

# Back to proto-plus message type
dog = util.convert_protobuf_to_proto_plus(dog)

Diferencias en la interfaz de mensajes de Protobuf

La interfaz de proto-plus está documentada en detalle, pero aquí destacaremos algunas diferencias clave que afectan los casos de uso comunes de la biblioteca cliente de Google Ads.

Serialización de bytes

Mensajes de proto-plus
serialized = type(campaign).serialize(campaign)
deserialized = type(campaign).deserialize(serialized)
Mensajes de Protobuf
serialized = campaign.SerializeToString()
deserialized = campaign.FromString(serialized)

Serialización de JSON

Mensajes de proto-plus
serialized = type(campaign).to_json(campaign)
deserialized = type(campaign).from_json(serialized)
Mensajes de Protobuf
from google.protobuf.json_format import MessageToJson, Parse

serialized = MessageToJson(campaign)
deserialized = Parse(serialized, campaign)

Máscaras de campo

El método auxiliar de máscara de campo que proporciona api-core está diseñado para usar instancias de mensajes de protobuf. Por lo tanto, cuando uses mensajes de proto-plus, conviértelos en mensajes de protobuf para utilizar el asistente:

Mensajes de proto-plus
from google.api_core.protobuf_helpers import field_mask

campaign = client.get_type("Campaign")
protobuf_campaign = util.convert_proto_plus_to_protobuf(campaign)
mask = field_mask(None, protobuf_campaign)
Mensajes de Protobuf
from google.api_core.protobuf_helpers import field_mask

campaign = client.get_type("Campaign")
mask = field_mask(None, campaign)

Enumeraciones

Los enums expuestos por los mensajes de proto-plus son instancias del tipo enum nativo de Python y, por lo tanto, heredan varios métodos convenientes.

Recuperación del tipo de enumeración

Cuando usas el método GoogleAdsClient.get_type para recuperar enumeraciones, los mensajes que se devuelven son ligeramente diferentes según si usas mensajes de proto-plus o de protobuf. Por ejemplo:

Mensajes de proto-plus
val = client.get_type("CampaignStatusEnum").CampaignStatus.PAUSED
Mensajes de Protobuf
val = client.get_type("CampaignStatusEnum").PAUSED

Para simplificar la recuperación de enumeraciones, hay un atributo de conveniencia en las instancias de GoogleAdsClient que tiene una interfaz coherente, independientemente del tipo de mensaje que uses:

val = client.enums.CampaignStatusEnum.PAUSED

Recuperación de valores de enumeración

A veces, es útil conocer el valor o el ID de campo de un enum determinado. Por ejemplo, PAUSED en CampaignStatusEnum corresponde a 3:

Mensajes de proto-plus
campaign = client.get_type("Campaign")
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# To read the value of campaign status
print(campaign.status.value)
Mensajes de Protobuf
campaign = client.get_type("Campaign")
status_enum = client.enums.CampaignStatusEnum
campaign.status = status_enum.PAUSED
# To read the value of campaign status
print(status_enum.CampaignStatus.Value(campaign.status))

Recuperación del nombre de la enumeración

A veces, es útil conocer el nombre de un campo de enumeración. Por ejemplo, cuando lees objetos de la API, es posible que desees saber a qué estado de la campaña corresponde el int 3:

Mensajes de proto-plus
campaign = client.get_type("Campaign")
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# To read the name of campaign status
print(campaign.status.name)
Mensajes de Protobuf
campaign = client.get_type("Campaign")
status_enum = client.enums.CampaignStatusEnum
# Sets the campaign status to the int value for PAUSED
campaign.status = status_enum.PAUSED
# To read the name of campaign status
status_enum.CampaignStatus.Name(campaign.status)

Campos repetidos

Como se describe en la documentación de proto-plus, los campos repetidos suelen ser equivalentes a las listas escritas, lo que significa que se comportan casi de la misma manera que un list.

Agrega valores a los campos escalares repetidos

Cuando agregas valores a campos repetidos de tipo escalar, por ejemplo, campos string o int64, la interfaz es la misma independientemente del tipo de mensaje:

Mensajes de proto-plus
ad.final_urls.append("https://www.example.com")
Mensajes de Protobuf
ad.final_urls.append("https://www.example.com")

Esto también incluye todos los demás métodos de list comunes, por ejemplo, extend:

Mensajes de proto-plus
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])
Mensajes de Protobuf
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])

Agrega tipos de mensajes a campos repetidos

Si el campo repetido no es un tipo escalar, el comportamiento al agregarlos a campos repetidos es ligeramente diferente:

Mensajes de proto-plus
frequency_cap = client.get_type("FrequencyCapEntry")
frequency_cap.cap = 100
campaign.frequency_caps.append(frequency_cap)
Mensajes de Protobuf
# The add method initializes a message and adds it to the repeated field
frequency_cap = campaign.frequency_caps.add()
frequency_cap.cap = 100

Asigna campos repetidos

En el caso de los campos repetidos escalares y no escalares, puedes asignar listas al campo de diferentes maneras:

Mensajes de proto-plus
# In proto-plus it's possible to use assignment.
urls = ["https://www.example.com"]
ad.final_urls = urls
Mensajes de Protobuf
# Protobuf messages do not allow assignment, but you can replace the
# existing list using slice syntax.
urls = ["https://www.example.com"]
ad.final_urls[:] = urls

Mensajes vacíos

A veces, es útil saber si una instancia de mensaje contiene información o si alguno de sus campos está configurado.

Mensajes de proto-plus
# When using proto-plus messages you can simply check the message for
# truthiness.
is_empty = bool(campaign)
is_empty = not campaign
Mensajes de Protobuf
is_empty = campaign.ByteSize() == 0

Texto del mensaje

Para los mensajes de proto-plus y protobuf, te recomendamos que uses el método auxiliar copy_from en GoogleAdsClient:

client.copy_from(campaign, other_campaign)

Campos de mensaje vacíos

El proceso para configurar campos de mensajes vacíos es el mismo, independientemente del tipo de mensaje que uses. Solo debes copiar un mensaje vacío en el campo en cuestión. Consulta la sección Copia del mensaje y la guía Campos de mensaje vacíos. Este es un ejemplo de cómo establecer un campo de mensaje vacío:

client.copy_from(campaign.manual_cpm, client.get_type("ManualCpm"))

Nombres de campos que son palabras reservadas

Cuando se usan mensajes de proto-plus, los nombres de los campos aparecen automáticamente con un guion bajo final si el nombre también es una palabra reservada en Python. A continuación, se muestra un ejemplo de cómo trabajar con una instancia de Asset:

asset = client.get_type("Asset")
asset.type_ = client.enums.AssetTypeEnum.IMAGE

La lista completa de nombres reservados se construye en el módulo del generador de gapic. También se puede acceder a ella de forma programática.

Primero, instala el módulo:

python -m pip install gapic-generator

Luego, en un REPL o una secuencia de comandos de Python, haz lo siguiente:

import gapic.utils
print(gapic.utils.reserved_names.RESERVED_NAMES)

Presencia de campos

Dado que los campos de las instancias de mensajes de protobuf tienen valores predeterminados, no siempre es intuitivo saber si se configuró un campo o no.

Mensajes de proto-plus
# Use the "in" operator.
has_field = "name" in campaign
Mensajes de Protobuf
campaign = client.get_type("Campaign")
# Determines whether "name" is set and not just an empty string.
campaign.HasField("name")

La interfaz de clase Message de protobuf tiene un método HasField que determina si se configuró el campo en un mensaje, incluso si se configuró con un valor predeterminado.

Métodos de mensajes de búferes de protocolo

La interfaz de mensajes de protobuf incluye algunos métodos convenientes que no forman parte de la interfaz de proto-plus. Sin embargo, es sencillo acceder a ellos convirtiendo un mensaje de proto-plus a su contraparte de protobuf:

# Accessing the ListFields method
protobuf_campaign = util.convert_protobuf_to_proto_plus(campaign)
print(campaign.ListFields())

# Accessing the Clear method
protobuf_campaign = util.convert_protobuf_to_proto_plus(campaign)
print(campaign.Clear())

Herramienta de seguimiento de errores

Si tienes alguna pregunta sobre estos cambios o problemas para migrar a la versión más reciente de la biblioteca, presenta un problema en nuestro sistema de seguimiento.