ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Python 类属性与实例属性:从原理到实战

Python 类属性与实例属性:从原理到实战 目录一、两种属性的区别二、属性读取演示完整演示代码三、类属性全局修改四、实例赋值的坑五、属性查找规则六、完整流程示例七、del 删除属性八、实战统计实例数量九、动态添加类属性十、两类属性对比十一、易错清单十二、本章小结上一章我们学会了基础的类、实例、__init__构造方法与实例属性我们知道self.xxx是每一个对象独有的数据每只小猫有自己的名字、毛色每台手机有专属品牌、售价互相不会干扰。但现实里还有一类数据属于同一类事物全体共用不需要每个实例单独存一份。比如所有手机都属于“智能手机”这个大类这个分类标签不用苹果、小米各自存一遍放在模板里统一管理就足够。今天我们就来拆解两种属性的核心区别实例属性对象独有和类属性全类共享理清读取、修改的全部规则避开极易踩坑的同名赋值陷阱。一、两种属性的区别我们以手机产品模板来举例直观看懂两种数据的定位class Phone: # 类属性全品类手机共用分类标签 category 智能手机 def __init__(self, brand, price): # 实例属性单台手机独有的信息 self.brand brand self.price price1. 类属性 category相当于产品大类统一标识所有手机共用这一条内容只保存一份。如果修改这个分类名称所有手机读取到的标签都会同步更新。2. 实例属性 brand、price是单台设备专属信息华为、小米各自的品牌、价格互不干扰修改其中一台不会影响其他设备。简单总结实例属性 每个对象私有信息彼此隔离类属性 全类共享信息一处修改全部对象同步生效。二、属性读取演示完整演示代码class Phone(object): # 定义类属性放在所有函数外部 category 智能手机 def __init__(self, brand, price): # self开头为实例属性每个实例独立 self.brand brand self.price price 新建两台手机实例 p1 Phone(苹果, 5999) p2 Phone(小米, 3999) 读取实例专属数据两台结果不一样 print(p1.brand) print(p2.brand) print(p1.price) print(p2.price) 读取共享类属性两种写法都可以 写法1直接使用类名读取规范推荐 print(Phone.category) 写法2通过实例读取结果完全相同 print(p1.category) print(p2.category)运行输出结果苹果 小米 5999 3999 智能手机 智能手机 智能手机能看出不管通过类还是任意实例拿到的类属性数值完全一致。三、类属性全局修改如果使用「类名.属性名」修改类属性所有创建好的实例再读取时都会拿到更新后的内容。下面给出完整可运行的代码包含完整的Phone类定义class Phone: # 类属性全品类手机共用分类标签 category 智能手机 def __init__(self, brand, price): # 实例属性单台手机独有的信息 self.brand brand self.price price p1 Phone(苹果, 5999) p2 Phone(小米, 3999) 通过类统一修改共享分类 Phone.category 5G智能手机 两台设备读取到的内容同步变化 print(p1.category) print(p2.category) print(Phone.category)输出内容统一变为5G智能手机实现全局统一修改。四、实例赋值的坑很多初学者会直接用实例修改类属性名称这种操作不会改动全局共享数据只会给当前实例新增一份同名私有数据造成数据隔离。沿用第二节的Phone类只贴改动片段p1 Phone(苹果, 5999) p2 Phone(小米, 3999) # 错误操作通过实例给 category 赋值 p1.category 老年机 # p1 只会读取自己新增的私有数据 print(p1.category) # 类和其他实例不受任何影响 print(Phone.category) print(p2.category)运行输出老年机 智能手机 智能手机底层逻辑实例读取数据时会优先查找自身携带的属性只有自身不存在对应字段时才会读取类里的共享数据。一旦给实例新增同名属性全局共享内容就不再对它生效。五、属性查找规则为什么实例赋值会「隔离」出私有数据答案藏在 Python 的属性查找顺序里。这是理解两种属性差异的核心难点务必掌握。属性查找的完整底层规则Python实例读取属性时会按照固定顺序逐层查找先在当前实例自身查找该属性找到就直接使用实例自身不存在该属性再去所属类中查找类属性类中也没有就抛出AttributeError报错。配套演示报错代码class Phone: category 手机 p Phone(小米) print(p.price) # 实例、类都无 price直接报错运行这段代码会抛出AttributeError: Phone object has no attribute price因为实例p自身没有price类Phone里也没有price两层都找不到最终只能报错。六、完整流程示例配套简短代码完整走完定义、读取、修改、隔离全过程。沿用第二节的Phone类只贴改动片段p1 Phone(apple) p2 Phone(xiaomi) # 初始共享标签 print(Phone.category) print(p1.category) print(p2.category) # 类统一修改标签 Phone.category 手机 print(p1.category) print(Phone.category) print(p2.category) # 实例创建独立同名属性 p1.category 5G手机 print(p1.category) print(Phone.category) print(p2.category)七、del 删除属性除了读取和修改属性还可以用del关键字删除。删除实例独有属性和删除类全局属性效果完全不同下面分别演示1. 删除实例独有属性实例属性是每个对象单独持有的数据用del 实例.属性名可以把它从当前实例上移除其他实例不受影响class Phone: def __init__(self, brand, price): self.brand brand self.price price p1 Phone(苹果, 5999) p2 Phone(小米, 3999) # 删除 p1 的实例属性 price del p1.price # p1 再读取 price 会报错 print(p1.price) # AttributeError # p2 的 price 不受影响 print(p2.price) # 39992. 删除类全局属性类属性属于整个类共享用del 类名.属性名删除后所有实例再读取该属性都会报错class Phone: category 智能手机 p1 Phone(苹果) p2 Phone(小米) # 删除类属性 category del Phone.category # 所有实例再读取都会报错 print(p1.category) # AttributeError print(p2.category) # AttributeError3. 对比del 实例同名属性后会重新读取类属性结合前面学过的查找规则如果实例自身有同名属性删除它之后实例会重新回到类里读取共享的类属性class Phone: category 智能手机 p1 Phone(苹果) # 给实例新增同名属性覆盖类属性 p1.category 老年机 print(p1.category) # 老年机读取实例自身 # 删除实例自带的 category del p1.category # 实例自身没有了重新读取类属性 print(p1.category) # 智能手机原因解释del p1.category只删除实例自身新增的同名属性类里共享的category仍然存在。删除后实例再读取时自身找不到该属性就会按查找顺序回到类中读取所以输出恢复为智能手机。八、实战统计实例数量类属性最常用的场景是全局统计记录一共创建了多少个实例不需要为每一个对象单独记录数字。沿用第二节的Phone类只贴改动片段class Phone: # 类属性全局计数器所有实例共用 count 0 category 智能手机 def __init__(self, brand): # 实例属性单台手机独有的信息 self.brand brand # 每创建一台设备总数 1 Phone.count 1 连续创建三台设备 p1 Phone(华为) p2 Phone(vivo) p3 Phone(oppo) 读取全局统计数量 print(已生产设备总数量, Phone.count) print(设备统一分类, Phone.category)运行结果已生产设备总数量 3 设备统一分类智能手机反面错误案例不要用self.count 1做全局计数很多新手会写成self.count 1以为这样就能实现全局计数其实这是错误写法只会给当前实例新增一份独立的count全局共享的count永远保持初始值class Phone: count 0 def __init__(self, brand): self.brand brand self.count 1 # 错误写法只会给实例新增 count全局 count 永远为 0 p1 Phone(华为) print(Phone.count) # 输出 0计数失效原因解释self.count 1等价于先读取再赋值由于实例自身没有count读取时拿到的是类里的共享值 0但赋值时却是在当前实例上新建了一个同名属性count并不会修改类共享的count。所以全局计数必须写成Phone.count 1直接通过类名修改类属性才能让所有实例同步看到更新。九、动态添加类属性前面看到的类属性都是写在class代码块内部其实类属性并不强制要求定义在类里。代码任意位置只要通过「类名.属性名」赋值就能给类动态新增一个类属性所有实例同样可以读取到class Phone: pass 类外部动态添加类属性 Phone.count 0 p1 Phone() p2 Phone() 所有实例都能读取到动态新增的类属性 print(Phone.count) # 0 print(p1.count) # 0 print(p2.count) # 0运行结果0 0 0原因解释类属性本质上就是挂在类对象上的一个名字只要通过类名.属性名 值赋值无论写在类定义内部还是外部效果完全一样。这也说明类属性不局限于写在类定义里代码任意位置通过类名都能新增进一步拓展了对类属性的认知。业务场景举例程序运行中动态新增分类标签。比如电商后台在运营过程中新增了一个「限时秒杀」分类不需要修改类定义代码直接通过Goods.category 限时秒杀动态挂到类上所有已创建的商品实例立刻都能读取到这个新标签无需逐个对象赋值。十、两类属性对比到这里实例属性和类属性的核心差异已经全部讲完。下面用一张对比表把两者的定义位置、存储方式、读取方式、修改影响和删除效果一次性梳理清楚方便复习和查阅。对比维度实例属性类属性定义位置在__init__方法内通过self.属性名定义在类代码块内、所有方法外部直接属性名 值定义存储方式每个实例单独保存一份互不干扰整个类只保存一份所有实例共享读取方式实例.属性名类名.属性名或实例.属性名均可通过类修改不适用类无法直接修改实例属性类名.属性名 新值所有实例同步更新通过实例赋值正常修改当前实例自己的数据不会修改类属性只会给当前实例新增同名私有属性删除方式del 实例.属性名只影响当前实例del 类名.属性名所有实例再读取都会报错典型应用每台手机的品牌、价格等专属信息全品类统一分类标签、全局计数器等共享数据一句话记忆实例属性管「个体差异」类属性管「群体共性」。写代码时先想清楚这份数据是每个对象各自持有还是全体对象共用再决定用哪种属性就能避免绝大多数混淆。十一、易错清单本章涉及多个容易踩坑的细节下面把高频易错点集中列出来配合正确写法一起对照易错点 1用实例赋值想修改类属性错误写法p1.category 老年机以为能改全局分类。正确认知这只会给p1新增一份同名私有属性类和其他实例完全不受影响。要修改类属性必须用Phone.category 老年机。易错点 2用self.count 1做全局计数错误写法在__init__里写self.count 1以为能统计实例总数。正确写法必须写成Phone.count 1直接通过类名修改类属性才能让所有实例共享同一个计数器。易错点 3忘记属性查找顺序实例读取属性时永远先找自身再找类。一旦实例有了同名属性类里的共享值就被「屏蔽」了。删除实例同名属性后又会重新读取到类属性。易错点 4删除类属性导致全局报错del Phone.category会删除整个类共享的属性之后所有实例再读取category都会抛出AttributeError。删除类属性前务必确认没有其他地方还在使用它。易错点 5混淆「读取」和「赋值」的路径读取时p1.category和Phone.category结果一样但赋值时两者效果完全不同前者新增实例属性后者修改类属性。读取和赋值要分开记忆不能混为一谈。十二、本章小结本章围绕实例属性和类属性系统梳理了从定义、读取、修改到删除的完整规则核心要点可以概括为以下几条实例属性是每个对象独有的数据通过self.属性名定义彼此隔离互不影响。类属性是全类共享的数据定义在类代码块内、方法外部一处修改所有实例同步生效。读取规则实例读取属性时先找自身再找类两层都没有就抛出AttributeError。修改规则通过类名修改类属性才能全局生效通过实例赋值只会新增同名私有属性不会改动类属性。删除规则del 实例.属性名只影响当前实例del 类名.属性名影响所有实例。实用场景类属性非常适合做全局计数器、统一分类标签等共享数据实例属性适合保存每个对象的专属信息。掌握好「实例管个体、类管群体」这条主线再配合属性查找顺序就能在实战中准确判断该用哪种属性写出更清晰、更不易出错的 Python 代码
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表