Messaggi Protobuf

La versione 14.0.0 della libreria client Python introduce un nuovo parametro di configurazione obbligatorio denominato use_proto_plus che specifica se vuoi che la libreria restituisca messaggi proto-plus o messaggi protobuf. Per maggiori dettagli su come impostare questo parametro, consulta la documentazione di configurazione.

Questa sezione descrive le implicazioni in termini di prestazioni della scelta dei tipi di messaggi da utilizzare. Pertanto, ti consigliamo di leggere e comprendere le opzioni per prendere una decisione consapevole. Tuttavia, se vuoi eseguire l'upgrade alla versione 14.0.0 senza apportare modifiche al codice, puoi impostare use_proto_plus su True per evitare di interrompere le modifiche all'interfaccia.

Messaggi Proto-plus e protobuf

Nella versione 10.0.0, la libreria client Python è stata migrata a una nuova pipeline del generatore di codice che integrava proto-plus per migliorare l'ergonomia dell'interfaccia dei messaggi protobuf facendo in modo che si comportino più come oggetti Python nativi. Lo svantaggio di questo miglioramento è che il proto-plus introduce un sovraccarico di prestazioni.

Prestazioni proto-plus

Uno dei vantaggi principali di Proto-plus è che converte i messaggi protobuf e i tipi ben noti in tipi Python nativi attraverso un processo chiamato marshalling dei tipi.

Il marshalling si verifica quando si accede a un campo su un'istanza di messaggio proto-plus, in particolare quando un campo viene letto o impostato, ad esempio, in una definizione protobuf:

syntax = "proto3";

message Dog {
  string name = 1;
}

Quando questa definizione viene convertita in una classe proto-plus, avrà il seguente aspetto:

import proto

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

Puoi quindi inizializzare la classe Dog e accedere al relativo campo name come faresti con qualsiasi altro oggetto Python:

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

Durante la lettura e l'impostazione del campo name, il valore viene convertito da un tipo Python str nativo a un tipo string in modo che il valore sia compatibile con il runtime protobuf.

Nell'analisi che abbiamo condotto dal rilascio della versione 10.0.0, abbiamo stabilito che il tempo dedicato a queste conversioni di tipo ha un impatto abbastanza significativo sulle prestazioni da rendere importante agli utenti la possibilità di utilizzare i messaggi protobuf.

Casi d'uso per messaggi proto-plus e protobuf

Casi d'uso dei messaggi proto-plus
Proto-plus offre una serie di miglioramenti ergonomici rispetto ai messaggi protobuf, quindi è ideale per scrivere codice gestibile e leggibile. Essendo espongono oggetti Python nativi, sono più facili da usare e da capire.
Casi d'uso dei messaggi Protobuf
Utilizza i protobuf per casi d'uso sensibili alle prestazioni, in particolare nelle app che devono elaborare rapidamente report di grandi dimensioni o che creano richieste di modifica con un numero elevato di operazioni, ad esempio con BatchJobService o OfflineUserDataJobService.

Modifica dinamica dei tipi di messaggi

Dopo aver selezionato il tipo di messaggio appropriato per la tua app, potresti scoprire che devi utilizzare l'altro tipo per un flusso di lavoro specifico. In questo caso, è facile passare da un tipo all'altro in modo dinamico utilizzando le utilità offerte dalla libreria client. Utilizzare la stessa classe di messaggi Dog di cui sopra:

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)

Differenze nell'interfaccia dei messaggi Protobuf

L'interfaccia proto+ è documentata in dettaglio, ma qui metteremo in evidenza alcune differenze chiave che riguardano i casi d'uso comuni per la libreria client di Google Ads.

Serializzazione in byte

Messaggi protocollo più
serialized = type(campaign).serialize(campaign)
deserialized = type(campaign).deserialize(serialized)
Messaggi Protobuf
serialized = campaign.SerializeToString()
deserialized = campaign.FromString(serialized)

Serializzazione JSON

Messaggi protocollo più
serialized = type(campaign).to_json(campaign)
deserialized = type(campaign).from_json(serialized)
Messaggi Protobuf
from google.protobuf.json_format import MessageToJson, Parse

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

Maschere dei campi

Il metodo helper per la maschera di campo fornito da api-core è progettato per utilizzare istanze di messaggio protobuf. Pertanto, quando utilizzi messaggi proto-plus, convertili in messaggi protobuf per utilizzare l'helper:

Messaggi protocollo più
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)
Messaggi Protobuf
from google.api_core.protobuf_helpers import field_mask

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

Enum

Le enum esposte dai messaggi protocollo più sono istanze del tipo enum nativo di Python e quindi ereditano una serie di metodi di convenienza.

Recupero tipo enum

Quando utilizzi il metodo GoogleAdsClient.get_type per recuperare le enumerazioni, i messaggi restituiti sono leggermente diversi a seconda che utilizzi messaggi proto-plus o protobuf. Ad esempio:

Messaggi protocollo più
val = client.get_type("CampaignStatusEnum").CampaignStatus.PAUSED
Messaggi Protobuf
val = client.get_type("CampaignStatusEnum").PAUSED

Per semplificare il recupero delle enum, è disponibile un attributo di convenienza nelle istanze GoogleAdsClient che ha un'interfaccia coerente, indipendentemente dal tipo di messaggio che stai utilizzando:

val = client.enums.CampaignStatusEnum.PAUSED

Recupero valore enum

A volte è utile conoscere il valore o l'ID campo di una determinata enumerazione. Ad esempio, PAUSED su CampaignStatusEnum corrisponde a 3:

Messaggi protocollo più
campaign = client.get_type("Campaign")
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# To read the value of campaign status
print(campaign.status.value)
Messaggi 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))

Recupero nome enum

A volte è utile conoscere il nome di un campo enum. Ad esempio, quando leggi oggetti dall'API, potresti voler sapere a quale stato della campagna corrisponde l'int 3:

Messaggi protocollo più
campaign = client.get_type("Campaign")
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# To read the name of campaign status
print(campaign.status.name)
Messaggi 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)

Campi ripetuti

Come descritto nei documenti proto-plus, i campi ripetuti sono generalmente equivalenti agli elenchi digitati, il che significa che si comportano in modo quasi identico a un list.

Aggiunta ai campi scalari ripetuti

Quando aggiungi valori ai campi scalar type ripetuti, ad esempio i campi string o int64, l'interfaccia è la stessa indipendentemente dal tipo di messaggio:

Messaggi protocollo più
ad.final_urls.append("https://www.example.com")
Messaggi Protobuf
ad.final_urls.append("https://www.example.com")

Sono inclusi anche tutti gli altri metodi list comuni, ad esempio extend:

Messaggi protocollo più
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])
Messaggi Protobuf
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])

Aggiunta di tipi di messaggi ai campi ripetuti

Se il campo ripetuto non è di tipo scalare, il comportamento quando li aggiungi a campi ripetuti è leggermente diverso:

Messaggi protocollo più
frequency_cap = client.get_type("FrequencyCapEntry")
frequency_cap.cap = 100
campaign.frequency_caps.append(frequency_cap)
Messaggi Protobuf
# The add method initializes a message and adds it to the repeated field
frequency_cap = campaign.frequency_caps.add()
frequency_cap.cap = 100

Assegnare campi ripetuti

Per i campi ripetuti sia scalari che non scalabili, puoi assegnare elenchi al campo in diversi modi:

Messaggi protocollo più
# In proto-plus it's possible to use assignment.
urls = ["https://www.example.com"]
ad.final_urls = urls
Messaggi 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

Messaggi vuoti

A volte è utile sapere se un'istanza di un messaggio contiene informazioni o se ha dei suoi campi impostati.

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

Copia del messaggio

Per i messaggi proto-plus e protobuf, consigliamo di utilizzare il metodo helper copy_from in GoogleAdsClient:

client.copy_from(campaign, other_campaign)

Campi del messaggio vuoti

La procedura per impostare i campi dei messaggi vuoti è la stessa, indipendentemente dal tipo di messaggio utilizzato. Devi solo copiare un messaggio vuoto nel campo in questione. Consulta la sezione Copia del messaggio e la guida Campi messaggio vuoti. Ecco un esempio di come impostare un campo di messaggio vuoto:

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

Nomi dei campi che sono parole riservate

Quando utilizzi messaggi proto-plus, i nomi dei campi vengono visualizzati automaticamente con un trattino basso finale se il nome è anche una parola riservata in Python. Ecco un esempio di utilizzo di un'istanza Asset:

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

L'elenco completo dei nomi riservati viene creato nel modulo gapic generatore. Puoi anche accedervi in modo programmatico.

Innanzitutto, installa il modulo:

python -m pip install gapic-generator

Quindi, in un REPL o uno script Python:

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

Presenza sul campo

Poiché i campi delle istanze di messaggio protobuf hanno valori predefiniti, non è sempre intuitivo sapere se un campo è stato impostato o meno.

Messaggi protocollo più
# Use the "in" operator.
has_field = "name" in campaign
Messaggi Protobuf
campaign = client.get_type("Campaign")
# Determines whether "name" is set and not just an empty string.
campaign.HasField("name")

L'interfaccia della classe protobuf Message ha un metodo HasField che determina se il campo di un messaggio è stato impostato, anche se è stato impostato su un valore predefinito.

Metodi dei messaggi Protobuf

L'interfaccia dei messaggi protobuf include alcuni metodi pratici che non fanno parte dell'interfaccia proto-plus. Tuttavia, è semplice accedervi convertendo un messaggio proto-plus nella sua controparte 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())

Issue Tracker

In caso di domande su queste modifiche o di problemi durante la migrazione alla versione 14.0.0 della libreria, segnala un problema sul nostro tracker.