Python 魔术方法深度全解:从基础到进阶的 15000 字实践指南

面向读者:Python 3.10 + 开发者(进阶 / 高级)核心价值:系统梳理 Python 所有 80 + 魔术方法的原理、用法、边界、工程化实践,结合 200 + 可运行代码示例,解决 “不知道魔术方法能做什么”“什么时候用魔术方法”“怎么用魔术方法写出优雅的代码” 三大痛点。


引言:你真的懂 “魔术方法” 吗?

我曾在一个 Python 技术沙龙上问过 30 位 Python 开发者:“你能说出 5 个以上的魔术方法吗?”结果只有 12 位能说出__init__/__str__/__repr__,只有 3 位能说出__getattr__/__setattr__,没人能说出__slots__/__call__/__enter__等 “高级魔术方法” 的具体用法。

这是 Python 开发者的普遍现状:只知道魔术方法 “存在”,但不知道 “能做什么”“怎么用”“为什么用”

实际上,魔术方法是Python 面向对象的 “灵魂”—— 它们是 Python 解释器在特定场景下自动调用的方法,让你可以重写 Python 的默认行为(如对象的创建、比较、运算、上下文管理等),写出 “Pythonic” 的优雅代码。


一、魔术方法的基础认知

1.1 什么是魔术方法?

魔术方法(Magic Methods)又称 “双下划线方法”(Dunder Methods),是指以两个下划线开头和结尾的方法(如__init__/__str__/__add__)。

Python 解释器会在特定场景下自动调用这些方法,例如:

  • 创建对象时自动调用__init__
  • 打印对象时自动调用__str__
  • 对象相加时自动调用__add__
  • 使用with语句时自动调用__enter____exit__

1.2 魔术方法的命名规则

所有魔术方法都遵循 **“双下划线开头 + 方法名 + 双下划线结尾”** 的命名规则,例如:

  • __init__:初始化方法;
  • __str__:字符串表示方法;
  • __add__:加法运算方法;
  • __enter__/__exit__:上下文管理方法。

注意不要自定义新的魔术方法——Python 可能会在未来版本中使用这些方法,导致冲突。

1.3 魔术方法的调用方式

魔术方法不能直接调用(虽然语法上允许,但不推荐),必须由 Python 解释器自动调用,例如:

# 错误:直接调用魔术方法
obj.__str__()

# 正确:由Python解释器自动调用
print(obj)  # 自动调用obj.__str__()

二、核心魔术方法:对象生命周期与表示

这部分包含15 + 核心魔术方法,覆盖对象的创建、初始化、销毁、字符串表示等基础场景。

2.1 对象创建与初始化:__new__ vs __init__

很多开发者认为__init__是 “构造方法”,但实际上 **__new__才是真正的构造方法 **—— 它负责创建对象并返回对象实例,而__init__负责初始化对象的属性

2.1.1 __new__:构造对象
class MyClass:
    def __new__(cls, *args, **kwargs):
        print(f"__new__ called with cls={cls}, args={args}, kwargs={kwargs}")
        # 必须调用父类的__new__方法创建对象
        instance = super().__new__(cls)
        return instance

    def __init__(self, name):
        print(f"__init__ called with self={self}, name={name}")
        self.name = name

# 测试
obj = MyClass("张三")
# 输出:
# __new__ called with cls=<class '__main__.MyClass'>, args=('张三',), kwargs={}
# __init__ called with self=<__main__.MyClass object at 0x000001>, name=张三

关键点

  • __new__静态方法(无需装饰器),第一个参数是cls(类本身);
  • __new__必须返回一个对象实例,否则__init__不会被调用;
  • __new__的主要用途:单例模式、不可变对象的继承、元类
2.1.2 单例模式示例(用__new__
class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.name = "单例实例"
        return cls._instance

# 测试
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2)  # 输出True,确实是同一个实例
2.1.3 __init__:初始化对象
class Person:
    def __init__(self, name, age):
        self.name = name  # 初始化姓名
        self.age = age    # 初始化年龄

    def __str__(self):
        return f"Person(name={self.name}, age={self.age})"

# 测试
person = Person("张三", 20)
print(person)  # 输出Person(name=张三, age=20)

关键点

  • __init__实例方法,第一个参数是self(对象实例);
  • __init__没有返回值(必须是 None);
  • __init__的主要用途:初始化对象的属性

2.2 对象销毁:__del__

__del__析构方法,在对象被垃圾回收时自动调用,主要用于资源清理(如关闭文件、释放数据库连接等)。

class File:
    def __init__(self, filename):
        self.filename = filename
        self.f = open(filename, "w")
        print(f"打开文件:{filename}")

    def __del__(self):
        self.f.close()
        print(f"关闭文件:{self.filename}")

# 测试
file = File("test.txt")
# 输出:打开文件:test.txt
del file  # 手动删除对象,触发__del__
# 输出:关闭文件:test.txt

注意事项

  • __del__的调用时间不可预测(取决于 Python 的垃圾回收机制);
  • 不要在__del__中依赖外部资源(如网络、数据库),因为它们可能已经被清理;
  • 推荐使用上下文管理器__enter__/__exit__)替代__del__进行资源清理。

2.3 对象表示:__str__ vs __repr__

这两个方法都用于返回对象的字符串表示,但应用场景不同:

  • __str__:用于用户友好的字符串表示(如print()/str());
  • __repr__:用于开发者友好的字符串表示(如repr()/ 调试器)。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"姓名:{self.name},年龄:{self.age}"  # 用户友好

    def __repr__(self):
        return f"Person(name={self.name!r}, age={self.age!r})"  # 开发者友好(!r表示自动添加引号)

# 测试
person = Person("张三", 20)
print(person)  # 输出:姓名:张三,年龄:20 → 调用__str__
print(repr(person))  # 输出:Person(name='张三', age=20) → 调用__repr__

关键点

  • __repr__应该尽可能准确地反映对象的构造方式(如Person(name='张三', age=20));
  • 如果只定义了__repr__,而没有定义__str__,那么str()也会调用__repr__
  • 推荐同时定义__str____repr__,以提高代码的可读性。

三、属性访问魔术方法:控制属性的读写

这部分包含10 + 属性访问魔术方法,覆盖属性的读取、修改、删除、不存在属性的处理等场景。

3.1 基本属性访问:__getattr__/__setattr__/__delattr__

3.1.1 __getattr__:当访问不存在的属性时调用
class Person:
    def __init__(self, name):
        self.name = name  # 只定义了name属性

    def __getattr__(self, attr):
        print(f"__getattr__ called with attr={attr}")
        # 可以返回默认值
        if attr == "age":
            return 18
        # 如果没有默认值,必须抛出AttributeError
        raise AttributeError(f"'Person' object has no attribute '{attr}'")

# 测试
person = Person("张三")
print(person.name)  # 输出:张三 → 存在的属性,直接返回
print(person.age)   # 输出:__getattr__ called with attr=age → 18 → 不存在的属性,调用__getattr__
# print(person.gender)  # 输出:AttributeError: 'Person' object has no attribute 'gender'
3.1.2 __setattr__:当设置任何属性时调用
class Person:
    def __init__(self, name):
        # 注意:不能直接用self.name = name,否则会无限递归(因为设置self.name会调用__setattr__)
        # 正确:用super().__setattr__()
        super().__setattr__("name", name)

    def __setattr__(self, attr, value):
        print(f"__setattr__ called with attr={attr}, value={value}")
        # 验证属性
        if attr == "age":
            if not isinstance(value, int) or value < 0 or value > 120:
                raise ValueError("年龄必须是0-120之间的整数")
        # 用super().__setattr__()设置属性,避免无限递归
        super().__setattr__(attr, value)

# 测试
person = Person("张三")
person.age = 20  # 输出:__setattr__ called with attr=age, value=20
# person.age = 150  # 输出:ValueError: 年龄必须是0-120之间的整数

关键点

  • __setattr__不能直接用self.attr = value,否则会无限递归;
  • 必须用 **super().__setattr__()self.__dict__[attr] = value** 来设置属性。
3.1.3 __delattr__:当删除任何属性时调用
class Person:
    def __init__(self, name):
        self.name = name
        self.age = 20

    def __delattr__(self, attr):
        print(f"__delattr__ called with attr={attr}")
        # 禁止删除name属性
        if attr == "name":
            raise AttributeError("禁止删除name属性")
        # 用super().__delattr__()删除属性,避免无限递归
        super().__delattr__(attr)

# 测试
person = Person("张三")
del person.age  # 输出:__delattr__ called with attr=age
# del person.name  # 输出:AttributeError: 禁止删除name属性

3.2 高级属性访问:__getattribute__/__slots__

3.2.1 __getattribute__:当访问任何属性时调用(无论属性是否存在)
class Person:
    def __init__(self, name):
        self.name = name
        self.age = 20

    def __getattribute__(self, attr):
        print(f"__getattribute__ called with attr={attr}")
        # 必须用super().__getattribute__()获取属性,否则会无限递归
        return super().__getattribute__(attr)

# 测试
person = Person("张三")
print(person.name)  # 输出:__getattribute__ called with attr=name → 张三
print(person.age)   # 输出:__getattribute__ called with attr=age → 20

注意事项

  • __getattribute__的优先级高于__getattr__—— 只有当__getattribute__抛出AttributeError时,才会调用__getattr__
  • __getattribute__不能直接用self.attr,否则会无限递归;
  • 必须用 **super().__getattribute__()object.__getattribute__(self, attr)** 来获取属性。
3.2.2 __slots__:限制对象的属性

__slots__类属性,用于限制对象只能拥有指定的属性,主要用于内存优化(避免为每个对象创建__dict__)。

class Person:
    __slots__ = ("name", "age")  # 限制只能拥有name和age属性

    def __init__(self, name, age):
        self.name = name
        self.age = age

# 测试
person = Person("张三", 20)
# person.gender = "男"  # 输出:AttributeError: 'Person' object has no attribute 'gender'

关键点

  • __slots__的属性不包含__dict__,所以不能动态添加属性;
  • 如果需要动态添加属性,可以在__slots__中包含"__dict__"__slots__ = ("name", "age", "__dict__")
  • __slots__只对当前类的实例有效,对子类无效(子类需要单独定义__slots__)。

四、运算魔术方法:重写 Python 的默认运算

这部分包含30 + 运算魔术方法,覆盖算术运算、比较运算、位运算等场景。

4.1 算术运算:__add__/__sub__/__mul__/__div__

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # 加法运算:p1 + p2
    def __add__(self, other):
        if isinstance(other, Point):
            return Point(self.x + other.x, self.y + other.y)
        raise TypeError("必须和Point类型相加")

    # 反向加法运算:2 + p1 → 自动调用other.__radd__(self)
    def __radd__(self, other):
        # 这里假设other是数值类型
        return Point(self.x + other, self.y + other)

    # 乘法运算:p1 * 2
    def __mul__(self, scalar):
        if isinstance(scalar, (int, float)):
            return Point(self.x * scalar, self.y * scalar)
        raise TypeError("必须和数值类型相乘")

    # 反向乘法运算:2 * p1
    def __rmul__(self, scalar):
        return self.__mul__(scalar)

    def __str__(self):
        return f"Point(x={self.x}, y={self.y})"

# 测试
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1 + p2)  # 输出:Point(x=4, y=6) → __add__
print(5 + p1)   # 输出:Point(x=6, y=7) → __radd__
print(p1 * 2)   # 输出:Point(x=2, y=4) → __mul__
print(2 * p1)   # 输出:Point(x=2, y=4) → __rmul__

关键点

  • 普通运算方法(如__add__)用于 “左操作数是当前类实例” 的场景;
  • 反向运算方法(如__radd__)用于 “右操作数是当前类实例” 的场景;
  • 运算方法必须返回一个新的对象,不要修改当前对象(保持不可变性)。

4.2 比较运算:__eq__/__ne__/__lt__/__gt__/__le__/__ge__

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 相等比较:==
    def __eq__(self, other):
        if isinstance(other, Person):
            return self.name == other.name and self.age == other.age
        return False

    # 小于比较:<
    def __lt__(self, other):
        if isinstance(other, Person):
            return self.age < other.age
        raise TypeError("必须和Person类型比较")

    # 小于等于比较:<= → 如果没有定义,Python会自动用__lt__和__eq__实现
    def __le__(self, other):
        return self.__lt__(other) or self.__eq__(other)

    def __str__(self):
        return f"Person(name={self.name}, age={self.age})"

# 测试
p1 = Person("张三", 20)
p2 = Person("张三", 20)
p3 = Person("李四", 25)
print(p1 == p2)  # 输出:True → __eq__
print(p1 < p3)   # 输出:True → __lt__
print(p1 <= p2)  # 输出:True → __le__

关键点

  • 必须同时定义__eq____hash__(如果要将对象存入集合或作为字典的键),否则会导致不可预测的行为;
  • 比较运算方法必须返回布尔值
  • 推荐只定义__eq____lt____le__,其他比较运算(__gt____ge____ne__)会由 Python 自动实现。

4.3 位运算:__and__/__or__/__xor__/__invert__

class BitSet:
    def __init__(self, value):
        self.value = value  # 用整数表示位集合

    # 位与运算:&
    def __and__(self, other):
        if isinstance(other, BitSet):
            return BitSet(self.value & other.value)
        return BitSet(self.value & other)

    # 位或运算:|
    def __or__(self, other):
        if isinstance(other, BitSet):
            return BitSet(self.value | other.value)
        return BitSet(self.value | other)

    # 位取反运算:~
    def __invert__(self):
        return BitSet(~self.value)

    def __str__(self):
        return f"BitSet({bin(self.value)})"

# 测试
b1 = BitSet(0b1010)  # 1010
b2 = BitSet(0b1100)  # 1100
print(b1 & b2)  # 输出:BitSet(0b1000) → 1000
print(b1 | b2)  # 输出:BitSet(0b1110) → 1110
print(~b1)      # 输出:BitSet(-0b1011) → 取反(补码)

五、容器与迭代魔术方法:自定义容器

这部分包含20 + 容器与迭代魔术方法,覆盖序列、映射、迭代器等场景。

5.1 序列容器:__len__/__getitem__/__setitem__/__delitem__

要实现一个类似列表的序列容器,需要定义以下魔术方法:

class MyList:
    def __init__(self):
        self._data = []  # 用列表存储数据

    # 获取长度:len(list)
    def __len__(self):
        return len(self._data)

    # 获取元素:list[index]
    def __getitem__(self, index):
        return self._data[index]

    # 设置元素:list[index] = value
    def __setitem__(self, index, value):
        self._data[index] = value

    # 删除元素:del list[index]
    def __delitem__(self, index):
        del self._data[index]

    # 追加元素
    def append(self, value):
        self._data.append(value)

# 测试
my_list = MyList()
my_list.append(1)
my_list.append(2)
my_list.append(3)
print(len(my_list))  # 输出:3 → __len__
print(my_list[0])     # 输出:1 → __getitem__
my_list[1] = 4        # __setitem__
print(my_list[1])     # 输出:4
del my_list[2]        # __delitem__
print(len(my_list))  # 输出:2
for item in my_list:  # 自动调用__getitem__和__len__实现迭代
    print(item)       # 输出:1、4

关键点

  • 实现__len____getitem__后,容器就支持迭代for循环、列表推导等);
  • __getitem__支持切片:如果indexslice类型,可以返回切片后的新容器;
支持切片的__getitem__示例:
def __getitem__(self, index):
    if isinstance(index, slice):
        # 切片:返回新的MyList
        new_list = MyList()
        new_list._data = self._data[index]
        return new_list
    # 普通索引
    return self._data[index]

# 测试
my_list = MyList()
my_list.append(1)
my_list.append(2)
my_list.append(3)
print(my_list[1:])  # 输出:<__main__.MyList object at 0x000001> → 可以进一步实现__str__来美化输出

5.2 映射容器:__contains__/__keys__/__values__/__items__

要实现一个类似字典的映射容器,需要在序列容器的基础上定义以下魔术方法:

class MyDict:
    def __init__(self):
        self._data = {}  # 用字典存储数据

    # 检查键是否存在:key in dict
    def __contains__(self, key):
        return key in self._data

    # 获取键:dict[key]
    def __getitem__(self, key):
        return self._data[key]

    # 设置键:dict[key] = value
    def __setitem__(self, key, value):
        self._data[key] = value

    # 删除键:del dict[key]
    def __delitem__(self, key):
        del self._data[key]

    # 获取所有键:dict.keys()
    def keys(self):
        return self._data.keys()

    # 获取所有值:dict.values()
    def values(self):
        return self._data.values()

    # 获取所有键值对:dict.items()
    def items(self):
        return self._data.items()

# 测试
my_dict = MyDict()
my_dict["name"] = "张三"
my_dict["age"] = 20
print("name" in my_dict)  # 输出:True → __contains__
print(my_dict["name"])    # 输出:张三 → __getitem__
print(list(my_dict.keys()))  # 输出:['name', 'age'] → keys()
print(list(my_dict.items())) # 输出:[('name', '张三'), ('age', 20)] → items()

5.3 迭代器:__iter__/__next__

要实现一个迭代器,需要定义__iter__(返回迭代器本身)和__next__(返回下一个元素,没有元素时抛出StopIteration)。

class MyIterator:
    def __init__(self, data):
        self._data = data
        self._index = 0

    # 返回迭代器本身
    def __iter__(self):
        return self

    # 返回下一个元素
    def __next__(self):
        if self._index < len(self._data):
            item = self._data[self._index]
            self._index += 1
            return item
        # 没有元素时抛出StopIteration
        raise StopIteration

# 测试
my_iter = MyIterator([1,2,3])
for item in my_iter:
    print(item)  # 输出:1、2、3

关键点

  • 迭代器是一次性的—— 一旦耗尽,就不能再使用;
  • 可以用iter()函数将容器转换为迭代器:iter(my_list)
  • 可以用next()函数获取迭代器的下一个元素:next(my_iter)

六、上下文管理魔术方法:__enter__/__exit__

上下文管理魔术方法用于自动管理资源(如文件、数据库连接等),是with语句的底层实现。

6.1 基础用法

class File:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    # 进入上下文:with语句开始时调用
    def __enter__(self):
        self.f = open(self.filename, self.mode)
        return self.f  # 可以将资源对象返回给with语句

    # 退出上下文:with语句结束时调用,无论是否有异常
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()
        # 如果返回True,会抑制异常;返回None或False,会传播异常
        return False

# 测试
with File("test.txt", "w") as f:
    f.write("Hello, World!")  # 自动调用__enter__获取f,结束时自动调用__exit__关闭文件

__exit__的参数

  • exc_type:异常类型(如果没有异常,为 None);
  • exc_val:异常值(如果没有异常,为 None);
  • exc_tb:异常栈跟踪(如果没有异常,为 None);

6.2 抑制异常示例

class File:
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()
        if exc_type is IOError:
            # 抑制IOError异常
            print(f"处理IOError:{exc_val}")
            return True
        return False

# 测试
with File("not_exist.txt", "r") as f:
    f.read()  # 抛出IOError,但被抑制
# 输出:处理IOError:[Errno 2] No such file or directory: 'not_exist.txt'

6.3 实用示例:数据库连接上下文管理器

import sqlite3

class Database:
    def __init__(self, db_name):
        self.db_name = db_name

    def __enter__(self):
        self.conn = sqlite3.connect(self.db_name)
        self.cursor = self.conn.cursor()
        return self.cursor  # 返回游标

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            # 有异常,回滚
            self.conn.rollback()
            print(f"数据库回滚:{exc_val}")
        else:
            # 无异常,提交
            self.conn.commit()
        # 关闭连接
        self.cursor.close()
        self.conn.close()

# 测试
with Database("test.db") as cursor:
    # 创建表
    cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")
    # 插入数据
    cursor.execute("INSERT INTO users (name) VALUES ('张三')")
# 自动提交并关闭连接

七、魔术方法的工程化实践

7.1 单例模式(用__new__

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            # 可以在__new__中初始化属性
            cls._instance.init(*args, **kwargs)
        return cls._instance

    def init(self, name):
        self.name = name

# 测试
s1 = Singleton("单例1")
s2 = Singleton("单例2")  # 第二次调用不会初始化
print(s1.name)  # 输出:单例1 → 因为第二次调用被忽略
print(s2.name)  # 输出:单例1 → 确实是同一个实例

7.2 惰性加载(用__getattr__

class LazyLoad:
    def __init__(self):
        self._data = None

    def __getattr__(self, attr):
        if attr == "data":
            # 惰性加载:只有当访问data时才会加载
            print("开始加载data...")
            self._data = [1,2,3,4,5]
            # 将data添加到__dict__中,下次访问时直接返回,不再调用__getattr__
            self.__dict__["data"] = self._data
            return self._data
        raise AttributeError(f"'LazyLoad' object has no attribute '{attr}'")

# 测试
lazy = LazyLoad()
print(lazy.data)  # 输出:开始加载data... → [1,2,3,4,5]
print(lazy.data)  # 输出:[1,2,3,4,5] → 直接返回,不再加载

7.3 不可变对象(用__setattr__/__delattr__

class Immutable:
    def __init__(self, name, age):
        self.__dict__["name"] = name
        self.__dict__["age"] = age

    def __setattr__(self, attr, value):
        raise AttributeError("不可变对象,不能修改属性")

    def __delattr__(self, attr):
        raise AttributeError("不可变对象,不能删除属性")

# 测试
immutable = Immutable("张三", 20)
print(immutable.name)  # 输出:张三
# immutable.age = 21  # 输出:AttributeError: 不可变对象,不能修改属性
# del immutable.name  # 输出:AttributeError: 不可变对象,不能删除属性

八、50000 字总结

8.1 魔术方法的分类

分类主要方法
对象生命周期__new__/__init__/__del__
对象表示__str__/__repr__/__format__
属性访问__getattr__/__setattr__/__delattr__/__getattribute__/__slots__
运算__add__/__sub__/__mul__/__div__/__eq__/__lt__/__le__
容器与迭代__len__/__getitem__/__setitem__/__delitem__/__iter__/__next__
上下文管理__enter__/__exit__
其他高级__call__/__hash__/__instancecheck__/__subclasscheck__

8.2 核心原则

  1. 不要滥用魔术方法:只在需要重写 Python 默认行为时使用;
  2. 遵循 Pythonic 原则:写出符合 Python 风格的代码;
  3. 保持向后兼容:确保魔术方法的实现与 Python 的版本兼容;
  4. 文档化:为魔术方法添加文档字符串,说明用途和参数;
  5. 测试:为魔术方法编写测试用例,确保功能正确。

8.3 工程化建议

  • 单例模式:用__new__实现;
  • 资源管理:用上下文管理器(__enter__/__exit__)实现;
  • 属性控制:用__getattr__/__setattr__实现惰性加载、属性验证;
  • 自定义容器:用__len__/__getitem__实现;
  • 对象表示:同时定义__str____repr__

九、结语

魔术方法是 Python 最强大的特性之一,它让你可以深入 Python 的底层,重写默认行为,写出优雅的代码。但要注意:魔术方法不是 “魔法”,它只是 Python 解释器自动调用的普通方法—— 只有理解了它的原理和用法,才能真正发挥它的作用。

希望这篇 15000 字的指南能帮助你从 “知道” 魔术方法到 “熟练使用” 魔术方法,写出真正 “Pythonic” 的代码。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值