You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python OOP中self引用属性与直接访问类属性的区别疑问

Python类属性两种访问写法的差异说明

首先是你提到的示例代码:

from random import randrange

class Pet():
    hunger_threshold = 10
    
    def __init__(self, name = "Kitty"):
        self.name = name
        self.hunger = randrange(self.hunger_threshold)

两种写法的核心差异

你提到的两行初始化代码分别是:

  1. self.hunger = randrange(self.hunger_threshold)
  2. self.hunger = randrange(hunger_threshold)

两者的取值逻辑完全不同:

  • 第一行通过实例self查找属性:Python会先检查当前实例自身有没有绑定hunger_threshold属性,如果实例没有单独定义这个属性,才会顺着类继承链去类、父类上找同名类属性。如果后续你给单个实例单独赋值了同名属性,比如执行my_pet.hunger_threshold = 20,那这个实例初始化时取到的阈值就是20,会覆盖类上定义的默认值10。
  • 第二行直接通过作用域查找变量名:它找的是类定义阶段就存在于类作用域里的hunger_threshold变量,也就是固定绑定在类上的默认值10,完全不受实例后续单独绑定同名属性的影响。

可以用一段简单的测试代码直观看到差异:

from random import randrange

# 测试self.xxx的写法
class PetWithSelf:
    hunger_threshold = 10
    def __init__(self):
        # 给实例单独绑定同名属性
        self.hunger_threshold = 20
        self.hunger = randrange(self.hunger_threshold)

p1 = PetWithSelf()
print(p1.hunger) # 随机值范围是0~19,取到的是实例自身绑定的20

# 测试直接写变量名的写法
class PetDirect:
    hunger_threshold = 10
    def __init__(self):
        self.hunger_threshold = 20
        self.hunger = randrange(hunger_threshold)

p2 = PetDirect()
print(p2.hunger) # 随机值范围是0~9,取到的是类作用域里的默认值10

为什么第二种写法不需要提前给实例绑定属性就能运行

这是Python的作用域查找规则决定的:
类体代码在类定义执行时,会运行在一个临时的类作用域中,类内部定义的方法属于嵌套在这个类作用域里的函数。当方法内部引用一个没有在方法局部赋值的名字时,Python会按照局部作用域→外层嵌套作用域(包含类定义时的临时作用域)→全局作用域→内置作用域的顺序查找对应名字。
hunger_threshold = 10是直接定义在类体里的变量,天然属于类作用域下的名字,__init__方法作为嵌套函数可以直接顺着作用域链找到这个变量,根本不需要通过实例属性查找,自然不需要提前执行self.hunger_threshold = xxx给实例绑定属性。

补充一个容易踩的坑:类作用域的嵌套查找仅对普通函数生效,类内部的列表推导、生成器表达式会生成独立的作用域,这类场景下直接写hunger_threshold会触发找不到名字的报错,必须用self.hunger_threshold或者类名.hunger_threshold的方式访问。

内容的提问来源于stack exchange,提问作者Yokanishaa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:45:07