类属性和实例属性的关系
- 一段代码:
class Person: name = "PersonClass" # 类属性 def __init__(self, name=""): self.name = name # 实例属性 # 创建实例 p = Person("小明") print(f"类属性:", Person.name) print(f"实例属性:", p.name)- 代码逐行解析
类的定义与类属性
class Person: name = "PersonClass" # 类属性class Person:定义了一个名为Person的类,类是创建对象(实例)的模板。name = "PersonClass"是类属性:属于整个Person类,所有基于这个类创建的实例,在没有自己的同名实例属性时,都能访问到这个值;类属性也可以直接通过类名.属性名访问。
初始化方法与实例属性
def __init__(self, name=""): self.name = name # 实例属性__init__是 Python 类的构造方法(初始化方法),创建实例时会自动执行。self代表当前创建的实例本身,是必传的第一个参数。self.name = name是实例属性:属于每个独立的实例,不同实例的这个属性可以有不同的值;实例属性必须通过实例名.属性名访问。name=""给参数设置了默认值,创建实例时如果不传name,实例属性name就为空字符串。
创建实例与打印属性
p = Person("小明") print(f"类属性:", Person.name) print(f"实例属性:", p.name)p = Person("小明")创建了Person类的一个实例p,并把"小明"传给__init__的name参数,此时实例p的name属性被赋值为"小明"。Person.name直接访问类属性,结果是"PersonClass"。p.name访问实例p的实例属性,结果是"小明"(因为实例属性会覆盖同名的类属性)。
- 执行结果
运行这段代码会输出:
类属性: PersonClass 实例属性: 小明- 总结:
- 类属性:属于类本身,通过
类名.属性名访问,是所有实例共享的; - 实例属性:属于单个实例,通过
实例名.属性名访问,每个实例的实例属性可以独立赋值; - 当实例属性和类属性同名时,访问
实例名.属性名会优先获取实例属性,类属性不受影响。
- 补充验证,加深理解:
class Person: name = "PersonClass" # 类属性 def __init__(self, name=""): self.name = name # 实例属性 # 创建实例 p = Person("小明") # 直接访问实例的 name 属性:优先取实例属性 print(p.name) # 输出:小明 # 删除实例的 name 属性后,实例会访问类属性 del p.name print(p.name) # 输出:PersonClass类方法和实例方法的关系
class Person: name = "PersonClass" # 类属性 def __init__(self, name=""): self.name = name # 实例属性 # 类方法 @classmethod def get_cls_name(cls): return cls.name # 实例方法 def get_self_name(self): return self.name # 创建实例 p = Person("小明") print(f"类方法:", Person.get_cls_name()) print(f"类方法:", p.get_cls_name()) print(f"实例方法:", p.get_self_name())- 代码解析:
1. 类方法定义与调用
# 类方法:第一个参数是cls(代表类本身),类与类的实例均可调用 @classmethod def get_cls_name(cls): return cls.name # 调用类方法 print(f"类方法:", Person.get_cls_name()) # 类直接调用:cls = Person,返回类属性 name print(f"类方法:", p.get_cls_name()) # 实例调用:cls 自动指向实例所属的类(Person),仍返回类属性- @classmethod的含义是:本方法被声明为类方法。
- 类方法的核心是
cls参数:无论用类(Person)还是实例(p)调用,cls始终指向类本身(而非实例),因此cls.name永远取的是类属性Person.name,而非实例属性p.name。
2 .实例方法定义与调用
# 实例方法:第一个参数是self(代表实例本身),只能用实例调用 def get_self_name(self): return self.name # 调用实例方法 print(f"实例方法:", p.get_self_name()) # 实例调用:self = p,返回实例属性 name(小明)- 实例方法的核心是
self参数:必须用实例(p)调用,self指向当前实例,因此self.name取的是实例自己的属性(小明),而非类属性。 - 总结
- 类属性 vs 实例属性:类属性属于类,所有实例共享;实例属性属于单个实例,优先级高于同名类属性。
- 类方法:用
@classmethod装饰,参数是cls(指向类),类 / 实例均可调用,操作的是类属性。 - 实例方法:参数是
self(指向实例),只能用实例调用,操作的是实例属性(或类属性)。
类方法与静态方法的关系
class Person: name = "PersonClass" def __init__(self, name): self.name = name # 类方法 @classmethod def class_method(cls): cls.name = "PersonClass" return cls.name # 可以通过cls来传递和操作类属性 # 静态方法 @staticmethod def static_method(): Person.name = "PersonStatic" # 除非直接写类名,否则无法操作类属性(可以但不推荐) return Person.name # 创建实例 p = Person("小明") # 访问类的类方法 print(Person.class_method()) # 输出:PersonStatic # 访问类的静态方法 print(Person.static_method()) # 输出:PersonStatic # 访问实例的类方法 print(p.class_method()) # 输出:PersonStatic # 访问实例的静态方法 print(p.static_method()) # 输出:PersonStatic- 关键细节解释:
1 . 类方法的核心:cls参数
cls是自动传入的,指向调用该方法的类;- 即使通过实例调用,
cls依然指向Person类,因此能稳定访问类属性;
2. 静态方法的核心:无绑定参数
- 静态方法本质是 “挂在类名下的普通函数”,和类的关联仅在于 “归类管理”;
- 不能直接访问类属性 / 实例属性(除非手动写
Person,但不推荐);
3.什么时候用哪个?
- 如果需要操作类属性(读 / 改),或需要创建类的实例→ 用类方法;
- 如果你需要一个和类相关但不依赖类 / 实例属性的工具函数 → 用静态方法;
- 如果你需要操作实例属性→ 用普通实例方法(
self参数)。
使用setattr()方法动态设定/修改类方法和静态方法
- 将一个函数用classmethod()定义为类方法,就可以在方法中操作类属性:
class Person: def __init__(self, name): self.name = name # 只定义了一个实例方法,没有定义类方法 # 创建实例 p = Person("小明") def class_method(cls): cls.name = "PersonClass" # 添加一个类属性 setattr(Person, "class_method", classmethod(class_method)) # Person.class_method() # 用类调用类方法 p.class_method() # 用实例调用类方法也可以 print(Person.name) # 输出:PersonClass print(p.name) # 输出:小明- 将一个函数用staticmethod()定义为静态方法,就可以修改或新建类的静态方法:
class Person: def __init__(self, name): self.name = name # 只定义了一个实例方法,没有定义类方法 @staticmethod def static_method(): print("static_method") # 创建实例 p = Person("小明") # 定义一个新的静态方法 def new_static_method(): print("new_static_method") setattr(Person, "static_method", staticmethod(new_static_method)) # 使用类或者实例都可以调用静态方法 p.static_method() # 输出:new_static_method Person.static_method() # 输出:new_static_method使用setattr()方法动态修改类的实例方法
- 将一个函数用types.MethodType()定义以后,就可以修改(或新建)类的实例方法:
import types class Person: def __init__(self, name): self.name = name # 只定义了一个实例属性,没有定义方法 # 创建实例 p = Person("小明") # 定义一个实例方法 def set_age(self, age): self.age = age setattr(p, "age", int) # 动态添加实例的属性 setattr(p, "set_age", types.MethodType(set_age, p)) # 动态绑定实例的方法 p.set_age(10) # 调用实例的新方法 print(f"名字:{p.name},年龄:{p.age}") # 输出:名字:小明,年龄:10注意:types.MethodType是单例生效的,只对当前传入的实例 (p) 生效。
- 如果需要对类的所有新建实例都生效:
class Person: def __init__(self, name): self.name = name # 只定义了一个实例属性,没有定义方法 # 定义一个实例方法 def set_age(self, age): self.age = age setattr(Person, "age", int) # 动态添加实例的属性 setattr(Person, "set_age", set_age) # 动态绑定实例的方法 p = Person("小红") p.set_age(20) # 调用实例的新方法 print(f"名字:{p.name},年龄:{p.age}") # 输出:名字:小红,年龄:20- Qt窗口部件实例,动态修改其事件处理函数的范例
import sys import types from PySide6.QtCore import Slot from PySide6.QtWidgets import QApplication, QWidget, QMessageBox @Slot() def close_event(self, event): # 弹出确认对话框 reply = QMessageBox.question( self, "确认", "确定要关闭窗口吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No # 默认选中No ) # 根据用户选择决定是否关闭窗口 if reply == QMessageBox.Yes: event.accept() # 接受关闭事件 else: event.ignore() # 忽略关闭 app = QApplication(sys.argv) window = QWidget() window.show() setattr(window, "closeEvent", types.MethodType(close_event, window)) # 动态绑定窗口部件的关闭事件处理函数 sys.exit(app.exec())