欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在Python中使用Protocol?Buffers的詳細(xì)介紹

 更新時(shí)間:2024年10月14日 09:17:49   作者:授客  
本文詳細(xì)介紹了協(xié)議緩沖區(qū)(Protocol Buffers)在Python中的應(yīng)用,包括其定義、序列化和解析過(guò)程,協(xié)議緩沖區(qū)是一種靈活且高效的自動(dòng)化解決方案,本文包括了如何將地址簿應(yīng)用程序的個(gè)人詳細(xì)信息寫(xiě)入文件的示例代碼,并提供了相應(yīng)的下載和安裝指導(dǎo),感興趣的朋友一起看看吧

實(shí)踐環(huán)境

protoc-25.4-win64.zip

下載地址:

https://github.com/protocolbuffers/protobuf/releases

https://github.com/protocolbuffers/protobuf/releases/download/v25.4/protoc-25.4-win64.zip

protobuf 5.27.2

pip install protobuf==5.27.2

Python 3.9.13

問(wèn)題域

本文將使用的示例是一個(gè)非常簡(jiǎn)單的“地址簿”應(yīng)用程序,它可以從文件中讀取和寫(xiě)入人們的聯(lián)系方式。通訊簿中的每個(gè)人都有一個(gè)姓名、一個(gè)ID、一個(gè)電子郵件地址和一個(gè)聯(lián)系電話號(hào)碼。

如何序列化和檢索這樣的結(jié)構(gòu)化數(shù)據(jù)?有幾種方法可以解決這個(gè)問(wèn)題:

使用Python pickle。這是默認(rèn)方法,因?yàn)樗鼉?nèi)置于語(yǔ)言中,但它不能很好地處理模式演化,如果你需要與用C++或Java編寫(xiě)的應(yīng)用程序共享數(shù)據(jù),它也不能很好的工作。

你可以發(fā)明一種特殊的方法將數(shù)據(jù)項(xiàng)編碼為單個(gè)字符串,例如將4個(gè)整數(shù)編碼為“12:3:-23:67”。這是一種簡(jiǎn)單而靈活的方法,盡管它確實(shí)需要編寫(xiě)一次性編碼和解析代碼,并且解析帶來(lái)的運(yùn)行時(shí)成本很小。這最適合對(duì)非常簡(jiǎn)單的數(shù)據(jù)進(jìn)行編碼。

將數(shù)據(jù)序列化為XML。這種方法非常有吸引力,因?yàn)閄ML(某種程度上)是人類(lèi)可讀的,并且有許多語(yǔ)言的綁定庫(kù)。如果想與其他應(yīng)用程序/項(xiàng)目共享數(shù)據(jù),這可能是一個(gè)不錯(cuò)的選擇。然而,眾所周知,XML是空間密集型的,對(duì)其進(jìn)行編碼/解碼會(huì)給應(yīng)用程序帶來(lái)巨大的性能損失。此外訪問(wèn)XML DOM樹(shù)訪問(wèn)類(lèi)中的簡(jiǎn)單字段要復(fù)雜得多。

可以使用協(xié)議緩沖區(qū)(Protocol buffers)替代這些選擇。協(xié)議緩沖區(qū)是解決這個(gè)問(wèn)題的靈活、高效、自動(dòng)化的解決方案。使用協(xié)議緩沖區(qū) ,可以編寫(xiě)希望存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)的.proto描述。協(xié)議緩沖區(qū)編譯器將從該文件創(chuàng)建一個(gè)類(lèi),該類(lèi)以有效的二進(jìn)制格式實(shí)現(xiàn)協(xié)議緩沖區(qū)數(shù)據(jù)的自動(dòng)編碼和解析。生成的類(lèi)為構(gòu)成協(xié)議緩沖區(qū)的字段提供getterssetters方法,并處理將協(xié)議緩沖區(qū)作為一個(gè)單元進(jìn)行讀寫(xiě)的細(xì)節(jié)。重要的是,協(xié)議緩沖區(qū)格式支持隨著時(shí)間的推移擴(kuò)展格式的想法,這樣代碼仍然可以讀取用舊格式編碼的數(shù)據(jù)。

定義協(xié)議格式(編寫(xiě)proto文件)

要?jiǎng)?chuàng)建地址簿應(yīng)用程序,需要從.proto文件開(kāi)始。.proto文件中的定義很簡(jiǎn)單:為要序列化的每個(gè)數(shù)據(jù)結(jié)構(gòu)添加一個(gè)消息(message),然后為消息中的每個(gè)字段指定名稱(chēng)和類(lèi)型。

示例:addressbook.proto

syntax = "proto2"; // proto2指定proto buffer的版本
package tutorial;
message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;
  enum PhoneType {
    PHONE_TYPE_UNSPECIFIED = 0;
    PHONE_TYPE_MOBILE = 1;
    PHONE_TYPE_HOME = 2;
    PHONE_TYPE_WORK = 3;
  }
  message PhoneNumber {
    optional string number = 1;
    optional PhoneType type = 2 [default = PHONE_TYPE_HOME];
  }
  repeated PhoneNumber phones = 4; // phones字段是一個(gè)重復(fù)字段,可以包含多個(gè)電話號(hào)碼。
}
message AddressBook {
  repeated Person people = 1;
}

說(shuō)明:

以上這個(gè).proto文件以package聲明開(kāi)始,這有助于防止不同項(xiàng)目之間的命名沖突。在Python中,包通常由目錄結(jié)構(gòu)決定,因此在.proto文件定義的package對(duì)生成的代碼沒(méi)有影響。但是,仍然應(yīng)該聲明一個(gè)package,以避免在協(xié)議緩沖區(qū)名稱(chēng)空間以及非Python語(yǔ)言中的名稱(chēng)沖突。

接下來(lái),是消息定義。消息只是包含一組類(lèi)型字段的集合。許多標(biāo)準(zhǔn)的簡(jiǎn)單數(shù)據(jù)類(lèi)型可以作為字段類(lèi)型使用,包括bool、int32float、doublestring。還可以通過(guò)使用其他消息類(lèi)型作為字段類(lèi)型來(lái)為消息添加更多的結(jié)構(gòu) - 在上面的示例中,Person消息包含PhoneNumber消息,而AddressBook消息包含Person消息。甚至可以定義嵌套在其他消息中的消息類(lèi)型-如上,PhoneNumber類(lèi)型定義在Person中。如果希望其中一個(gè)字段具有預(yù)定義的值列表之一,也可以定義枚舉類(lèi)型 - 在這里希望指定電話號(hào)碼可以是以下電話類(lèi)型之一:

  • PHONE_TYPE_MOBILE
  • PHONE_TYPE_HOME
  • PHONE_TYPE_WORK

每個(gè)元素上的“=1”、“=2”標(biāo)記標(biāo)識(shí)該字段在二進(jìn)制編碼中使用的唯一“標(biāo)記”,這確保了在序列化和反序列化過(guò)程中,‌每個(gè)字段可以被正確地識(shí)別和處理。‌這些數(shù)字標(biāo)簽在編譯時(shí)被轉(zhuǎn)換為命名空間和類(lèi)型簽名,‌從而保證了字段的唯一性。使用1-15的標(biāo)記編號(hào)比使用更高的數(shù)字要少一個(gè)字節(jié)編碼,因此作為優(yōu)化,可以決定將這些標(biāo)簽用于常用或重復(fù)的元素,將16及更高標(biāo)記編號(hào)的用于不太常用的可選元素。重復(fù)字段中的每個(gè)元素都需要重新編碼標(biāo)記號(hào),因此重復(fù)字段特別適合此優(yōu)化。

每個(gè)字段都必須使用以下修飾符之一進(jìn)行注解:

  • optional:該字段可以設(shè)置,也可以不設(shè)置。如果未設(shè)置可選字段值,則使用默認(rèn)值。對(duì)于簡(jiǎn)單類(lèi)型,可以指定自己的默認(rèn)值,就像示例中為電話號(hào)碼type所做的那樣。否則,將使用系統(tǒng)默認(rèn)值:數(shù)字類(lèi)型的默認(rèn)值為零,字符串類(lèi)型的默認(rèn)值為空字符串,布爾類(lèi)型的默認(rèn)值為false。對(duì)于嵌入式消息,默認(rèn)值始終是消息的“默認(rèn)實(shí)例”或“原型”,其沒(méi)有設(shè)置任何字段。調(diào)用訪問(wèn)器以獲取尚未顯式設(shè)置的可選(或必需)字段的值時(shí),始終返回該字段的默認(rèn)值。
  • repeated:該字段可以重復(fù)任意多次(包括零次),表示該字段可以包含多個(gè)值。將重復(fù)字段視為動(dòng)態(tài)大小的數(shù)組,重復(fù)值的順序?qū)⒃趨f(xié)議緩沖區(qū)中保留。
  • required:必須提供該字段的值,否則該消息將被視為“未初始化”。序列化未初始化的消息將引發(fā)異常。解析未初始化的消息將失敗。除此之外,必需字段的行為與可選字段完全相同。

重要

required是永久的,在將字段標(biāo)記為required時(shí)應(yīng)非常小心。如果在某個(gè)時(shí)候希望停止編寫(xiě)或發(fā)送必需字段,將該字段更改為可選字段將很成問(wèn)題 - 舊的讀取器會(huì)認(rèn)為沒(méi)有此字段的消息不完整,并可能會(huì)意外地拒絕或刪除它們。你應(yīng)該考慮為協(xié)議緩沖區(qū)編寫(xiě)特定于應(yīng)用程序的自定義驗(yàn)證例程。在Google 強(qiáng)烈不贊成使用required字段;在 proto2 語(yǔ)法中定義的大多數(shù)消息僅使用optionalrepeated。(Proto3 根本不支持required字段。)

編譯協(xié)議緩沖區(qū)

現(xiàn)在有了.proto,接下來(lái)需要做的就是生成讀寫(xiě) AddressBook(以及 Person 和 PhoneNumber)消息所需的類(lèi)。為此,需要在 .proto 上運(yùn)行協(xié)議緩沖區(qū)編譯器 protoc

1、下載protoc后解壓,將protoc所在bin目錄路徑添加到系統(tǒng)環(huán)境變量

>protoc --version
libprotoc 25.4

2、現(xiàn)在運(yùn)行編譯器,指定源目錄(應(yīng)用程序源代碼所在的位置 - 如果未提供值,則使用當(dāng)前目錄)、目標(biāo)目錄(希望生成的代碼的存儲(chǔ)目錄;通常與 $SRC_DIR 相同)和 .proto 的路徑。如下:

protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/addressbook.proto

因?yàn)橄胍?Python 類(lèi),所以使用 --python_out 選項(xiàng) - 為其他受支持的語(yǔ)言提供了類(lèi)似的選項(xiàng)。

protoc還可以使用--pyi_out生成python存根(.pyi)。

這會(huì)在你指定的目標(biāo)目錄中生成對(duì)應(yīng)的xxxx_pb2.py

實(shí)踐:cmd打開(kāi)控制臺(tái),進(jìn)入到addressbook.proto3所在目錄,然后執(zhí)行以下命令

protoc --python_out=. addressbook.proto2

命令執(zhí)行成功后,會(huì)再當(dāng)前目錄下生成與.proto2文件同名目錄(例中為addressbook),目錄下自動(dòng)生成對(duì)應(yīng)的py文件(例中為proto2_pb2.py,實(shí)踐時(shí)將其拷貝到addressbook.proto2所在目錄并從命名為addressbook_pb2.py

協(xié)議緩沖區(qū) API

與生成 Java 和 C++ 協(xié)議緩沖區(qū)代碼不同,Python 協(xié)議緩沖區(qū)編譯器不會(huì)直接為你生成數(shù)據(jù)訪問(wèn)代碼。相反(如果你查看 addressbook_pb2.py,你就會(huì)看到),它會(huì)為你的所有消息、枚舉和字段生成特殊描述符,以及一些神秘的空類(lèi),每個(gè)消息類(lèi)型一個(gè)類(lèi)。

# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: addressbook.proto2
# Protobuf Python Version: 4.25.4
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61\x64\x64ressbook.proto2\x12\x08tutorial\"\xa3\x02\n\x06Person\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x05\x12\r\n\x05\x65mail\x18\x03 \x01(\t\x12,\n\x06phones\x18\x04 \x03(\x0b\x32\x1c.tutorial.Person.PhoneNumber\x1aX\n\x0bPhoneNumber\x12\x0e\n\x06number\x18\x01 \x01(\t\x12\x39\n\x04type\x18\x02 \x01(\x0e\x32\x1a.tutorial.Person.PhoneType:\x0fPHONE_TYPE_HOME\"h\n\tPhoneType\x12\x1a\n\x16PHONE_TYPE_UNSPECIFIED\x10\x00\x12\x15\n\x11PHONE_TYPE_MOBILE\x10\x01\x12\x13\n\x0fPHONE_TYPE_HOME\x10\x02\x12\x13\n\x0fPHONE_TYPE_WORK\x10\x03\"/\n\x0b\x41\x64\x64ressBook\x12 \n\x06people\x18\x01 \x03(\x0b\x32\x10.tutorial.Person')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'addressbook.proto2_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
  DESCRIPTOR._options = None
  _globals['_PERSON']._serialized_start=33
  _globals['_PERSON']._serialized_end=324
  _globals['_PERSON_PHONENUMBER']._serialized_start=130
  _globals['_PERSON_PHONENUMBER']._serialized_end=218
  _globals['_PERSON_PHONETYPE']._serialized_start=220
  _globals['_PERSON_PHONETYPE']._serialized_end=324
  _globals['_ADDRESSBOOK']._serialized_start=326
  _globals['_ADDRESSBOOK']._serialized_end=373
# @@protoc_insertion_point(module_scope)

每個(gè)類(lèi)中的重要行是 __metaclass__ = reflection.GeneratedProtocolMessageType??梢詫⑺鼈円暈閯?chuàng)建類(lèi)的模板。在加載時(shí),GeneratedProtocolMessageType 元類(lèi)使用指定的描述符來(lái)創(chuàng)建使用每種消息類(lèi)型所需的所有 Python 方法,并將它們添加到相關(guān)的類(lèi)中。然后可以在代碼中使用完全填充的類(lèi)。

所有這一切的最終效果是,你可以使用 Person 類(lèi),就好像它將 Message 基類(lèi)的每個(gè)字段定義為常規(guī)字段一樣。例如:

import addressbook_pb2
person = addressbook_pb2.Person()
person.id = 1234
person.name = "John Doe"
person.email = "jdoe@example.com"
phone = person.phones.add()
phone.number = "555-4321"
phone.type = addressbook_pb2.Person.PHONE_TYPE_HOME

注意,這些賦值不僅僅是向通用 Python 對(duì)象添加任意新字段。如果你嘗試分配 .proto 文件中未定義的字段,則會(huì)引發(fā) AttributeError。如果你將字段分配給錯(cuò)誤類(lèi)型的值,則會(huì)引發(fā) TypeError。此外,在設(shè)置字段之前讀取字段的值會(huì)返回默認(rèn)值。

枚舉

元類(lèi)將枚舉擴(kuò)展為一組具有整數(shù)值的符號(hào)常量。因此,例如,常量 addressbook_pb2.Person.PhoneType.PHONE_TYPE_WORK 的值為 2。

標(biāo)準(zhǔn)消息方法

每個(gè)消息類(lèi)還包含許多其他方法,讓你可以檢查或操作整個(gè)消息,包括:

  • IsInitialized(): 檢查是否已設(shè)置所有必需的字段。
  • __str__():返回消息的可讀表示形式,特別適用于調(diào)試。(通常這樣調(diào)用 str(message)或 print(message)
  • CopyFrom(other_msg):使用給定消息的值覆蓋消息。
  • Clear():清除所有元素,使其返回到空狀態(tài)。

這些方法實(shí)現(xiàn)了 Message 接口。有關(guān)更多信息,請(qǐng)參閱 Message 的完整 API 文檔。

解析和序列化

每個(gè)協(xié)議緩沖區(qū)類(lèi)都具有使用協(xié)議緩沖區(qū)二進(jìn)制格式來(lái)寫(xiě)入和讀取所選類(lèi)型消息的方法。這些方法包括:

  • SerializeToString():序列化消息并將其作為字符串返回。注意,bytes是二進(jìn)制的,不是文本;僅將 str 類(lèi)型用作方便的容器。
  • ParseFromString(data):從給定的字符串解析消息。

這些只是用于解析和序列化的選擇中的一部分。同樣,請(qǐng)參閱Message API 參考以獲取完整列表。

重要

協(xié)議緩沖區(qū)和面向?qū)ο笤O(shè)計(jì) 協(xié)議緩沖區(qū)類(lèi)基本上是數(shù)據(jù)持有者(如 C 中的結(jié)構(gòu)),不提供其他功能;它們?cè)趯?duì)象模型中不是好的首要公民。如果想為生成的類(lèi)添加更豐富的行為,最好的方法是將生成的協(xié)議緩沖區(qū)類(lèi)包裝在特定于應(yīng)用程序的類(lèi)中。如果你無(wú)法控制 .proto文件的設(shè)計(jì)(例如,如果正在復(fù)用來(lái)自另一個(gè)項(xiàng)目的文件),那么包裝協(xié)議緩沖區(qū)也是一個(gè)好主意。在這種情況下,您可以使用包裝器類(lèi)來(lái)構(gòu)建更適合你應(yīng)用程序的獨(dú)特環(huán)境的接口:隱藏一些數(shù)據(jù)和方法,公開(kāi)便捷功能等。絕不應(yīng)通過(guò)繼承生成的類(lèi)繼承來(lái)向它們添加行為。這會(huì)破壞內(nèi)部機(jī)制,而且無(wú)論如何也不是好的面向?qū)ο髮?shí)踐。

編寫(xiě)消息

假設(shè)希望通訊錄應(yīng)用程序能夠做到的第一件事就是將個(gè)人詳細(xì)信息寫(xiě)入通訊錄文件。為此,需要?jiǎng)?chuàng)建和填充協(xié)議緩沖區(qū)類(lèi)的實(shí)例,然后將它們寫(xiě)入輸出流。

這里示例代碼從文件中讀取 AddressBook,根據(jù)用戶(hù)輸入向其中添加一個(gè)新 Person,然后將新的 AddressBook 再次寫(xiě)回文件。直接調(diào)用或引用協(xié)議編譯器生成的代碼的部分已突出顯示。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import addressbook_pb2
import os
def PromptForAddress(person):
    '''基于用戶(hù)輸入填充Person消息'''
    person.id = int(input('Enter person ID number: '))
    person.name = input('Enter name: ')
    email = input('Enter email address (blank for none): ')
    if email != '':
        person.email = email
    while True:
        number = input('Enter a phone number (or leave blank to finish): ')
        if number == '':
            break
        phone_number = person.phones.add()
        phone_number.number = number
        phone_type = input('Is this a mobile, home, or work phone? ')
        if phone_type == 'mobile':
            phone_number.type = addressbook_pb2.Person.PhoneType.PHONE_TYPE_MOBILE
        elif phone_type == 'home':
            phone_number.type = addressbook_pb2.Person.PhoneType.PHONE_TYPE_HOME
        elif phone_type == 'work':
            phone_number.type = addressbook_pb2.Person.PhoneType.PHONE_TYPE_WORK
        else:
            print('Unknown phone type; leaving as default value.')
address_book = addressbook_pb2.AddressBook()
# 讀取已存在地址簿
if os.path.exists('my_addressbook.db'):
    with open('my_addressbook.db', 'rb') as f:
        address_book.ParseFromString(f.read())
# 添加一個(gè)通訊地址
PromptForAddress(address_book.people.add())
# 將通訊地址寫(xiě)到磁盤(pán)
with open('my_addressbook.db', 'wb') as f:
    f.write(address_book.SerializeToString())

運(yùn)行程序后按提示輸入內(nèi)容,形如以下

Enter person ID number: 1
Enter name: shouke
Enter email address (blank for none): shouke@163.com
Enter a phone number (or leave blank to finish): 15813735565
Is this a mobile, home, or work phone? mobile
Enter a phone number (or leave blank to finish): 

讀取消息

此示例讀取上述示例創(chuàng)建的文件,并打印其中的所有信息

# -*- coding:utf-8 -*-
import addressbook_pb2
def ListPeople(address_book):
  '''遍歷地址簿中的所有people并打印相關(guān)信息'''
  for person in address_book.people:
    print('Person ID: ', person.id)
    print('Name: ', person.name)
    if person.HasField('email'):
      print('E-mail address: ', person.email)
    for phone_number in person.phones:
      if phone_number.type == addressbook_pb2.Person.PhoneType.PHONE_TYPE_MOBILE:
        print('Mobile phone #: ', end='')
      elif phone_number.type == addressbook_pb2.Person.PhoneType.PHONE_TYPE_HOME:
        print('Home phone #: ', end='')
      elif phone_number.type == addressbook_pb2.Person.PhoneType.PHONE_TYPE_WORK:
        print('Work phone #: ', end='')
      print(phone_number.number)
address_book = addressbook_pb2.AddressBook()
# 讀取已存在地址簿
with open('my_addressbook.db', 'rb') as f:
  address_book.ParseFromString(f.read())
ListPeople(address_book)

運(yùn)行輸出:

Person ID:  1
Name:  shouke
E-mail address:  shouke@163.com
Mobile phone #: 15813735565

另一個(gè)示例

例子中,定義了一個(gè)名為Device的消息,它有4個(gè)字段:nameprice,typelabels

device.proto

syntax = "proto3";
message Device {
  string name = 1;
  int32 price = 2;
  string type = 3;
  map<string, string> labels = 15;
}

根據(jù)device.proto文件生成python文件

protoc --python_out=. device.proto

自動(dòng)在當(dāng)前目錄下生成device目錄及device/proto3_pb2.py文件

使用生成的py文件(拷貝上述py文件并重命名為device_pb2.py,和以下文件存放在同級(jí)目錄)

my_test.py

# -*- coding:utf-8 -*-
import device_pb2
# 創(chuàng)建一個(gè)Person對(duì)象并設(shè)置字段值
device = device_pb2.Device()
device.name = '聯(lián)想小星'
device.price =  3999
device.type = 'Notebook'
device.labels['color'] = 'red'
device.labels['outlook'] = 'fashionable'
# 序列化Person對(duì)象為二進(jìn)制字符串
serialized_device = device.SerializeToString()
print(f"序列化后的數(shù)據(jù):{serialized_device}")
# 反序列化二進(jìn)制字符串為一個(gè)新的Person對(duì)象
new_device = device_pb2.Device()
new_device.ParseFromString(serialized_device)
# 輸出新的Device對(duì)象的字段值
print(type(new_device.labels)) # <class 'google._upb._message.ScalarMapContainer'>
for label, value in new_device.labels.items():
    print(label, value) # 輸出內(nèi)容形如:color red
print(new_device.labels) # {'color': 'red', 'outlook': 'fashionable'}
print(f'反序列化后的數(shù)據(jù):設(shè)備名稱(chēng)={new_device.name}, 價(jià)格={new_device.price}, 類(lèi)型={new_device.type}, 標(biāo)簽={new_device.labels}')
# 輸出:反序列化后的數(shù)據(jù):設(shè)備名稱(chēng)=聯(lián)想小星, 價(jià)格=3999, 類(lèi)型=Notebook, 標(biāo)簽={'color': 'red', 'outlook': 'fashionable'}

參考鏈接

https://protobuf.dev/getting-started/pythontutorial/

https://protobuf.com.cn/getting-started/pythontutorial/

https://protobuf.dev/programming-guides/proto3/

到此這篇關(guān)于在Python中使用Protocol Buffers基礎(chǔ)介紹的文章就介紹到這了,更多相關(guān)Python使用Protocol Buffers內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論