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)
两种写法的核心差异
你提到的两行初始化代码分别是:
self.hunger = randrange(self.hunger_threshold)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
相关产品推荐
相关产品推荐

