Messages Protobuf

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

La version 14.0.0 de la bibliothèque cliente Python introduit un nouveau paramètre de configuration obligatoire appelé use_proto_plus, qui spécifie si la bibliothèque doit renvoyer des messages proto-plus ou des messages protobuf. Pour savoir comment définir ce paramètre, consultez la documentation sur la configuration.

Cette section décrit les implications en termes de performances liées au choix des types de messages à utiliser. Par conséquent, nous vous recommandons de lire et de comprendre les options proposées afin de prendre une décision éclairée. Toutefois, si vous souhaitez effectuer une mise à niveau vers la version 14.0.0 sans modifier le code, vous pouvez définir use_proto_plus sur True pour éviter les modifications d'interface importantes.

Messages Proto-plus et protobuf

Dans la version 10.0.0, la bibliothèque cliente Python a migré vers un nouveau pipeline de générateur de code qui a intégré proto-plus afin d'améliorer l'ergonomie de l'interface de message protobuf en la rendant plus semblable à des objets Python natifs. En contrepartie de cette amélioration, proto-plus entraîne une surcharge des performances.

Performances Proto+

L'un des principaux avantages de proto-plus est qu'il convertit les messages protobuf et les types connus en types Python natifs via un processus appelé marshaling de types.

Le marshaling se produit lorsqu'un champ est consulté sur une instance de message proto-plus, en particulier lorsqu'un champ est lu ou défini, par exemple dans une définition protobuf:

syntax = "proto3";

message Dog {
  string name = 1;
}

Lorsque cette définition est convertie en classe proto-plus, elle ressemble à ceci:

import proto

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

Vous pouvez ensuite initialiser la classe Dog et accéder à son champ name comme vous le feriez pour tout autre objet Python:

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

Lors de la lecture et de la définition du champ name, la valeur est convertie d'un type Python natif str en un type string afin qu'elle soit compatible avec l'environnement d'exécution protobuf.

Dans l'analyse que nous avons réalisée depuis le lancement de la version 10.0.0, nous avons déterminé que le temps passé à effectuer ces conversions de type a un impact suffisamment important sur les performances. Il est donc important de donner aux utilisateurs la possibilité d'utiliser des messages protobuf.

Cas d'utilisation des messages proto-plus et protobuf

Cas d'utilisation du message proto-plus
Proto-plus offre un certain nombre d'améliorations ergonomiques par rapport aux messages protobuf. Ils sont donc parfaits pour écrire du code lisible et lisible. Étant donné qu'ils exposent des objets Python natifs, ils sont plus faciles à utiliser et à comprendre.
Cas d'utilisation du message Protobuf
Utilisez des protobufs pour les cas d'utilisation sensibles aux performances, en particulier dans les applications qui doivent traiter rapidement des rapports volumineux ou qui créent des requêtes mutate avec un grand nombre d'opérations, par exemple avec BatchJobService ou OfflineUserDataJobService.

Modification dynamique des types de messages

Après avoir sélectionné le type de message approprié pour votre application, vous devrez peut-être utiliser l'autre type pour un workflow spécifique. Dans ce cas, il est facile de basculer de l'un à l'autre de manière dynamique à l'aide des utilitaires proposés par la bibliothèque cliente. En utilisant la même classe de message Dog que ci-dessus:

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)

Différences entre l'interface du message Protobuf

L'interface proto-plus est décrite en détail, mais nous allons ici mettre en évidence quelques différences clés qui affectent les cas d'utilisation courants de la bibliothèque cliente Google Ads.

Sérialisation des octets

Messages Proto+
serialized = type(campaign).serialize(campaign)
deserialized = type(campaign).deserialize(serialized)
Messages Protobuf
serialized = campaign.SerializeToString()
deserialized = campaign.FromString(serialized)

Sérialisation JSON

Messages Proto+
serialized = type(campaign).to_json(campaign)
deserialized = type(campaign).from_json(serialized)
Messages Protobuf
from google.protobuf.json_format import MessageToJson, Parse

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

Masques de champ

La méthode d'assistance du masque de champ fournie par api-core est conçue pour utiliser des instances de message protobuf. Ainsi, lorsque vous utilisez des messages proto-plus, convertissez-les en messages protobufs pour utiliser l'application auxiliaire:

Messages Proto+
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)
Messages Protobuf
from google.api_core.protobuf_helpers import field_mask

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

Enums

Les énumérations exposées par les messages proto-plus sont des instances du type enum natif de Python et héritent donc d'un certain nombre de méthodes pratiques.

Récupération du type d'énumération

Lorsque vous utilisez la méthode GoogleAdsClient.get_type pour récupérer des énumérations, les messages renvoyés varient légèrement selon que vous utilisez des messages proto-plus ou protobuf. Exemple :

Messages Proto+
val = client.get_type("CampaignStatusEnum").CampaignStatus.PAUSED
Messages Protobuf
val = client.get_type("CampaignStatusEnum").PAUSED

Pour faciliter la récupération des énumérations, il existe un attribut de commodité sur les instances GoogleAdsClient qui possède une interface cohérente, quel que soit le type de message que vous utilisez:

val = client.enums.CampaignStatusEnum.PAUSED

Récupération de la valeur d'énumération

Il est parfois utile de connaître la valeur ou l'ID de champ d'une énumération donnée. Par exemple, PAUSED sur CampaignStatusEnum correspond à 3:

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

Récupération du nom de l'énumération

Il est parfois utile de connaître le nom d'un champ d'énumération. Par exemple, lors de la lecture d'objets à partir de l'API, vous pouvez savoir à quel état de campagne correspond l'int 3:

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

Champs répétés

Comme décrit dans la documentation de proto-plus, les champs répétés sont généralement équivalents aux listes saisies, ce qui signifie qu'ils se comportent presque de la même manière que list.

Ajout à des champs scalaires répétés

Lorsque vous ajoutez des valeurs à des champs scalaires répétés, par exemple des champs string ou int64, l'interface est la même quel que soit le type de message:

Messages Proto+
ad.final_urls.append("https://www.example.com")
Messages Protobuf
ad.final_urls.append("https://www.example.com")

Cela inclut également toutes les autres méthodes list courantes, par exemple extend:

Messages Proto+
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])
Messages Protobuf
ad.final_urls.extend(["https://www.example.com", "https://www.example.com/2"])

Ajouter des types de messages à des champs répétés

Si le champ répété n'est pas un type scalaire, le comportement lors de l'ajout de champs répétés est légèrement différent:

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

Attribuer des champs répétés

Pour les champs scalaires et non scalaires répétés, vous pouvez attribuer des listes au champ de différentes manières:

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

Messages vides

Il est parfois utile de savoir si une instance de message contient des informations ou si certains de ses champs sont définis.

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

Copie du message

Pour les messages proto-plus et protobuf, nous vous recommandons d'utiliser la méthode d'aide copy_from sur GoogleAdsClient:

client.copy_from(campaign, other_campaign)

Champs de message vides

Le processus de définition des champs de message vides est le même, quel que soit le type de message que vous utilisez. Il vous suffit de copier un message vide dans le champ en question. Consultez la section Texte des messages ainsi que le guide Champs de message vides. Voici un exemple de définition d'un champ de message vide:

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

Noms de champs réservés

Lorsque vous utilisez des messages proto-plus, les noms de champs apparaissent automatiquement avec un trait de soulignement à la fin si le nom est également un mot réservé en Python. Voici un exemple d'utilisation d'une instance Asset:

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

La liste complète des noms réservés est créée dans le module Générateur gapic. Vous pouvez également y accéder par programmation.

Commencez par installer le module:

python -m pip install gapic-generator

Ensuite, dans un REPL ou un script Python:

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

Présence sur le terrain

Étant donné que les champs des instances de message protobuf ont des valeurs par défaut, il n'est pas toujours intuitif de savoir si un champ a été défini ou non.

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

L'interface de la classe protobuf Message possède une méthode HasField qui détermine si le champ d'un message a été défini, même s'il a été défini sur une valeur par défaut.

Méthodes des messages Protobuf

L'interface du message protobuf comprend des méthodes pratiques qui ne font pas partie de l'interface proto-plus. Cependant, il est facile d'y accéder en convertissant un message proto-plus en son équivalent 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

Si vous avez des questions sur ces modifications ou si vous rencontrez des problèmes de migration vers la version 14.0.0 de la bibliothèque, signalez le problème à notre outil de suivi.