Python中self和__init__的本质:对象内存初始化机制
2026/9/15 13:15:47 网站建设 项目流程

1. 这不是语法糖,是Python面向对象的“呼吸节奏”

你刚写完第一行class Person:,手指悬在键盘上,盯着光标发呆——接下来该写什么?def __init__(self):?还是直接写个def say_hello()?网上搜“Python class怎么用”,跳出来的教程要么一上来就甩出二十行带注释的代码,要么只告诉你“__init__是构造函数,self是必须写的第一个参数”,然后戛然而止。结果你照着抄了一遍,运行时却报错TypeError: __init__() missing 1 required positional argument: 'self',或者更迷惑的是:为什么我明明没传self,它自己就出现了?为什么self.name = name能把变量绑到对象上,而name = name却只是在函数里造了个临时变量?

这根本不是“记住规则”的问题。__init__self和实例属性,三者共同构成了Python类最底层的对象生命周期启动机制——它不是语法装饰,而是Python解释器在内存中为每个新对象分配身份、建立归属关系、初始化状态的强制性握手协议。你写的每一行self.xxx = yyy,本质上都是在对一块刚申请出来的内存区域下达指令:“这块地址归这个对象管,现在把值 yyy 存进去,并且起名叫 xxx”。而self就是这块内存区域的“身份证号”,是Python自动塞给你的唯一入口凭证。

我带过几十个零基础转行的学员,90%的人卡在第二周,不是因为不会写循环,而是因为没真正理解self的存在逻辑。他们反复修改代码,把self.name改成name,再改回self.name,像在调试一个黑盒开关。直到某天,我让他们关掉编辑器,拿出一张纸,画出两行代码执行时内存里到底发生了什么:

p1 = Person("Alice") p2 = Person("Bob")

p1 = Person("Alice")执行时,Python 做了三件事:

  1. 在堆内存里划出一块空白区域(比如地址0x7f8a1234),这是p1的专属领地;
  2. 自动调用Person.__init__(),并悄悄把0x7f8a1234这个地址作为第一个参数传进去,这个参数的名字,就叫self
  3. __init__函数体内,self.name = "Alice"这句,等价于“去地址0x7f8a1234那块内存里,建一个叫name的标签,把字符串"Alice"的引用存进去”。

所以self不是你“要写”的东西,它是Python“已经给你准备好”的东西。你不写self,解释器就不知道该把"Alice"存到哪块内存里;你写了self却不传值,解释器就找不到那块内存的地址。它不是约定,是铁律;不是技巧,是基础设施。这篇文章不教你怎么背语法,而是带你亲手拆开__init__的外壳,看清self如何在内存中穿针引线,让nameageis_active这些变量真正成为“属于这个对象”的实例属性——而不是飘在函数栈帧里的幻影。

2. 核心设计逻辑:为什么非得是__init__+self这套组合?

2.1__init__不是“构造函数”,而是“实例初始化器”

很多从Java或C++转过来的开发者,第一反应就是:“哦,__init__就是构造函数”。这个类比看似省事,实则埋下巨大隐患。Java的构造函数负责创建对象并返回引用,而Python的__init__完全不负责创建对象。真正的对象创建,发生在__new__方法里——它才是那个在内存中切出一块空白区域、返回地址的“基建工人”。__init__只是紧随其后进场的“装修队长”,它的唯一任务,就是拿着__new__给它的地址(即self),往这块毛坯房里添置家具、粉刷墙壁、接通水电。

你可以用一个生活化类比来理解:

  • __new__是房产公司,批地、打桩、浇筑混凝土,造出一栋空壳楼(对象实例);
  • __init__是物业管家,拿到这栋楼的产权证(self),开始登记住户信息(self.name = name)、安装门禁系统(self.is_registered = True)、配置水电表(self.balance = 0.0);
  • 如果你跳过管家,直接让住户自己去贴门牌、装电表,就会出现混乱——这就是不写__init__或乱写self导致属性丢失的根本原因。

所以__init__的存在,本质是解耦对象创建与状态初始化。Python强制你把“造房子”和“搞装修”分开,确保每次新建对象,都有一套标准化的初始化流程。这也是为什么你不能在__init__return一个新对象——它没有返回值,它的使命就是“装修”,不是“再造一栋”。

2.2self是隐式传递的“对象身份证”,不是可选参数

self的名字可以改成thisme甚至banana,但它的位置和角色不可动摇:它必须是实例方法的第一个参数,且由Python自动传入。这不是Python的“人性化设计”,而是CPython解释器的调用约定(calling convention)。当你写下:

p1 = Person("Alice") p1.say_hello()

第二行实际发生的,是解释器在背后执行了Person.say_hello(p1)p1这个实例对象,被当作第一个参数,原封不动地塞进了say_hello的括号里。如果你的方法定义漏掉了self

class Person: def say_hello(): # ❌ 错误:没有 self 参数 print("Hello!")

那么p1.say_hello()就等价于Person.say_hello(p1),但函数签名声明它不需要任何参数,于是立刻抛出TypeError。这就像你去银行柜台办业务,柜员(函数)说“请出示身份证”,你(调用者)乖乖递上身份证(p1),结果柜员却说“我不收身份证,你走错窗口了”。

更关键的是,self携带了对象的全部身份信息。它不只是一个地址,更是通往该对象所有实例属性和方法的唯一通道。self.name能访问到"Alice",是因为self知道自己是谁,知道自己的内存布局,知道name这个键对应哪块数据。如果你在方法里写name = "Charlie",那只是在当前函数的局部作用域里造了一个同名变量,跟self管的那块内存毫无关系——它既不会覆盖"Alice",也不会被其他方法看到。

2.3 实例属性的本质:绑定在self上的动态字典项

Python中,每个实例对象内部都维护着一个名为__dict__的字典,它就是实例属性的物理存储容器。当你执行self.name = "Alice",Python做的不是“声明一个变量”,而是执行self.__dict__['name'] = "Alice"。你可以随时验证:

class Person: def __init__(self, name): self.name = name self.age = 25 p = Person("Alice") print(p.__dict__) # {'name': 'Alice', 'age': 25}

这个__dict__是动态的、可修改的。你可以在__init__之外随时添加新属性:

p.height = 165 # 动态添加 print(p.__dict__) # {'name': 'Alice', 'age': 25, 'height': 165}

但注意:这种动态性是一把双刃剑。如果某个属性本该在__init__中初始化,却被遗漏,它就永远不会出现在__dict__里,后续访问会直接AttributeError。比如:

class Person: def __init__(self, name): self.name = name # 忘记初始化 age! p = Person("Alice") print(p.age) # AttributeError: 'Person' object has no attribute 'age'

这里没有“默认值”,没有“空指针”,只有赤裸裸的缺失。Python不会替你补全,它只忠实地执行你写的每一条self.xxx = yyy。所以__init__的核心价值,就是强制你在对象诞生的第一刻,就明确声明“这个对象必须拥有哪些属性”,把__dict__的初始结构定下来,避免后期因属性缺失导致的不可预测错误。

3. 实操细节解析:从零写出健壮的__init__与实例属性

3.1__init__的标准结构与参数设计原则

一个生产环境可用的__init__,绝不是简单罗列self.xxx = xxx。它需要遵循三个硬性原则:必填校验、类型约束、默认兜底。我们以一个真实的BankAccount类为例,逐行拆解:

class BankAccount: def __init__(self, account_number: str, initial_balance: float = 0.0, owner_name: str = ""): # 1. 必填校验:account_number 不能为空字符串 if not isinstance(account_number, str) or not account_number.strip(): raise ValueError("account_number must be a non-empty string") # 2. 类型约束:initial_balance 必须是数字且非负 if not isinstance(initial_balance, (int, float)) or initial_balance < 0: raise ValueError("initial_balance must be a non-negative number") # 3. 默认兜底:owner_name 若为空,则设为 'Anonymous' self.owner_name = owner_name.strip() if owner_name.strip() else "Anonymous" # 4. 安全赋值:所有校验通过后,才写入实例属性 self.account_number = account_number.strip() self._balance = initial_balance # 使用下划线前缀表示“受保护”属性 self._transaction_history = [] # 私有属性,不希望外部直接修改

这段代码里藏着五个关键实操要点:

  1. 参数类型提示(Type Hints)不是摆设account_number: str告诉IDE和团队成员“这个参数预期是什么类型”,配合isinstance()运行时校验,形成双重保险。别信“Python是动态语言所以不用管类型”,线上崩溃90%源于类型错配。

  2. strip()是字符串初始化的黄金搭档:用户输入的账号" 12345 ""12345"是两个不同的字符串,但业务上它们应等价。strip()清除首尾空格,是处理用户输入的标配操作,必须在__init__里第一时间做,而不是等到业务逻辑里再判断。

  3. _balance_transaction_history的下划线前缀:这不是Python的强制访问控制(Python没有private关键字),而是一种约定俗成的信号,告诉其他开发者:“请不要直接读写这些属性,应该通过deposit()withdraw()等方法操作”。它降低了误用风险,也为你后续加日志、审计、权限控制留出接口。

  4. initial_balance设为默认参数= 0.0:这解决了“新账户余额为0”这一高频场景,调用者可以只传account_number,代码更简洁:BankAccount("ACC001")。但注意,默认值必须是不可变对象(如0.0,"",None),绝不能是[]{},否则所有实例会共享同一个列表/字典,引发灾难性bug。

  5. 校验与赋值严格分离:先完成所有输入校验(1-3步),确认无误后再执行self.xxx = yyy(第4步)。这样能保证:只要__init__成功返回,对象就处于一个完全合法、可预测的状态。哪怕校验逻辑很复杂,也要把它放在赋值之前。

提示:永远不要在__init__里调用可能失败的外部服务(如数据库连接、HTTP请求)。__init__应该是纯内存操作,快如闪电。耗时或可能失败的操作,应封装在独立的load_from_db()fetch_data()方法里,由调用者显式触发。

3.2 实例属性的生命周期管理:何时该用@property

self._balance是一个典型的“需要管控访问”的实例属性。直接暴露account._balance = -1000会导致账户余额为负,业务逻辑崩坏。此时,@property就是你的安全阀:

class BankAccount: def __init__(self, account_number, initial_balance=0.0): # ... 初始化代码(同上) self._balance = initial_balance @property def balance(self) -> float: """只读属性:返回当前余额""" return self._balance @balance.setter def balance(self, value: float): """写入管控:禁止直接设置余额""" raise AttributeError("Cannot directly set balance. Use deposit() or withdraw().") def deposit(self, amount: float) -> None: if amount <= 0: raise ValueError("Deposit amount must be positive") self._balance += amount def withdraw(self, amount: float) -> None: if amount <= 0: raise ValueError("Withdrawal amount must be positive") if amount > self._balance: raise ValueError("Insufficient funds") self._balance -= amount

@property的威力在于:它把一个方法伪装成了一个属性。调用者写account.balance,感觉是在读一个普通变量;而你作为作者,却能在背后执行任意逻辑(校验、计算、日志)。上面的例子中,balance属性是只读的,任何试图account.balance = 100的操作都会触发setter抛出异常,强制用户走deposit()/withdraw()的正道。

@property不是万能膏药。我见过太多新手滥用它,把所有属性都包一层:

# ❌ 反模式:过度包装,徒增复杂度 @property def account_number(self): return self._account_number @account_number.setter def account_number(self, value): self._account_number = value.strip()

这完全没有必要。account_number是一个简单的、无需业务逻辑干预的标识符,直接暴露self.account_number更清晰、更高效。@property应该只用于以下场景:

  • 需要读写管控(如余额、密码、状态);
  • 需要惰性计算(如full_namefirst_namelast_name拼接,但拼接开销大,只在首次访问时计算);
  • 需要兼容旧接口(如老代码依赖obj.size,新需求要求size从数据库实时查,用@property无缝替换)。

3.3 处理可变默认参数:列表、字典、集合的致命陷阱

这是Python初学者踩坑率最高的知识点之一。看这个经典反例:

# ❌ 危险!所有实例共享同一个列表 class BadLogger: def __init__(self, messages=[]): # ❌ 列表是可变对象,作为默认参数 self.messages = messages logger1 = BadLogger() logger2 = BadLogger() logger1.messages.append("Error 1") print(logger2.messages) # ['Error 1'] —— 天啊,logger2 也收到了!

原因在于:Python函数的默认参数在函数定义时就被创建并缓存,而不是在每次调用时重新生成。messages=[]这个空列表,在BadLogger类定义时就被创建了一次,之后所有不传messages的调用,都复用这个“同一个”列表对象。

正确解法只有一种:用None作为默认值,然后在函数体内手动创建新对象:

# ✅ 正确:每次调用都创建新列表 class GoodLogger: def __init__(self, messages=None): self.messages = messages if messages is not None else [] logger1 = GoodLogger() logger2 = GoodLogger() logger1.messages.append("Error 1") print(logger2.messages) # [] —— 干净,隔离,符合直觉

这个原则适用于所有可变对象:list,dict,set,bytearray。记住口诀:“可变默认参数是毒药,一律用 None 代,体内再 new”。我在Code Review中,只要看到def __init__(self, items=[]):,立刻打回重写,没有商量余地。

3.4 实例属性与类属性的边界:什么时候该用cls.xxx

self.xxx是实例属性,每个对象一份;ClassName.xxxcls.xxx(在类方法中)是类属性,所有实例共享。混淆二者会导致诡异bug。看这个例子:

class Counter: count = 0 # ❌ 类属性,所有实例共享! def __init__(self, name): self.name = name self.count += 1 # ❌ 这里出错了! c1 = Counter("A") c2 = Counter("B") print(c1.count) # 0 print(c2.count) # 0 print(Counter.count) # 0 —— 全是0?怎么回事?

问题出在self.count += 1。这行代码实际执行了两步:

  1. self.count查找:先在c1.__dict__里找count,没找到;
  2. 向上查找类属性:找到Counter.count,值为0
  3. 计算0 + 1 = 1
  4. 赋值给self.count:这一步会在c1.__dict__里创建一个全新的count键,值为1,从此c1.count就和Counter.count再无关系。

所以c1.count1(实例属性),c2.count1(另一个实例属性),而Counter.count还是0(类属性)。这不是你想要的计数器。

正确做法是明确区分用途

  • 如果是每个实例独有的状态(如姓名、余额、配置),用self.xxx
  • 如果是所有实例共享的常量或全局状态(如API base_url、数据库连接池、版本号),用类属性ClassName.xxx,并在__init__只读访问,绝不赋值;
  • 如果是需要跨实例共享的可变状态(如总请求数),必须用类属性,并在类方法中显式操作:
class Counter: _total_count = 0 # ✅ 类属性,命名以下划线开头表示“受保护” def __init__(self, name): self.name = name # ✅ 在类方法中操作类属性 Counter._total_count += 1 @classmethod def get_total_count(cls): return cls._total_count c1 = Counter("A") c2 = Counter("B") print(Counter.get_total_count()) # 2

4. 完整实操:从零构建一个生产级Student

现在,我们把前面所有要点融合,动手写一个真实可用的Student类。它要管理学生基本信息、课程成绩、GPA计算,并具备完整的错误防护。这不是玩具代码,而是你能直接拷贝进项目里用的工业级实现。

4.1 需求分析与核心设计决策

我们要支持的场景:

  • 创建学生:必须提供学号(唯一ID)、姓名;
  • 选课:添加课程名和成绩(0-100分);
  • 查询:获取所有课程、计算GPA(平均分);
  • 保护:学号不可修改,成绩需校验,课程名去重。

设计决策:

  • student_idname是核心标识,必须在__init__中强校验并设为只读属性;
  • courses是可变数据,用dict存储{course_name: score},避免重复课程;
  • GPA 计算是派生值,用@property实现惰性计算,避免冗余存储;
  • 所有外部输入(课程名、成绩)必须经过strip()和范围校验。

4.2 代码实现与逐行注释

from typing import Dict, Optional, List class Student: """ 学生信息管理类 支持学号、姓名、课程成绩的增删查,以及GPA计算。 所有输入均进行严格校验,确保数据一致性。 """ def __init__(self, student_id: str, name: str, courses: Optional[Dict[str, float]] = None): """ 初始化学生实例 Args: student_id: 学号,非空字符串,创建后不可修改 name: 姓名,非空字符串 courses: 初始课程字典,格式为 {课程名: 成绩},成绩为0-100的浮点数 """ # 1. 学号校验:非空、去空格、长度合理(假设4-12位) if not isinstance(student_id, str) or not student_id.strip(): raise ValueError("student_id must be a non-empty string") self._student_id = student_id.strip() if len(self._student_id) < 4 or len(self._student_id) > 12: raise ValueError("student_id length must be between 4 and 12 characters") # 2. 姓名校验:非空、去空格 if not isinstance(name, str) or not name.strip(): raise ValueError("name must be a non-empty string") self._name = name.strip() # 3. 课程字典初始化:使用 None 作为默认值,避免可变默认参数陷阱 self._courses: Dict[str, float] = courses.copy() if courses else {} # 4. 对初始课程进行校验(复用 add_course 逻辑) for course_name, score in self._courses.items(): self._validate_course_input(course_name, score) # 4.1 只读属性:student_id 和 name @property def student_id(self) -> str: """学号只读属性""" return self._student_id @property def name(self) -> str: """姓名只读属性""" return self._name # 4.2 课程管理方法 def add_course(self, course_name: str, score: float) -> None: """ 添加一门课程及成绩 Args: course_name: 课程名称,非空字符串 score: 成绩,0.0-100.0之间的浮点数 Raises: ValueError: 当课程名为空或成绩超出范围时 """ self._validate_course_input(course_name, score) # 课程名去重:统一转为小写+去空格,避免 "Math" 和 "math " 被视为不同课程 normalized_name = course_name.strip().lower() self._courses[normalized_name] = score def remove_course(self, course_name: str) -> bool: """ 删除一门课程 Args: course_name: 课程名称 Returns: bool: True 表示删除成功,False 表示课程不存在 """ normalized_name = course_name.strip().lower() if normalized_name in self._courses: del self._courses[normalized_name] return True return False def get_course_score(self, course_name: str) -> Optional[float]: """ 获取指定课程的成绩 Args: course_name: 课程名称 Returns: float or None: 成绩或 None(如果课程不存在) """ normalized_name = course_name.strip().lower() return self._courses.get(normalized_name) # 4.3 GPA 计算:惰性、只读属性 @property def gpa(self) -> float: """ 计算并返回GPA(平均分) 惰性计算:仅在首次访问时计算,结果不缓存(因课程可能动态变化) """ if not self._courses: return 0.0 return sum(self._courses.values()) / len(self._courses) # 4.4 辅助方法:课程输入校验(DRY原则) def _validate_course_input(self, course_name: str, score: float) -> None: """私有方法:校验单个课程输入""" if not isinstance(course_name, str) or not course_name.strip(): raise ValueError("course_name must be a non-empty string") if not isinstance(score, (int, float)) or score < 0.0 or score > 100.0: raise ValueError("score must be a number between 0.0 and 100.0") # 4.5 字符串表示:方便调试和日志 def __str__(self) -> str: courses_str = ", ".join([f"{k}: {v}" for k, v in self._courses.items()]) return f"Student(id='{self.student_id}', name='{self.name}', courses={{{courses_str}}})" def __repr__(self) -> str: return f"Student('{self.student_id}', '{self.name}', {self._courses})"

4.3 实操验证与现场记录

让我们用几行代码,验证这个类是否真的健壮:

# 场景1:正常创建 s1 = Student("S2023001", "Zhang San") print(s1) # Student(id='S2023001', name='Zhang San', courses={}) # 场景2:添加课程 s1.add_course("Mathematics", 95.5) s1.add_course("Physics", 88.0) s1.add_course("Chemistry", 92.0) print(s1.gpa) # 91.83333333333333 # 场景3:课程名大小写不敏感 s1.add_course("mathematics", 96.0) # 覆盖之前的Mathematics成绩 print(s1.get_course_score("MATHEMATICS")) # 96.0 # 场景4:错误输入捕获 try: s1.add_course("", 85.0) # 空课程名 except ValueError as e: print(f"Caught: {e}") # Caught: course_name must be a non-empty string try: s1.add_course("Biology", 105.0) # 超分 except ValueError as e: print(f"Caught: {e}") # Caught: score must be a number between 0.0 and 100.0 # 场景5:学号不可修改(尝试失败) try: s1.student_id = "S2023002" # AttributeError except AttributeError as e: print(f"Caught: {e}") # Caught: can't set attribute # 场景6:查看内部状态 print(s1.__dict__) # {'_student_id': 'S2023001', '_name': 'Zhang San', '_courses': {'mathematics': 96.0, 'physics': 88.0, 'chemistry': 92.0}}

所有测试全部通过。这个Student类做到了:

  • 强校验:每一个输入点都有isinstance和范围检查;
  • 防误用student_idname是只读属性,_courses是私有字典;
  • 健壮性:课程名标准化处理,避免大小写、空格导致的重复;
  • 可维护性_validate_course_input方法复用,逻辑集中;
  • 可读性__str____repr__让调试一目了然。

5. 常见问题排查与独家避坑指南

5.1 “AttributeError: 'XXX' object has no attribute 'yyy'” —— 最高频错误

现象:运行时报错,说某个实例没有你确信写过的属性。

排查思路(按优先级排序):

  1. 检查__init__是否真的执行了self.yyy = value:最常见的原因是if条件没满足,导致赋值语句被跳过。例如:

    def __init__(self, is_vip): if is_vip: self.discount_rate = 0.1 # 普通用户进不来这里! self.name = "Default" # 这行总会执行

    创建Student(False)时,discount_rate根本没被创建。

  2. 检查拼写错误self.user_namevsself.username,Python区分大小写。

  3. 检查作用域:是否在iffor块内定义了yyy,但没在self上绑定?例如:

    def __init__(self): for i in range(3): temp_list = [1,2,3] # 这是局部变量! # temp_list 在这里已消失,self.temp_list 从未被创建
  4. 检查__init__是否被子类覆盖且未调用super().__init__()

    class VIPStudent(Student): def __init__(self, student_id, name, vip_level): # ❌ 忘记调用父类 __init__,导致 student_id/name 未初始化! self.vip_level = vip_level

我的实操心得:遇到这个错误,第一反应不是改代码,而是打印obj.__dict__。它会像X光一样,照出这个对象此刻到底有哪些属性。如果yyy不在字典里,说明__init__没给它分配内存;如果在,说明是拼写或访问路径错了。

5.2 “UnboundLocalError: local variable 'xxx' referenced before assignment”

现象:在一个方法里,变量xxx有时能用,有时报错说“在赋值前被引用”。

根本原因:Python的变量作用域规则。当你在函数内对一个变量赋值xxx = 1),Python就认定这个变量是局部变量。即使赋值语句在if里,整个函数体内的xxx都被视为局部变量。如果if条件为Falsexxx就没被赋值,但你又试图print(xxx),就报错。

经典案例

def process_data(self, data): if data: result = self._calculate(data) # result 是局部变量 return result # ❌ 如果 data 为 False,result 未定义!

解决方案永远在函数开头,为所有可能用到的局部变量赋予初始值

def process_data(self, data): result = None # ✅ 明确初始化 if data: result = self._calculate(data) return result

注意:这和实例属性self.xxx无关。self.xxx是对象属性,存在于obj.__dict__;而xxx是函数局部变量,存在于栈帧里。两者生命周期完全不同。

5.3 “NameError: name 'self' is not defined” —— 新手地狱

现象:在方法里写self.name,却报错说self未定义。

原因只有一个:你忘了在方法定义时,把self作为第一个参数写进去。

class BadClass: def my_method(): # ❌ 缺少 self! print(self.name) # NameError: name 'self' is not defined

为什么IDE不报错?因为self只是一个参数名,Python不强制叫self。你可以写def my_method(me): print(me.name),它完全合法。所以IDE无法判断你“本意”是不是要写self

我的避坑技巧:在PyCharm或VS Code里,开启“Python Linter”(如pylint或flake8),它们会检测my_method()这种缺少self的实例方法,并给出E0211W0211警告。把这个警告级别设为error,就能在写代码时立刻发现。

5.4 性能陷阱:__init__里做了不该做的事

__init__应该是轻量级的。但我见过太多人在里面塞了重操作:

# ❌ 危险:__init__ 变成慢操作 def __init__(self, user_id): self.user_id = user_id # 下面三行让 __init__ 变成网络IO密集型! self.user_profile = requests.get(f"https://api/user/{user_id}").json() self.permissions = self._load_permissions_from_db() self.recent_activities = self._fetch_last_10_activities()

后果

  • 创建对象变慢,影响性能;
  • __init__可能因网络超时、数据库连接失败而抛异常,破坏对象创建的原子性;
  • 单元测试困难,需要mock所有外部依赖。

正确姿势__init__只做内存初始化。把耗时操作拆成独立方法,由调用者按需触发:

# ✅ 正确:__init__ 保持轻量 def __init__(self, user_id): self.user_id = user_id self._user_profile = None # 延迟加载 self._permissions = None self._recent_activities = None def load_profile(self): if self._user_profile is None: self._user_profile = requests.get(...).json() return self._user_profile

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询