一文了解Python常见的序列化操作

python:marshal --- 内部 Python 对象序列化 阅读详情

1.marshal

marshal使用的是与Python语言相关但与机器无关的二进制来读写Python对象的。这种二进制的格式也跟Python语言的版本相关,marshal序列化的格式对不同的版本的Python是不兼容的。

marshal一般用于Python内部对象的序列化。

一般地包括:

  • 基本类型 booleans, integers,floating point numbers,complex numbers
  • 序列集合类型 strings, bytes, bytearray, tuple, list, set, frozenset, dictionary
  • code对象 code object
  • 其它类型 None, Ellipsis, StopIteration

marshal的主要作用是对Python“编译”的.pyc文件读写的支持。这也是marshal对Python版本不兼容的原因。开发者如果要使用序列化/反序列化,那么应该使用pickle模块。
常见的方法

marshal.dump(value, file[, version])

序列化一个对象到文件中

marshal.dumps(value[, version])

序列化一个对象并返回一个bytes对象

marshal.load(file)

从文件中反序列化一个对象

marshal.loads(bytes)

从bytes二进制数据中反序列化一个对象

2.pickle

pickle模块也能够以二进制的方式对Python对象进行读写。相比marshal提供基本的序列化能力,pickle的序列化应用更加广泛。

pickle序列化后的数据也是与Python语言相关的,即其它语言例如Java无法读取由Python通过pickle序列化的二进制数据。如果要使用与语言无法的序列化那么我们应该使用json。下文将会说明。

能被pickle序列化的数据类型有:

  • None, True, and False
  • integers, floating point numbers, complex numbers
  • strings, bytes, bytearrays
  • tuples, lists, sets, and dictionaries 以及包含可以被pickle序列化对象
  • 在模块顶层定义的函数对象 (使用 def定义的, 而不是 lambda表达式)
  • 在模块顶层定义内置函数
  • 在模式顶层定义的类
  • 一个类的__dict__包含了可序列化的对象或__getstate__()方法返回了能够被序列化的对象

如果pickle一个不支持序列化的对象时将会抛出PicklingError。

常见的方法

pickle.dump(obj, file, protocol=None, *, fix_imports=True)

将obj对象序列化到一个file文件中,该方法与Pickler(file, protocol).dump(obj)等价。

pickle.dumps(obj, protocol=None, *, fix_imports=True)

将obj对象序列化成bytes二进制数据。

pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict")

从file文件中反序列化一个对象,该方法与Unpickler(file).load()等价。

pickle.loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict")

从二进制数据bytes_object反序列化对象。

序列化例子

import pickle

# 定义了一个包含了可以被序列化对象的字典
data = {
    'a': [1, 2.0, 3, 4 + 6j],
    'b': ("character string", b"byte string"),
    'c': {None, True, False}
}

with open('data.pickle', 'wb') as f:
    # 序列化对象到一个data.pickle文件中
    # 指定了序列化格式的版本pickle.HIGHEST_PROTOCOL
    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

执行之后在文件夹中多一个data.pickle文件

serialization
├── data.pickle
├── pickles.py
└── unpickles.py

反序列化例子

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import pickle

with open('data.pickle', 'rb') as f:
    # 从data.pickle文件中反序列化对象
    # pickle能够自动检测序列化文件的版本
    # 所以这里可以不用版本号
    data = pickle.load(f)

    print(data)

# 执行后结果
# {'a': [1, 2.0, 3, (4+6j)], 'b': ('character string', b'byte string'), 'c': {False, True, None}}

3.json

json是与语言无关,非常通用的数据交互格式。在Python它与marshal和pickle一样拥有相似的API。
常见的方法

json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

序列化对象到fp文件中

json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

将obj序列化成json对象

json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

从文件中反序列化成一个对象

json.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

从json格式文档中反序列化成一个对象

json与Python对象的转化对照表
在这里插入图片描述
对于基本类型、序列、以及包含基本类型的集合类型json都可以很好的完成序列化工作。

序列化例子

>>> import json
>>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> print(json.dumps("\"foo\bar"))
"\"foo\bar"
>>> print(json.dumps('\u1234'))
"\u1234"
>>> print(json.dumps('\\'))
"\\"
>>> print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True))
{"a": 0, "b": 0, "c": 0}
>>> from io import StringIO
>>> io = StringIO()
>>> json.dump(['streaming API'], io)
>>> io.getvalue()
'["streaming API"]'

反序列化例子

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>> import json
>>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
['foo', {'bar': ['baz', None, 1.0, 2]}]
>>> json.loads('"\\"foo\\bar"')
'"foo\x08ar'
>>> from io import StringIO
>>> io = StringIO('["streaming API"]')
>>> json.load(io)
['streaming API']

对于object的情况就复杂一些了

例如定义了复数complex对象的json文档

complex_data.json

{
  "__complex__": true,
  "real": 42,
  "imaginary": 36
}

要把这个json文档反序列化成Python对象,就需要定义转化的方法

# coding=utf-8
import json

# 定义转化函数,将json中的内容转化成complex对象
def decode_complex(dct):
    if "__complex__" in dct:
        return complex(dct["real"], dct["imaginary"])
    else:
        return dct

if __name__ == '__main__':
    with open("complex_data.json") as complex_data:
        # object_hook指定转化的函数
        z = json.load(complex_data, object_hook=decode_complex)
        print(type(z))
        print(z)

# 执行结果
# <class 'complex'>
# (42+36j)

如果不指定object_hook,那么默认将json文档中的object转成dict

# coding=utf-8
import json

if __name__ == '__main__':

    with open("complex_data.json") as complex_data:
        # 这里不指定object_hook
        z2 = json.loads(complex_data.read())
        print(type(z2))
        print(z2)
# 执行结果
# <class 'dict'>
# {'__complex__': True, 'real': 42, 'imaginary': 36}

可以看到json文档中的object转成了dict对象。

一般情况下这样使用似乎也没什么问题,但如果对类型要求很高的场景就需要明确定义转化的方法了。

除了object_hook参数还可以使用json.JSONEncoder

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import json

class ComplexEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, complex):
            # 如果complex对象这里转成数组的形式
            return [obj.real, obj.imag]
            # 默认处理
        return json.JSONEncoder.default(self, obj)

if __name__ == '__main__':
    c = json.dumps(2 + 1j, cls=ComplexEncoder)
    print(type(c))
    print(c)

# 执行结果
# <class 'str'>
# [2.0, 1.0]

因为json模块并不是对所有类型都能够自动完成序列化的,对于不支持的类型,会直接抛出TypeError。

>>> import datetime
>>> d = datetime.datetime.now()
>>> dct = {'birthday':d,'uid':124,'name':'jack'}
>>> dct
{'birthday': datetime.datetime(2019, 6, 14, 11, 16, 17, 434361), 'uid': 124, 'name': 'jack'}
>>> json.dumps(dct)
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    json.dumps(dct)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable

对于不支持序列化的类型例如datetime以及自定义类型,就需要使用JSONEncoder来定义转化的逻辑。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import json
import datetime

# 定义日期类型的JSONEncoder
class DatetimeEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, datetime.date):
            return obj.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, obj)

if __name__ == '__main__':
    d = datetime.date.today()
    dct = {"birthday": d, "name": "jack"}
    data = json.dumps(dct, cls=DatetimeEncoder)
    print(data)

# 执行结果
# {"birthday": "2019-06-14", "name": "jack"}

现在我们希望发序列化时,能够将json文档中的日期格式转化成datetime.date对象,这时就需要使用到json.JSONDecoder了。

# coding=utf-8
import json
import datetime

# 定义Decoder解析json
class DatetimeDecoder(json.JSONDecoder):

    # 构造方法
    def __init__(self):
        super().__init__(object_hook=self.dict2obj)

    def dict2obj(self, d):
        if isinstance(d, dict):
            for k in d:
                if isinstance(d[k], str):
                    # 对日期格式进行解析,生成一个date对象
                    dat = d[k].split("-")
                    if len(dat) == 3:
                        date = datetime.date(int(dat[0]), int(dat[1]), int(dat[2]))
                        d[k] = date
        return d

if __name__ == '__main__':
    d = datetime.date.today()
    dct = {"birthday": d, "name": "jack"}
    data = json.dumps(dct, cls=DatetimeEncoder)
    # print(data)

    obj = json.loads(data, cls=DatetimeDecoder)
    print(type(obj))
    print(obj)

# 执行结果
# {"birthday": "2019-06-14", "name": "jack"}
# <class 'dict'>
# {'birthday': datetime.date(2019, 6, 14), 'name': 'jack'}

4.总结一下

Python常见的序列化工具有marshal、pickle和json。marshal主要用于Python的.pyc文件,并与Python版本相关。它不能序列化用户定义的类。

pickle是Python对象的序列化工具则比marshal更通用些,它可以兼容Python的不同版本。json是一种语言无关的数据结构,广泛用于各种网络应用尤其在REST API的服务中的数据交互。

Python 操作 protobuf 常见用法 ProtoBuf: 是一套完整的 IDL(接口描述语言),出自Google,基于 C++ 进行的实现,开发人员可以根据 ProtoBuf 的语言规范生成多种编程语言(Golang、Python、Java 等)的接口代码,本篇只讲述 Python 的基础操作。据说 ProtoBuf 所生成的二进制文件在存储效率上比 XML 高 3~10 倍,并且处理性能高 1~2 个数量级,这也是选择 ProtoB... 阅读详情

相关推荐

Python教程:一文了解Python中的序列化与反序列化

在现代编程中,数据的存储与传输占据着重要的地位。Python作为一种流行的编程语言,提供了多种序列化与反序列化(Serialization and Deserialization)的方法。本文将系统地介绍Python中的序列化与反序列化,包括基本概念、常用模块、详细代码示例以及注意事项和常见问题。

旦莫的博客 1750

Python marshal模块 主要函数 marshal.dump(object, file) Marshal.dumps(object) Marshal.load——《跟老吕学Python编程》

是一个强大的工具,用于在Python中处理二进制数据。通过了解它的功能和限制,你可以更有效地使用它来序列化和反序列化Python对象。然而,由于它的限制,如果你需要处理更复杂的对象或需要在不同的Python版本或平台上交换数据,你可能需要考虑使用其他序列化模块,如pickle或json。👨‍💻博主Python老吕评论,您的举手之劳将对我提供了无限的写作动力!🤞🔥《跟老吕学Python编程》《Python游戏开发实战讲解》《Python Web开发实战》《Python网络爬虫实战》

Python老吕的博客 1585

Python处理JSON文件数据各类操作一文详解+实例代码

前言 本文将详细描述如何利用python来处理JSON文件,将由浅入深从JSON文件格式介绍一步一步到python自带json库函数解析,整合上述操作再实现python脚本处理JSON文件。 一、JSON介绍 JSON(JavaScriptObject Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于ECMAScript(欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语...

master_hunter的博客 6048

python marshal loads failed_python使用marshal模块序列化实例

本文实例讲述了python使用marshal模块序列化的方法,分享给大家供大家参考。具体方法如下:先来看看下面这段代码:?12345678910111213141516171819202122232425262728 import marshal data1 = ['abc',12,23,'jb51'] #几个测试数据 data2 = {1:'aaa',b:'dad'} data3 = (1,2,...

weixin_35823403的博客 1082

单细胞分析实战:当Seurat的SCTransform遇上Harmony,我的整合流程优化笔记

本文详细介绍了单细胞转录组数据分析中SCTransform与Harmony整合流程的优化策略。通过实战案例展示如何调整SCTransform参数、优化Harmony整合效果,并解决常见问题,帮助研究者获得更可靠的生物学发现。特别适合需要处理批次效应的单细胞数据分析人员。

weixin_42552716的博客 361

超越界面设计:GUI Guider与LVGL在STM32F103上的系统集成与架构思考

本文探讨了GUI Guider与LVGL在STM32F103微控制器上的系统集成与架构设计,重点分析了工程模块化组织、内存管理优化、硬件驱动接口设计以及FreeRTOS多任务集成。针对STM32F103ZET6的资源限制,提供了实用的移植策略和性能优化方案,帮助开发者构建高效、可维护的嵌入式GUI系统。

j2k3l4的博客 924

python-文件操作:marshal

''' python程序将多个数据存储到文件中 ''' # 数据准备 s = "字符串" i = 19 f = 3.1415926 b = True c = 5 + 2p l = [2, "你好", 8] d = {"username": "admin", "password": "123"} #将上面多个数据放在同一个列表中 x = [s, i, f, b, c, l, d]

weixin_44606322的博客 591

基-2 16点fft的verilog实现

本文是采用输入8位数据,输出8位数据的16点fft实现,旋转因子采用8位rom存储,分为cos和sin2个rom存储;输入数据以及每一级的输出数据采用8位的ram进行存储,分为实部和虚部进行存储。采用顺序输入,那么输出则为倒序。生成rom,可先在matlab中,计算出cos和sin的值,然后写入coe文件,再由ip核生成,具体过程自行百度,懒得百度的话,可以发我的邮箱来获得具体的程序。因为本文采用...

juniorofMFC的博客 9992

9.4 marshal--Python内部对象序列化

本模块主要提供了针对Python内部对象进行保存到文件,以及从文件读取对象出来。保存到文件的格式采用二进制的方式,不过这个格式并没有文档介绍,因为本模块主要使用在Python内部临时编译代码.pyc文件进行操作,并且不考虑不同Python版本之间的兼容性。本模块存在的意义就是为了Python内部使用,比如Python内部的库等等。marshal.dump(value, file[, version

大坡3D软件开发 3958

算法面试八股文『 基础知识篇 』

个人整理的算法面试基础知识八股文,内容较详细。

SG_teresa's blog 4339

从聊天框到AI智能体:Agent原理、落地路径与工程实践

大模型时代,AI智能体(Agent)正从概念走向工程实践。与传统的聊天框一问一答不同,Agent强调目标驱动、任务拆解、工具调用与自主执行,其核心是一个由大模型、工具集、记忆和控制循环构成的闭环系统。理解这一原理,是开发者从“提问者”升级为“任务定义者”的关键。Agent的技术价值在于能处理重复性信息收集、多步骤内容生产、代码辅助和数据分析等真实场景,显著提升自动化水平。然而,大多数用户仍停留在聊天框阶段,原因包括产品入口、用户习惯、使用成本和提示词能力等多重因素。从聊天框平滑迁移到Agent,需要经历目标

weixin_29048309的博客 324

python序列化工具_一文了解Python常见的序列化操作

关于我一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android、Python、Java和Go,这个也是我们团队的主要技术栈。Github:https://github.com/hylinux1024微信公众号:终身开发者(angrycode)0x00 marshalmarshal使用的是与Python语言相关但与机器无关的二进制来读写Python对象的。...

weixin_39863155的博客 204

基于深度学习的柑橘病害检测系统——YOLOv8 + UI界面 + 数据集

深度学习是机器学习的一种重要分支,它通过构建多层的神经网络(尤其是卷积神经网络CNN)来从大规模的数据中学习特征。近年来,深度学习特别是在计算机视觉领域取得了显著的进展,成为图像分类、目标检测、图像分割等任务的主要技术。对于农业病害检测问题,深度学习通过对大量带标签的病害图像进行训练,能够自动识别不同类型的病害,并判断病害的程度。通过图像中目标(病害区域)的边界框,可以快速定位病害区域,进一步分析其影响范围。

YOLO 1044

obj是什么数据类型 python_一文了解Python常见的序列化操作

0x00 marshalmarshal 使用的是与Python 语言相关但与机器无关的二进制来读写Python 对象的。这种二进制的格式也跟Python 语言的版本相关,marshal 序列化的格式对不同的版本的Python 是不兼容的。marshal 一般用于Python 内部对象的序列化。一般地包括:booleans, integers,floating point numbers,compl...

weixin_42363600的博客 1221

STM32嵌入式标准化封装五层架构设计与工程实践

在嵌入式系统开发中,代码封装是保障可维护性、可复用性与可靠性的核心工程能力。其本质是通过分层抽象(硬件抽象层→设备驱动层→应用逻辑层)解耦硬件依赖与业务逻辑,实现跨芯片移植、多任务安全及确定性实时响应。关键技术包括HAL库重构以消除硬编码初始化、驱动层提供同步/异步/事件驱动三类UART接口、中断优先级矩阵管理确保响应确定性、双阶段时钟配置兼顾启动鲁棒性与运行能效,以及面向Modbus等工业协议的语义化API封装。这些实践直击STM32项目中引脚变更难、HAL裸调风险高、中断处理不安全、时钟配置僵化等典型痛

weixin_35019679的博客 68

同步调相机启动与并网控制技术方案

同步调相机起动与并网控制技术方案采用背靠背变流器系统,通过静止变频器(SFC)策略实现无原动机驱动的同步电机从静止到同步转速的平稳起动。方案详细阐述了硬件自检、模块初始化、转子位置检测等关键环节,重点解决启动转矩为零、冲击电流大等挑战。系统基于STM32或C2000系列控制器,包含网侧/机侧变流器、检测保护单元及通信接口,通过励磁脉冲和电压响应分析实现高精度转子初始定位,为后续并网提供必要条件。代码级实现涵盖了ADC自检、数字IO验证及通信端口测试等核心功能。

huanghm88的专栏 309

反序列化漏洞详解 一文了解反序列化漏洞

本篇总结归纳反序列化漏洞,包括php、java、python三种语言

闵行小鱼塘 6445

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

了解Python常见的序列化操作

marshal marshal使用的是与Python语言相关但与机器无关的二进制来读写Python对象的。这种二进制的格式也跟Python语言的版本相关,marshal序列化的格式对不同的版本的Python是不兼容的。 marshal一般用于Python内部对象的序列化。 一般地包括: 基本类型 booleans, integers,floating point numbers,complex n...

qq_39581763的博客 316

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

python中的序列化_一文了解Python序列化

程序运行时,所有的变量都储存在内存中,程序结束运行时,这些占用的内存将被系统回收,无法长期储存,将这些变量转换为可储存或可通过网络传输的过程称之为序列化(pickling),序列化后就可以将它们储存在磁盘或通过网络进行传输。1.pickle序列化Python提供了pickle模块来实现变量的序列化,这个模块可以将变量转换成字节码(bytes)形式储存,还能将储存的序列化字节码重新还原成数据对象;注...

weixin_29474859的博客 647

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

python反序列化

pickle序列化:将 Python 对象转换为可以存储或传输的格式。反序列化:将序列化的数据转换回 Python 对象。pickle模块:支持复杂的 Python 对象,但存在安全风险。json模块:支持 JSON 格式,更安全,适用于大多数应用场景。安全性:处理不受信任的数据时,应避免使用pickle。r:只读模式。w:写入模式,会清空原有内容。a:追加模式。b:二进制模式。:读写模式。x:独占创建模式。t:文本模式(默认)。

2301_79783285的博客 2290

一文看懂Python对JSON序列化与反序列化解析教程(小白版)

Python JSON处理简明教程 本教程介绍了Python中处理JSON数据的基本方法。JSON是一种轻量级数据交换格式,Python通过内置json模块支持JSON序列化与反序列化。 主要内容包括: JSON与Python数据类型的对应关系 使用json.dumps()将Python对象转为JSON字符串 使用json.loads()将JSON字符串转为Python对象 文件读写操作(json.dump/json.load) 处理中文和格式化输出的技巧 复杂JSON(含数组和嵌套结构)的处理方法 教程适

isoft888的专栏,致力于C#/python分享物联网、大数据,神经网络的等领域,欢迎讨论交流! 943

了解python之(反)序列化

了解python之(反)序列化 pickle序列化和反序列化 pickle只能在python间进行内存共享; 序列化语法: pickle.dump(obj,file,[,protocol]) obj--序列化对象 file--把序列化的对象obj保存到文件中,file必须有write()接口,file可以是以w模式打开的文件,也可以是任何可以实现write()接口的对象。 protocol--序列化模式,默认是0,(ASCII协议,表示以文本的形式进行序列化)。protocol还可以是1和.

王小东大将军的博客 1097
上一篇: 一文搞懂Python中的所有数组数据类型
下一篇: python逐行读取文件内容的三种方法
Python热爱者
博客等级 码龄7年 3059粉丝 1330原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值