ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python对象构造:深入理解__new__与__init__的区别与应用场景

Python对象构造:深入理解__new__与__init__的区别与应用场景 1. 从“人狗大作战”到对象工厂为什么你需要理解__new__和__init__最近在社区里看到不少关于“人狗大作战Python代码”的讨论挺有意思的。这类小游戏的核心其实就是创建一堆“人”和“狗”的对象然后让它们互动。很多新手在写这类代码时会直接定义一个Person或Dog类然后在__init__里初始化属性比如血量、攻击力。这没错但如果你想让你的“狗”在创建时就自带一个随机生成的、独一无二的名字或者想让你的“人”对象在创建时自动注册到一个全局的“人口普查”列表中仅仅靠__init__可能就有点力不从心了。这时候另一个更底层、更强大的方法__new__就该登场了。我见过不少朋友在配置 VSCode Python 环境或者安装各种包比如为了跑通某个深度学习工作流而安装缺失的节点时被各种依赖和版本搞得焦头烂额。其实理解 Python 对象从无到有的完整生命周期就像是理解了整个 Python 宇宙的“创世法则”。它能帮你从根本上厘清很多看似玄学的问题比如为什么有些库的类不能直接用MyClass()实例化为什么单例模式要那么写甚至能帮你更好地调试arcgis pro里“无法创建表”这类底层对象创建失败的错误。简单来说__init__是你最熟悉的“装修工”对象这个“毛坯房”已经建好了它负责往里面摆家具、刷油漆。而__new__则是那个更早介入的“建筑师”和“施工队”它负责申请地皮、打地基、把房子的框架也就是对象实例本身给建造出来。绝大多数情况下你用不到__new__Python 默认的“施工队”会帮你搞定一切。但当你需要定制这个“建造过程”本身时比如你想用同一块地皮内存反复盖房子单例或者你想在房子动工前先检查一下施工许可证参数校验甚至你想盖一个完全不一样的房子返回其他类的实例你就必须请__new__这位“建筑师”出马了。这篇文章我们就来彻底拆解这对“黄金搭档”。我不会只停留在“__new__先执行__init__后执行”这种表面结论而是要带你深入到 CPython 的层面看看一个对象究竟是如何从无到有被“构造”出来的。我们会用大量的代码示例从最简单的场景到实现单例模式、实现不可变对象、进行对象缓存等高级用法让你不仅知道怎么用更明白为什么要这么用以及用的时候有哪些“坑”在等着你。理解了它们你再看那些复杂的第三方库源码或者设计自己的高级数据结构时思路会清晰得多。2. 对象诞生的完整流程比你想的更早一步当我们写下obj MyClass(arg1, arg2)这行看似简单的代码时Python 解释器在幕后执行了一个精密的多步舞蹈。很多人以为__init__是起点其实大错特错。让我们把这个过程慢放一步步拆解。2.1 第一步__new__的召唤与职责首先MyClass(arg1, arg2)这个表达式实际上触发的是对MyClass的__call__方法的调用。没错类本身也是一个可调用对象callable这得益于它元类通常是type中定义的__call__方法。这个__call__方法内部严格遵循着一个固定的工作流程调用__new____call__首先会调用类的__new__(cls, *args, **kwargs)方法。这里的cls参数就是MyClass本身。__new__是一个静态方法虽然你用staticmethod装饰它也没问题但通常不这么写它的核心职责是分配内存并返回一个新的、未初始化的对象实例。它必须返回一个对象通常是cls类型的实例。类型检查如果__new__成功返回了一个对象并且这个对象的类型确实是cls或者是cls的子类那么流程继续。如果__new__返回的不是一个对象或者对象类型不匹配那么整个实例化过程就会抛出TypeError。调用__init__只有在上述检查通过后__call__才会去调用__init__方法。注意__init__的self参数就是__new__返回的那个对象。__init__的职责是对这个已经存在的对象进行初始化设置其初始状态属性。__init__不需要返回任何值它的返回值会被直接忽略。关键点在于__init__调用时self已经是一个完整的对象了。你可以把它想象成__new__造出了一辆汽车的车架、发动机和底盘对象实例然后__init__负责给这辆车装上座椅、喷上漆、加满油初始化属性。2.2 默认的__new__从哪里来如果你没有在自己的类中定义__new__方法Python 会去哪里找呢它会沿着类的继承链向上查找最终找到object基类中的__new__。object.__new__是一个用 C 语言实现的底层函数它的工作就是向操作系统申请一块内存用来存放这个新对象。这块内存的大小在类被定义时通过元类type就已经根据类属性、方法等信息计算好了。我们可以用一个简单的例子来验证这个顺序class MyClass: def __new__(cls, *args, **kwargs): print(f__new__ 被调用参数 args: {args}, kwargs: {kwargs}) # 必须调用父类的 __new__ 来创建实例 instance super().__new__(cls) print(f__new__ 创建了实例: {instance}) return instance def __init__(self, value): print(f__init__ 被调用self: {self}, value: {value}) self.value value obj MyClass(42)输出会是__new__ 被调用参数 args: (42,), kwargs: {} __new__ 创建了实例: __main__.MyClass object at 0x... __init__ 被调用self: __main__.MyClass object at 0x..., value: 42清晰可见__new__先执行并且接收到了所有传参(42,)。它创建并返回了实例然后这个实例被传递给了__init__。2.3 一个常见的误解在__init__中创建“实例”这是新手常犯的错误。__init__的self参数就是实例本身你无法在__init__内部再去“创建”这个实例。你只能在__init__里操作它。试图在__init__里写self SomeOtherObject()是徒劳的这只会改变局部变量self的引用而不会影响外部实际得到的对象。真正决定返回哪个对象的是__new__。注意正因为__new__先于__init__执行所以如果你在__new__中返回了其他类的实例那么__init__可能根本不会被调用我们会在后面的高级用法中看到这一点。3.__new__的实战超越初始化的对象控制理解了基本流程我们来看看__new__在哪些场景下能大显身手。这些场景的共同点是我们需要干预或定制“对象创建”这个根本行为。3.1 实现经典的单例模式单例模式确保一个类只有一个实例。用__new__来实现是最直接、最Pythonic的方式之一。class Singleton: _instance None # 类变量用于存储唯一的实例 def __new__(cls, *args, **kwargs): # 如果实例不存在则创建 if cls._instance is None: print(创建新的 Singleton 实例) # 调用父类的 __new__ 创建实例 cls._instance super().__new__(cls) else: print(返回已存在的 Singleton 实例) # 无论是否新建都返回 _instance return cls._instance def __init__(self, value): # 注意对于单例每次调用 Singleton(xx) 都会触发 __init__ # 这可能导致属性被重复覆盖。 print(fSingleton __init__ 被调用设置 value: {value}) self.value value # 测试 s1 Singleton(10) # 输出创建新的 Singleton 实例 \n Singleton __init__ 被调用设置 value: 10 print(s1.value) # 输出10 s2 Singleton(20) # 输出返回已存在的 Singleton 实例 \n Singleton __init__ 被调用设置 value: 20 print(s2.value) # 输出20 print(s1 is s2) # 输出True print(s1.value) # 输出20s1的value也被改成了20这里暴露了单例模式使用__new__时的一个关键陷阱__init__每次都会被调用即使返回的是旧实例__init__也会重新初始化它这可能导致数据被意外覆盖。为了解决这个问题我们需要更精细的控制。改进版单例一种常见做法是使用一个标志位来避免重复初始化。class BetterSingleton: _instance None _initialized False # 新增初始化标志 def __new__(cls, *args, **kwargs): if cls._instance is None: print(创建新的 BetterSingleton 实例) cls._instance super().__new__(cls) return cls._instance def __init__(self, value): # 如果已经初始化过则跳过 if self._initialized: print(fBetterSingleton 已初始化跳过 __init__) return print(fBetterSingleton __init__ 被调用设置 value: {value}) self.value value self.__class__._initialized True # 注意这里修改的是类变量 b1 BetterSingleton(100) # 创建新的... \n __init__被调用... b2 BetterSingleton(200) # 返回已存在的... \n 已初始化跳过 __init__ print(b1.value, b2.value) # 输出100 100 print(b1 is b2) # 输出True这个版本保证了属性只被设置一次。但请注意_initialized是一个类变量所有实例共享。如果你需要更复杂的单例逻辑比如线程安全可能还需要引入锁机制。3.2 创建不可变对象如继承 tuple, strPython 的内置不可变类型如tuple,str,int在创建后无法修改。当你继承这些类型时由于它们是不可变的你必须在对象创建阶段即__new__中就提供最终的数据因为__init__对不可变对象无能为力。class Point2D(tuple): 一个表示二维点的不可变类继承自tuple def __new__(cls, x, y): # 我们需要在 __new__ 中创建并返回最终的 tuple 实例 # 我们返回一个包含 (x, y) 的元组实例 return super().__new__(cls, (x, y)) # 通常我们不再需要 __init__因为数据在 __new__ 中已经确定了。 # 但我们可以定义一些便利属性。 property def x(self): return self[0] property def y(self): return self[1] def __repr__(self): return fPoint2D({self.x}, {self.y}) p Point2D(3, 4) print(p) # 输出Point2D(3, 4) print(p.x, p.y) # 输出3 4 print(p[0]) # 输出3因为它本质上是个元组 # p.x 5 # 报错AttributeError: cant set attribute在这个例子中Point2D.__new__接收x和y参数然后调用tuple.__new__并传递一个已经构造好的元组(x, y)作为参数。这个返回的元组实例就是我们的Point2D对象。由于元组是不可变的后续的__init__即使我们定义了也无法修改其内容所以通常省略。3.3 实现对象缓存或池化对于一些创建成本高昂的对象比如数据库连接、网络连接、复杂计算结果的包装对象我们可以使用__new__来实现一个简单的缓存机制避免重复创建。class ExpensiveObject: 模拟一个创建成本很高的对象 _cache {} # 缓存字典键为创建参数值为实例 def __new__(cls, key): # 检查缓存 if key in cls._cache: print(f从缓存中获取 key{key} 的对象) return cls._cache[key] # 缓存未命中创建新对象 print(f创建新的 key{key} 的昂贵对象) instance super().__new__(cls) # 注意这里先创建实例放入缓存再交给 __init__ 初始化 cls._cache[key] instance return instance def __init__(self, key): # 问题来了即使是从缓存返回的旧实例__init__ 也会被调用 # 这会导致旧实例的属性被重置破坏了缓存的意义。 # 我们需要一个机制来避免重复初始化。 # 一个简单方法检查实例是否已有某个标志性属性。 if hasattr(self, _initialized_for_key) and self._initialized_for_key key: print(f对象 key{key} 已初始化跳过) return print(f初始化 key{key} 的对象) self.key key self._heavy_data self._simulate_heavy_creation(key) # 模拟耗时操作 self._initialized_for_key key # 设置初始化标志 def _simulate_heavy_creation(self, key): print(f 模拟为 key{key} 进行耗时计算或资源加载...) return f昂贵数据-{key} # 测试 obj1 ExpensiveObject(A) # 输出 # 创建新的 keyA 的昂贵对象 # 初始化 keyA 的对象 # 模拟为 keyA 进行耗时计算或资源加载... obj2 ExpensiveObject(A) # 希望从缓存获取 # 输出 # 从缓存中获取 keyA 的对象 # 初始化 keyA 的对象 -- 糟糕又被初始化了 # 模拟为 keyA 进行耗时计算或资源加载... -- 重复的昂贵操作如你所见简单的缓存遇到了和单例模式同样的问题__init__的重复调用。我们的解决思路类似通过一个与实例绑定的标志这里是_initialized_for_key来避免重复的昂贵初始化操作。这个例子清晰地展示了__new__和__init__协作时需要仔细考虑的状态管理问题。4.__init__的深度剖析不仅仅是属性赋值大多数时候我们只和__init__打交道。它的主要任务确实是初始化实例属性但深入理解其细节能让你写出更健壮、更优雅的代码。4.1__init__的签名自由与__new__的配合__init__的方法签名可以非常灵活它接收self之后的所有参数都可以自定义。但这里有一个与__new__配合的微妙之处__new__接收到的*args, **kwargs会原封不动地传递给__init__。这意味着如果你在__new__中修改了参数或者根据参数决定返回一个不同的对象那么__init__接收到的参数可能和你预期的不一样。class FlexibleInit: def __new__(cls, *args, **kwargs): print(f__new__ 收到 args: {args}, kwargs: {kwargs}) # 假设我们处理一下参数 if args and isinstance(args[0], str): # 如果第一个参数是字符串我们把它变成大写 new_args (args[0].upper(),) args[1:] # 但是我们创建实例时传递给父类 __new__ 的还是原始参数 instance super().__new__(cls) # 我们需要以某种方式把处理后的参数告诉 __init__ # 一种方法把处理后的参数存到实例的一个特殊属性里然后在 __init__ 中读取 instance._processed_args new_args instance._original_args args return instance return super().__new__(cls) def __init__(self, name, value0): # 这里接收到的 name 是原始参数 print(f__init__ 收到 name: {name}, value: {value}) # 如果我们想用处理后的参数需要检查是否有 _processed_args if hasattr(self, _processed_args): processed_name, *rest self._processed_args # 用处理后的参数初始化 self.name processed_name print(f 使用处理后的 name: {self.name}) else: self.name name self.value value obj1 FlexibleInit(hello, 100) # 输出 # __new__ 收到 args: (hello, 100), kwargs: {} # __init__ 收到 name: hello, value: 100 # 使用处理后的 name: HELLO print(obj1.name, obj1.value) # 输出HELLO 100 obj2 FlexibleInit(42, 200) # 第一个参数不是字符串 # 输出 # __new__ 收到 args: (42, 200), kwargs: {} # __init__ 收到 name: 42, value: 200 print(obj2.name, obj2.value) # 输出42 200这个例子展示了如何在__new__和__init__之间传递“处理后的状态”。这是一种高级模式通常用于参数预处理或验证。4.2 继承链中的__init__调用在继承体系中__init__的调用需要手动管理。Python 不会自动调用父类的__init__你需要使用super().__init__()。class Base: def __init__(self, base_value): print(fBase.__init__ 被调用base_value: {base_value}) self.base_value base_value class Derived(Base): def __init__(self, base_value, derived_value): # 必须先调用父类的 __init__ 来初始化父类部分 super().__init__(base_value) print(fDerived.__init__ 被调用derived_value: {derived_value}) self.derived_value derived_value d Derived(1, 2) # 输出 # Base.__init__ 被调用base_value: 1 # Derived.__init__ 被调用derived_value: 2忘记调用super().__init__()是一个常见错误会导致父类定义的属性没有被初始化。对于多重继承super()的调用顺序遵循方法解析顺序MRO这需要额外小心设计。4.3 用__init__实现简单的依赖注入或后置检查__init__是设置对象初始状态的理想场所。除了简单的赋值你还可以在这里进行参数验证确保传入的参数符合要求否则抛出ValueError或TypeError。建立对象间的关联例如将一个对象注册到全局的管理器中。执行启动逻辑比如开启一个后台线程或者建立网络连接但需谨慎因为初始化失败时清理资源可能比较麻烦。class ValidatedPerson: def __init__(self, name, age): if not isinstance(name, str) or len(name.strip()) 0: raise ValueError(姓名必须是非空字符串) if not isinstance(age, int) or age 0 or age 150: raise ValueError(年龄必须是0到150之间的整数) self.name name.strip() self.age age # 对象创建后自动注册到“人口普查”一个模拟的全局列表 self._register_to_census() def _register_to_census(self): # 假设有一个全局的人口列表 if not hasattr(ValidatedPerson, _census): ValidatedPerson._census [] ValidatedPerson._census.append(self) print(f{self.name} 已注册到人口普查。) try: p1 ValidatedPerson(Alice, 30) p2 ValidatedPerson(Bob, 25) p3 ValidatedPerson(, 40) # 这里会抛出 ValueError except ValueError as e: print(f创建失败: {e}) print(f总人口数: {len(ValidatedPerson._census)}) # 输出总人口数: 25. 高级模式与陷阱当__new__不按常理出牌__new__的强大之处在于它可以返回任意对象这开启了一些非常有趣但需要谨慎使用的可能性。5.1 返回其他类的实例工厂方法模式__new__可以返回一个完全不同的类的实例。这可以用来实现灵活的工厂模式根据输入参数决定创建哪种对象。class Dog: def bark(self): return Woof! class Cat: def meow(self): return Meow! class AnimalFactory: 根据类型字符串创建不同的动物对象 def __new__(cls, animal_type, *args, **kwargs): if animal_type dog: # 注意这里返回的是 Dog 的实例不是 AnimalFactory 的实例 # 因此 AnimalFactory.__init__ 不会被调用 return Dog(*args, **kwargs) elif animal_type cat: return Cat(*args, **kwargs) else: raise ValueError(f未知动物类型: {animal_type}) # 使用工厂 dog AnimalFactory(dog) # 实际上返回一个 Dog 实例 print(type(dog)) # 输出class __main__.Dog print(dog.bark()) # 输出Woof! cat AnimalFactory(cat) # 实际上返回一个 Cat 实例 print(cat.meow()) # 输出Meow! # 注意dog 和 cat 都不是 AnimalFactory 类型 print(isinstance(dog, AnimalFactory)) # 输出False print(isinstance(dog, Dog)) # 输出True这种模式非常强大但它也意味着AnimalFactory本身几乎不是一个“传统”的类——你无法创建AnimalFactory的实例它更像一个命名空间下的工厂函数。调用AnimalFactory()总是返回其他类的对象。5.2 与元类__call__的交互我们之前提到实例化MyClass()实际上调用的是其元类的__call__方法。元类的__call__方法内部封装了对__new__和__init__的调用。这意味着如果你自定义了元类你可以在元类的__call__方法中完全控制实例化的全过程包括修改参数、包装返回值、甚至完全绕过__new__和__init__。class TraceMeta(type): 一个追踪实例化过程的元类 def __call__(cls, *args, **kwargs): print(f[元类 TraceMeta.__call__] 开始创建 {cls.__name__} 的实例) print(f 参数: args{args}, kwargs{kwargs}) # 调用类的 __new__ 方法 instance cls.__new__(cls, *args, **kwargs) print(f __new__ 返回: {instance}) # 检查 __new__ 返回的是否是 cls 的实例 if isinstance(instance, cls): # 如果是则调用 __init__ instance.__init__(*args, **kwargs) print(f 已调用 __init__) else: print(f __new__ 返回了非 {cls.__name__} 的实例跳过 __init__) print(f[元类 TraceMeta.__call__] 返回实例: {instance}) return instance class MyTracedClass(metaclassTraceMeta): def __new__(cls, value): print(f [MyTracedClass.__new__] 被调用value{value}) return super().__new__(cls) def __init__(self, value): print(f [MyTracedClass.__init__] 被调用self{self}, value{value}) self.value value print(--- 正常实例化 ---) obj1 MyTracedClass(42) print(\n--- 返回其他实例的 __new__ ---) class WeirdClass(metaclassTraceMeta): def __new__(cls, value): print(f [WeirdClass.__new__] 被调用但返回一个字符串) return fI am a string, not a WeirdClass. Value was {value} # __init__ 不会被调用因为 __new__ 没返回 WeirdClass 实例 obj2 WeirdClass(99) print(fobj2 的类型是: {type(obj2)}, 值是: {obj2})运行这段代码你可以清晰地看到元类__call__如何协调__new__和__init__。这为我们理解 Python 对象创建的完整链条补上了最后一块拼图元类.__call__-类.__new__-可选类.__init__。5.3 陷阱忘记返回实例或返回错误类型在__new__中你必须返回一个对象。如果你忘记返回或者返回了NonePython 会得到一个None然后尝试调用__init__传入None作为self这会导致TypeError: __init__() missing 1 required positional argument: self或者AttributeError。另一个陷阱是返回了非cls类型的实例。就像我们上面看到的这会导致__init__被跳过。这有时是故意的如工厂模式但如果是无意的就会导致对象状态异常因为__init__中的初始化代码没有执行。class Problematic: def __new__(cls): print(Problematic.__new__) # 错误1忘记返回实例 # 这里没有 return 语句默认返回 None def __init__(self): print(Problematic.__init__) # 这行永远不会被执行或者会因 self 是 None 而报错 try: p Problematic() # 这里会报错 except TypeError as e: print(f错误: {e}) # 输出错误__init__() missing 1 required positional argument: self6. 性能考量与最佳实践理解了原理我们还需要关注在实际项目中如何正确、高效地使用这两个方法。6.1 何时该用__new__何时不该用使用__new__的场景你需要控制实例的创建过程如单例、对象池、缓存。你继承自不可变内置类型tuple,str,int,frozenset等必须在创建时固定数据。你需要根据参数决定返回完全不同类的对象工厂模式。你需要在对象分配内存前进行一些操作或检查虽然不常见。避免使用__new__的场景绝大多数普通的类初始化工作。请使用__init__。仅仅为了在__init__之前执行一些代码。可以考虑使用类方法classmethod作为替代的工厂方法或者将预处理逻辑放在__init__开头。如果你不确定是否需要它那很可能你不需要。滥用__new__会让代码难以理解和调试。6.2__init__与__post_init__dataclasses 的启示Python 3.7 引入的dataclasses模块提供了一个很好的实践它自动生成__init__同时提供了一个__post_init__方法。__post_init__在自动生成的__init__完成后被调用用于执行额外的初始化逻辑。这启示我们即使在自定义类中也可以遵循类似的模式来保持__init__的简洁。from dataclasses import dataclass dataclass class Person: name: str age: int def __post_init__(self): # 在自动生成的 __init__ 之后执行 if self.age 0: raise ValueError(年龄不能为负数) self.name self.name.title() # 姓名首字母大写 # 等效的手动实现简化版 class ManualPerson: def __init__(self, name: str, age: int): self.name name self.age age self._post_init() def _post_init(self): if self.age 0: raise ValueError(年龄不能为负数) self.name self.name.title() p1 Person(alice, 30) print(p1.name) # 输出Alice try: p2 Person(bob, -5) except ValueError as e: print(e) # 输出年龄不能为负数将复杂的验证和后续处理逻辑分离到_post_init这样的方法中可以使__init__更清晰也便于测试。6.3 在__new__中处理继承当你在一个继承体系中重写__new__时务必记得调用super().__new__(cls, ...)。如果你不调用父类最终是object的__new__就不会执行也就无法为你的对象分配内存。同样如果父类也有自定义的__new__逻辑你也可能破坏它。class BaseWithNew: def __new__(cls, *args, **kwargs): print(fBaseWithNew.__new__ for {cls}) # 必须传递 cls 给 super().__new__ return super().__new__(cls) # 正确 class DerivedBroken(BaseWithNew): def __new__(cls, *args, **kwargs): print(fDerivedBroken.__new__) # 错误忘记了调用 super().__new__ # 这会导致返回 None后续出错 # return None # 不能这样 class DerivedFixed(BaseWithNew): def __new__(cls, *args, **kwargs): print(fDerivedFixed.__new__) # 正确调用父类的 __new__ instance super().__new__(cls, *args, **kwargs) # 可以在返回前对 instance 做一些操作 instance._created_by DerivedFixed.__new__ return instance print(创建 DerivedFixed:) obj DerivedFixed() # 输出DerivedFixed.__new__ \n BaseWithNew.__new__ for class __main__.DerivedFixed print(obj._created_by) # 输出DerivedFixed.__new__7. 总结与个人经验回顾一下__new__和__init__是 Python 对象构造过程中两个不同阶段的主力。__new__负责“构建”分配内存返回对象__init__负责“初始化”设置初始状态。__new__是静态方法__init__是实例方法。__new__在__init__之前调用并且__new__的返回值决定了__init__是否被调用以及被调用时接收到的self是什么。在我多年的 Python 开发中真正需要自定义__new__的场景并不多大概只占 5%。但每当遇到时它都是解决问题的关键。比如在实现一个轻量级的 ORM 缓存时我用__new__拦截了模型类的实例化如果数据库中存在对应主键的记录则返回一个已存在的、属性已填充的实例否则才创建新实例并触发数据库查询。这极大地提升了复杂对象图的加载性能。另一个深刻的教训是关于__init__的副作用。早期我曾在一个__init__方法中启动一个异步任务结果发现当对象构造失败比如参数验证出错抛出异常时这个异步任务可能已经启动却无法被正确清理导致了资源泄漏。后来我遵循了“__init__只做简单的赋值和验证复杂的启动逻辑放在一个单独的.start()或__enter__方法中”的原则。最后对于初学者我的建议是先彻底掌握__init__把它用熟、用透。只有在明确遇到了__init__无法解决的问题时比如开头提到的单例、不可变对象继承、缓存再去深入研究__new__。理解它们的区别和联系能让你在阅读复杂的开源代码比如 Django 的 ModelBase、SQLAlchemy 的 DeclarativeMeta时不再感到迷茫也能让你在设计自己的高级库时拥有更强大的武器。
返回列表