不为失败找理由,只为成功找方法。所有的不甘,因为还心存梦想,所以在你放弃之前,好好拼一把,只怕心老,不怕路长。
python系列之面向对象的属性
python系列前期章节
- python系列之注释与变量
- python系列之输入输出语句与数据类型
- python系列之运算符
- python系列之控制流程语句
- python系列之字符串
- python系列之列表
- python系列之元组
- python系列之字典
- python系列之集合
- python系列之函数基础
- python系列之函数进阶
- python系统之综合案例1:用python打造智能诗词生成助手
- python系列之综合案例2:用python开发《魔法学院入学考试》文字冒险游戏
- python系列之类与对象:面向对象编程(用造人计划秒懂面向对象)
1、前言:为什么属性很重要?
想象一下,你正在建造一个机器人。这个机器人有各种部件:手臂、腿、传感器等。在Python面向对象编程中,属性就像是这些部件 - 它们定义了对象的特征和状态。掌握属性,你就掌握了让对象"活起来"的关键!
本文将带你从零开始,全面了解Python中的属性,让你在面向对象编程的路上少走弯路,多些乐趣!
2、本章指南
- 类与实例属性:有什么区别?
- 属性访问控制:公有、保护和私有
- 属性装饰器:@property的魔力
- 动态属性:让对象更灵活
- 描述符:高级属性控制
- 属性最佳实践与常见陷阱
3、类属性与实例属性:有什么区别?
3.1 类属性 - 大家共享的"家族特征"
类属性是所有实例共享的属性,就像家族姓氏一样,所有家庭成员都共享。
class Robot:
# 类属性
family_name = "AI-Robot"
robot_count = 0 # 跟踪创建了多少个机器人
def __init__(self, name):
self.name = name # 实例属性
Robot.robot_count += 1 # 每次创建实例时增加计数
# 创建两个机器人
r2d2 = Robot("R2-D2")
c3po = Robot("C-3PO")
print(r2d2.family_name) # 输出: AI-Robot
print(c3po.family_name) # 输出: AI-Robot
print(f"已创建机器人数量: {Robot.robot_count}") # 输出: 已创建机器人数量: 2
3.2 实例属性 - 每个对象独有的"个人特征"
实例属性是每个对象特有的,就像每个人的名字不同。
class Robot:
def __init__(self, name, serial_number):
self.name = name # 实例属性
self.serial_number = serial_number # 实例属性
r2d2 = Robot("R2-D2", "12345")
c3po = Robot("C-3PO", "67890")
print(r2d2.name) # 输出: R2-D2
print(c3po.name) # 输出: C-3PO
3.3 类属性 vs 实例属性:一个常见的坑
class Robot:
parts = [] # 类属性 - 所有实例共享!
def __init__(self, name):
self.name = name
# 注意:下面这样做会导致问题!
self.parts.append("处理器")
r1 = Robot("Robo-1")
r2 = Robot("Robo-2")
print(r1.parts) # 输出: ['处理器', '处理器']
# 啊哦!两个机器人的parts列表是同一个!
# 正确的方式:
class CorrectRobot:
def __init__(self, name):
self.name = name
self.parts = [] # 实例属性 - 每个对象独立
self.parts.append("处理器")
r1 = CorrectRobot("Robo-1")
r2 = CorrectRobot("Robo-2")
print(r1.parts) # 输出: ['处理器']
print(r2.parts) # 输出: ['处理器']
# 完美!每个机器人有自己的部件列表
4、属性访问控制:公有、保护和私有
Python中没有真正的私有属性,但它提供了一些约定来指示属性的访问级别。
4.1 公有属性 - 大家随便看
class Robot:
def __init__(self, name):
self.name = name # 公有属性
robot = Robot("Optimus")
print(robot.name) # 可以直接访问
robot.name = "Bumblebee" # 可以直接修改
4.2 保护属性 - 礼貌地说"请勿随意触摸"
class Robot:
def __init__(self, name, battery_level):
self.name = name
self._battery_level = battery_level # 保护属性(单下划线)
robot = Robot("Wall-E", 80)
print(robot._battery_level) # 技术上可以访问,但不应该这样做
4.3 私有属性 - 强一点的"禁止入内"
class Robot:
def __init__(self, name, secret_code):
self.name = name
self.__secret_code = secret_code # 私有属性(双下划线)
robot = Robot("R2-D2", "12345")
# print(robot.__secret_code) # 错误!AttributeError
print(robot._Robot__secret_code) # 输出: 12345
# 但还是可以通过名称修饰访问,所以Python的"私有"更多是约定
5、属性装饰器:@property的魔力
@property装饰器让你可以把方法当作属性访问,这非常有用!
5.1 基本用法
class Robot:
def __init__(self, name, battery_level):
self.name = name
self._battery_level = battery_level
@property
def battery_status(self):
"""将电池电量转换为描述性状态"""
if self._battery_level > 70:
return "电量充足"
elif self._battery_level > 30:
return "电量中等"
else:
return "电量不足,请充电"
robot = Robot("Terminator", 50)
print(robot.battery_status) # 输出: 电量中等
# 注意:我们像访问属性一样使用了方法,没有括号!
5.2 设置器和删除器
class Robot:
def __init__(self, name):
self.name = name
self._age = 0 # 私有属性
@property
def age(self):
"""机器人的年龄(只读)"""
return self._age
@age.setter
def age(self, value):
"""设置年龄,但有验证"""
if value < 0:
raise ValueError("年龄不能为负数!")
self._age = value
@age.deleter
def age(self):
"""删除年龄前的清理操作"""
print("备份年龄数据...")
self._age = 0
robot = Robot("Data")
print(robot.age) # 输出: 0
robot.age = 5 # 使用setter
print(robot.age) # 输出: 5
# robot.age = -1 # 抛出ValueError
del robot.age # 使用deleter,输出: 备份年龄数据...
print(robot.age) # 输出: 0
6、动态属性:让对象更灵活
有时我们不知道对象会有哪些属性,或者想动态添加属性。
6.1 动态添加属性
class Robot:
pass
robot = Robot()
robot.name = "Marvin" # 动态添加属性
robot.mood = "抑郁" # 再添加一个
print(f"{robot.name} 感觉很 {robot.mood}")
6.2 dict:查看对象的所有属性
class Robot:
def __init__(self, name):
self.name = name
self.age = 1
robot = Robot("Bender")
robot.mood = "开心" # 动态添加
print(robot.__dict__) # 输出: {'name': 'Bender', 'age': 1, 'mood': '开心'}
6.3 slots:优化内存使用
对于属性固定的类,可以使用__slots__来节省内存。
class EfficientRobot:
__slots__ = ['name', 'age', 'serial_number'] # 只允许这些属性
def __init__(self, name, age, serial_number):
self.name = name
self.age = age
self.serial_number = serial_number
robot = EfficientRobot("Optimus", 5, "12345")
# robot.mood = "勇敢" # 错误!AttributeError
7、描述符:高级属性控制
描述符是更高级的属性控制机制,允许更精细地控制属性的获取、设置和删除。
7.1 简单的描述符示例
class PositiveNumber:
"""描述符:确保值是正数"""
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
return instance.__dict__.get(self.name, 0)
def __set__(self, instance, value):
if value <= 0:
raise ValueError("必须是正数!")
instance.__dict__[self.name] = value
class Robot:
age = PositiveNumber() # 使用描述符
battery_level = PositiveNumber() # 另一个描述符
def __init__(self, name, age, battery_level):
self.name = name
self.age = age
self.battery_level = battery_level
robot = Robot("Number5", 1, 100)
print(robot.age) # 输出: 1
# robot.age = -1 # 抛出ValueError
8、属性最佳实践与常见陷阱
最佳实践
-
使用属性而不是直接暴露内部数据
# 不好 class Robot: def __init__(self, battery): self.battery = battery # 好 class Robot: def __init__(self, battery): self._battery = battery @property def battery(self): return self._battery @battery.setter def battery(self, value): if 0 <= value <= 100: self._battery = value else: raise ValueError("电量必须在0-100之间") -
使用保护属性表示内部实现
class Robot: def __init__(self): self._internal_data = [] # 保护属性,表示是内部实现 -
谨慎使用动态属性
# 通常更好的是明确定义所有属性 class Robot: def __init__(self, name, age): self.name = name self.age = age # 而不是后面动态添加属性
常见陷阱
-
可变类属性的陷阱
class Robot: friends = [] # 所有实例共享同一个列表! def add_friend(self, friend): self.friends.append(friend) r1 = Robot() r2 = Robot() r1.add_friend("Human") print(r2.friends) # 输出: ['Human'] - 啊哦! -
过度使用@property
# 不需要用@property包装每个简单属性 class OverEngineeredRobot: def __init__(self, name): self._name = name @property def name(self): return self._name @name.setter def name(self, value): self._name = value # 简单点更好 class SimpleRobot: def __init__(self, name): self.name = name
9、总结:属性使用速查表
| 场景 | 推荐方法 |
|---|---|
| 简单的数据属性 | 直接使用实例属性 self.attr = value |
| 需要验证或计算的属性 | 使用 @property 和 setter |
| 内部实现细节 | 使用保护属性 self._internal |
| 真正不应该被外部访问的属性 | 使用私有属性 self.__private(但记住Python中没有真正的私有,比如_类名__属性名) |
| 所有实例共享的数据 | 使用类属性 |
| 需要高度控制属性访问 | 使用描述符 |
| 属性固定的类,需要节省内存 | 使用 __slots__ |
3万+

被折叠的 条评论
为什么被折叠?



