1192 字
6 分钟
Python面向对象进阶:属性管理与魔术方法
深入探讨 Python 面向对象编程中的进阶话题,包括类属性与实例属性的区别、三种方法类型(实例/类/静态)、@property 封装以及常用的魔术方法。
在掌握了 Python 面向对象的基础之后,我们需要进一步了解如何编写更“Pythonic”的类。本文将涵盖属性管理、方法类型以及强大的魔术方法。
1. 属性与方法的进阶
1.1 类属性 vs 实例属性
这是新手最容易混淆的地方。
- 实例属性:定义在
__init__或其他方法中,使用self.variable。属于单个对象。 - 类属性:直接定义在类体中。属于类本身,所有实例共享。
class Dog: species = "Canis" # 类属性:所有狗都是犬科
def __init__(self, name): self.name = name # 实例属性:每只狗名字不同
d1 = Dog("Buddy")d2 = Dog("Charlie")
# 访问类属性print(d1.species) # Canis (通过实例访问)print(Dog.species) # Canis (推荐:通过类名访问)
# 修改类属性Dog.species = "Wolf"print(d1.species) # Wolf (所有实例感知变化)
# 【坑点预警】通过实例修改类属性d1.species = "Cat"# 这一步并没有修改类属性!而是在 d1 对象上创建了一个同名的实例属性 'species'# 屏蔽了对类属性的访问。
print(d1.species) # Cat (d1 的实例属性)print(d2.species) # Wolf (依然是类属性)print(Dog.species) # Wolf (类属性未变)1.2 三种方法类型
Python 的类中可以定义三种方法:
-
实例方法 (Instance Method):
- 第一个参数是
self。 - 可以访问实例属性和类属性。
- 最常用。
- 第一个参数是
-
类方法 (Class Method):
- 使用
@classmethod装饰器。 - 第一个参数是
cls(代表类本身)。 - 不能访问实例属性,只能访问类属性。
- 用途:常用于实现“工厂模式”或修改类状态。
- 使用
-
静态方法 (Static Method):
- 使用
@staticmethod装饰器。 - 不需要
self或cls参数。 - 就像一个普通函数放在了类里面,逻辑上属于这个类,但在运行时与类/实例无关。
- 用途:工具函数。
- 使用
class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day
# 实例方法 def format(self): return f"{self.year}-{self.month}-{self.day}"
# 类方法:作为构造函数的一种替代(工厂模式) @classmethod def from_string(cls, date_str): # date_str 格式 "2023-10-01" year, month, day = map(int, date_str.split('-')) return cls(year, month, day) # 返回一个新的实例
# 静态方法:不需要访问类或实例的数据 @staticmethod def is_valid(date_str): return '-' in date_str
# 使用d1 = Date(2023, 10, 1)d2 = Date.from_string("2023-12-25") # 调用类方法print(d2.format())
print(Date.is_valid("2023-10-1")) # True2. 封装与访问控制
2.1 私有属性与名称改写
Python 没有像 Java 那样严格的 private 关键字。它通过命名约定来实现封装。
public:self.name,公有,随处可访问。protected:self._age(单下划线),约定视为内部使用,但解释器不强制限制。private:self.__money(双下划线),解释器会进行名称改写 (Name Mangling),防止子类意外覆盖或外部直接访问。
class Account: def __init__(self, balance): self.__balance = balance # 私有属性
def get_balance(self): return self.__balance
acc = Account(100)# print(acc.__balance) # AttributeErrorprint(acc.get_balance()) # 100
# 强行访问(不推荐,除非调试)print(acc._Account__balance) # 100 (Python 将其改名为 _ClassName__variable)2.2 使用 @property 装饰器
@property 是 Pythonic 的封装方式。它允许你像访问属性一样调用方法,实现对属性的获取、设置和删除的控制。
class Person: def __init__(self, name): self._name = name
# Getter @property def name(self): return self._name
# Setter @name.setter def name(self, value): if not isinstance(value, str): raise ValueError("Name must be a string") self._name = value
# Deleter @name.deleter def name(self): print("Deleting name...") del self._name
p = Person("Alice")print(p.name) # 自动调用 getterp.name = "Bob" # 自动调用 setter# p.name = 123 # 抛出 ValueError3. 魔术方法 (Magic Methods)
魔术方法(Dunder Methods,双下划线方法)允许你的对象模拟内置类型的行为(如算术运算、长度获取、索引访问等)。
3.1 字符串表示:__str__ vs __repr__
__str__:面向用户,打印时 (print()) 调用,力求可读性。__repr__:面向开发者,调试时 (repl环境) 调用,力求准确性(最好能用来重建对象)。
class Vector: def __init__(self, x, y): self.x = x self.y = y
def __str__(self): return f"Vector({self.x}, {self.y})"
def __repr__(self): return f"Vector(x={self.x}, y={self.y})"
v = Vector(1, 2)print(v) # 调用 __str__print([v]) # 列表内的元素会调用 __repr__3.2 运算符重载
让你的对象支持 +, -, * 等操作。
# 接上面的 Vector 类 def __add__(self, other): if isinstance(other, Vector): return Vector(self.x + other.x, self.y + other.y) return NotImplemented
v1 = Vector(1, 2)v2 = Vector(3, 4)v3 = v1 + v2 # 自动调用 v1.__add__(v2)print(v3) # Vector(4, 6)3.3 其他常用魔术方法
__len__(self):len(obj)时调用。__getitem__(self, key):obj[key]时调用,实现索引或切片访问。__call__(self): 让对象像函数一样被调用obj()。__enter__/__exit__: 实现上下文管理器(with语句)。
总结
Python 的 OOP 既简洁又强大。
- 区分清楚类属性与实例属性,防止数据污染。
- 善用
@property和魔法方法,写出 Pythonic 的代码。 - 理解鸭子类型,不要过分纠结于类型检查,而要关注行为(接口)。
- 掌握
super(),为编写可维护的继承结构打好基础。
发现错误或想要改进这篇文章?
在 GitHub 上编辑此页 Python面向对象进阶:属性管理与魔术方法