Messages Protobuf

Le paramètre de configuration use_proto_plus vous permet d'indiquer si vous souhaitez que la bibliothèque renvoie 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 de chaque option en termes de performances afin que vous puissiez choisir l'approche la mieux adaptée à votre application.

Messages proto-plus et protobuf

Le pipeline du générateur de code intègre proto-plus pour améliorer l'ergonomie de l'interface de message protobuf en les faisant se comporter davantage comme des objets Python standards. Toutefois, cela signifie que l'utilisation de proto-plus introduit une surcharge de performances.

Performances Proto-Plus

L'un des principaux avantages de proto-plus est qu'il convertit les messages protobuf et les types connus en types Python intégrés grâce à un processus appelé sérialisation de type.

La sérialisation 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 se présente comme suit :

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 n'importe quel 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 str Python intégré en type string afin que la valeur soit compatible avec l'exécution protobuf.

Le temps passé à effectuer ces conversions de type a un impact suffisamment important sur les performances pour que vous décidiez, en fonction des besoins de votre application, d'utiliser des messages proto-plus ou protobuf.

Cas d'utilisation des messages proto-plus et protobuf

Cas d'utilisation des messages Proto-Plus
Proto-plus offre un certain nombre d'améliorations ergonomiques par rapport aux messages protobuf. Il est donc idéal pour écrire du code lisible et facile à gérer. Comme elles exposent des objets Python standards, elles sont plus faciles à utiliser et à comprendre.
Cas d'utilisation des messages Protobuf
 Utilisez des fichiers .proto pour les cas d'utilisation sensibles aux performances, en particulier dans les applications qui doivent traiter rapidement de grands rapports ou qui créent des requêtes de mutation avec un grand nombre d'opérations, par exemple avec BatchJobService ou OfflineUserDataJobService.

Changer de type de message de manière dynamique

Après avoir sélectionné le type de message approprié pour votre application, vous constaterez peut-être que vous devez utiliser l'autre type pour un workflow spécifique. Dans ce cas, vous pouvez basculer dynamiquement entre les deux types à l'aide des utilitaires proposés par la bibliothèque cliente. En utilisant la même classe de message Dog que précédemment :

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 les interfaces de messages Protobuf

L'interface proto-plus est détaillée dans la documentation. Les sections suivantes mettent en évidence les principales différences qui affectent les cas d'utilisation courants de la bibliothèque cliente Google Ads.

Sérialisation des octets

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

Sérialisation JSON

Messages Proto-Plus
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 pour le masque de champ fournie par api-core est conçue pour utiliser des instances de messages protobuf. Lorsque vous utilisez des messages proto-plus, convertissez-les en messages protobuf pour utiliser l'assistant :

Messages 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)
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 intégré de Python et héritent donc d'un certain nombre de méthodes pratiques.

Récupération du type Enum

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

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

Pour simplifier la récupération des énumérations, un attribut pratique est disponible sur les instances GoogleAdsClient. Il dispose d'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 enum

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-Plus
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
# Native protobuf enum fields already store the integer value (3):
print(campaign.status)
# Or look up the integer value from the enum name string:
print(status_enum.CampaignStatus.Value("PAUSED"))

Récupération du nom de l'enum

Il est parfois utile de connaître le nom d'un champ d'énumération. Par exemple, lorsque vous lisez des objets à partir de l'API, vous pouvez vouloir connaître l'état de la campagne auquel correspond l'entier 3 :

Messages Proto-Plus
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
print(status_enum.CampaignStatus.Name(campaign.status))

Champs répétés

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

Ajouter des valeurs aux champs scalaires répétés

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

Messages Proto-Plus
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-Plus
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 aux champs répétés

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

Messages Proto-Plus
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 répétés scalaires et non scalaires, vous pouvez attribuer des listes au champ de différentes manières :

Messages Proto-Plus
# 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 l'un de ses champs est défini.

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

Texte du message

Pour les messages proto-plus et protobuf, utilisez la méthode d'assistance copy_from sur GoogleAdsClient :

client.copy_from(campaign, other_campaign)

Champs de message vides

La procédure de définition des champs de message vides est la même, quel que soit le type de message que vous utilisez. Vous copiez un message vide dans le champ en question. Consultez la section Texte du message ainsi que le guide Champs de message vides. L'exemple suivant montre comment définir un champ de message vide :

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

Noms de champs qui sont des mots réservés

Lorsque vous utilisez des messages proto-plus, les noms de champs sont automatiquement suivis d'un trait de soulignement si le nom est également un mot réservé en Python. L'exemple suivant montre comment utiliser une instance Asset :

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

La liste complète des noms réservés est construite 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 de champs

É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-Plus
# 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 classe protobuf Message comporte une méthode HasField qui détermine si un sous-message, un champ oneof ou un champ scalaire optional d'un message a été défini, même s'il a été défini sur une valeur par défaut. (L'appel de HasField sur des champs scalaires non facultatifs dans proto3 génère une ValueError.)

Méthodes de message Protobuf

L'interface de message protobuf inclut des méthodes pratiques qui ne font pas partie de l'interface proto-plus. Toutefois, vous pouvez y accéder en convertissant un message proto-plus en son équivalent protobuf :

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

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

Outil de suivi des problèmes

Si vous avez des questions sur ces modifications ou si vous rencontrez des problèmes lors de la migration vers la dernière version de la bibliothèque, signalez un problème dans l'outil de suivi des problèmes.