Python 是一种支持多种编程范式的语言,面向对象编程 (OOP) 是其核心特性之一。对于有Java OOP经验的开发者来说,理解Python在类、对象、继承和多态等方面的独特实现方式,将有助于更好地运用Python进行设计和开发。
一、Python类定义基础
<a name="1-类定义-class-definition"></a>
1.1 基本语法与核心组成
在Python中,使用 class 关键字定义类,其后跟类名和可选的父类列表(用于继承),然后是冒号和缩进的类体。
class MyClassName(BaseClass1, BaseClass2, ...): # Python支持多重继承
"""可选的类文档字符串 (Docstring)"""
class_attribute = "我是类属性,所有实例共享" # 类属性
def __init__(self, param1, param2): # 初始化方法 (构造器)
self.instance_attribute1 = param1 # 实例属性,通过self绑定到实例
self.instance_attribute2 = param2
def instance_method(self, other_param): # 实例方法,第一个参数必须是self
# self 引用实例本身
print(f"实例属性1: {self.instance_attribute1}")
print(f"传入参数: {other_param}")
return self.instance_attribute1 + other_param
@classmethod
def class_method(cls, data): # 类方法,第一个参数是类本身(约定为cls)
print(f"这是一个类方法,访问类属性: {cls.class_attribute}")
# cls 可以用来创建类的实例,例如工厂方法
return cls(data, "default_value_from_classmethod")
@staticmethod
def static_method(x, y): # 静态方法,不隐式传递self或cls
print("这是一个静态方法,它不依赖实例或类状态。")
return x + y
1.2 对比Java类定义
- Java:
public class MyClassName extends BaseClass implements Interface1, Interface2 { ... } - 主要区别: Python原生支持从多个具体类继承(多重继承),而Java类是单继承,但可以通过实现多个接口来达到类似效果。
二、初始化方法 (__init__) 与Java构造器
<a name="2-初始化方法---init----vs-java-constructor"></a>
- Python:
__init__(self, ...)是一个特殊的初始化方法。当一个对象被创建后(对象本身通常由__new__方法创建,但开发者较少直接重写__new__),__init__会被自动调用,用于设置实例的初始状态和属性。self参数是对新创建实例的引用。 - Java: 构造方法名与类名相同,没有返回类型。
this关键字在非静态方法中隐式可用,指向当前实例。
三、self 关键字 与 Java的 this
<a name="3-self--vs--this"></a>
- Python:
self必须作为实例方法的第一个参数显式声明。它代表实例对象本身。当调用实例方法如instance.method(arg)时,Python解释器会自动将instance传递给self参数。 - Java:
this是一个关键字,在非静态方法中隐式指向当前实例,无需在参数列表中声明。
四、属性与方法详解
<a name="4-属性与方法-attributes-and-methods"></a>
4.1 实例属性与类属性
- 实例属性: 通常在
__init__方法中通过self.attribute_name = value定义,它们属于每个独立的实例。 - 类属性: 直接在类体中、方法之外定义的属性。它们由该类的所有实例共享。可以通过类名 (
ClassName.class_attribute) 或实例 (instance.class_attribute,如果实例没有同名的实例属性来覆盖它) 访问。
4.2 实例方法、类方法 (@classmethod) 与静态方法 (@staticmethod)
- 实例方法: 定义在类中的函数,第一个参数是
self,用于操作实例的状态和行为。 - 类方法 (
@classmethod): 使用@classmethod装饰器定义,其第一个参数是类本身 (约定命名为cls)。类方法可以访问和修改类属性,常用于实现工厂方法(返回类的实例)或操作与类整体相关的状态。 - 静态方法 (
@staticmethod): 使用@staticmethod装饰器定义,它不接收隐式的self或cls参数。静态方法在逻辑上属于这个类,但其行为不依赖于实例状态或类状态,更像是一个定义在类命名空间内的普通工具函数 (类似于Java中的static工具方法)。
五、封装与访问控制:Python的约定与Java的强制
<a name="5-封装与访问控制-encapsulation--access-control"></a>
Python在封装和访问控制方面与Java有显著不同。
5.1 Python的访问控制约定
Python 没有严格的 private, protected, public 关键字来强制访问控制。其封装更多依赖于“约定”和名称修饰。
- 单下划线前缀 (
_protected_member): 按照约定,这被视为“受保护的”成员。它暗示这是类的内部实现细节,不建议从类外部直接访问。但这仅仅是一个提示,Python解释器并不会阻止外部访问(体现了“我们都是成年人”的哲学)。 - vs. Java: Java通过
public,protected,default(包可见性),private关键字来强制实现不同级别的访问控制。
5.2 名称修饰 (__private_member)
- 双下划线前缀 (
__private_member) (不以双下划线结尾): 当在类定义中使用时,会触发Python的名称修饰 (name mangling) 机制。解释器会自动将这样的名称改写为_ClassName__private_member的形式。 - 目的: 主要目的是为了避免子类意外覆盖父类的“私有”成员,从而在一定程度上实现了“伪私有”。但它并非真正的私有,因为仍然可以通过改写后的名称 (
_ClassName__private_member) 访问到。
class MySecretiveClass:
def __init__(self):
self.public_data = "Public"
self._protected_data = "Protected"
self.__private_data = "Private"
def get_private_data_internally(self):
return self.__private_data
obj = MySecretiveClass()
print(obj.public_data)
print(obj._protected_data) # 可以访问,但不推荐
# print(obj.__private_data) # 会报 AttributeError
print(obj._MySecretiveClass__private_data) # 可以通过名称修饰后的名字访问
print(obj.get_private_data_internally())
六、继承机制:Python的多重继承与MRO
<a name="6-继承-inheritance"></a>
6.1 Python的继承语法与多重继承
Python通过在类定义时括号内指定父类来实现继承:class DerivedClass(BaseClass1, BaseClass2, ...): ...。
Python支持多重继承,即一个子类可以同时从多个父类继承属性和方法。
6.2 方法解析顺序 (MRO - Method Resolution Order)
在多重继承中,当调用一个方法时,Python使用 C3 线性化算法来确定应该在哪一个父类中查找该方法。MRO定义了类及其父类的查找顺序。可以通过 ClassName.mro() 或 ClassName.__mro__ 查看一个类的MRO。
6.3 super() 函数的应用
super() 函数用于在子类中调用父类的方法。它返回一个特殊的代理对象,该对象会按照MRO去查找并调用合适的父类方法。
super().__init__(...):常用于在子类的__init__中调用父类的初始化方法。super().method_name(...):调用父类的同名方法。 在Python 3中,super()可以无参数调用 (在实例方法中等价于super(CurrentClass, self))。
class ParentA:
def __init__(self, a):
print("ParentA init")
self.a = a
def greet(self):
print("Hello from ParentA")
class ParentB:
def __init__(self, b):
print("ParentB init")
self.b = b
def greet(self): # ParentB也有greet方法
print("Greetings from ParentB")
class Child(ParentA, ParentB): # 多重继承
def __init__(self, a, b, c):
print("Child init")
# super().__init__(a) # 会根据MRO调用ParentA的__init__
ParentA.__init__(self, a) # 显式调用ParentA的__init__
ParentB.__init__(self, b) # 显式调用ParentB的__init__
self.c = c
def child_greet(self):
super().greet() # 会根据MRO调用ParentA的greet
ParentB.greet(self) # 显式调用ParentB的greet
# print(Child.mro())
my_child = Child('val_a', 'val_b', 'val_c')
my_child.child_greet()
- vs. Java: Java类是单继承 (
class DerivedClass extends BaseClass { ... })。Java使用super.method()或super(...)调用父类的方法或构造器。
七、多态与Python的鸭子类型
<a name="7-多态与鸭子类型-polymorphism--duck-typing"></a>
Python的多态更多地体现在鸭子类型 (Duck Typing) 上:“如果一个东西走起来像鸭子,叫起来像鸭子,那么它就是一只鸭子。”
这意味着Python不关注对象的显式类型是什么,而是关注对象是否具有期望的方法和属性(即行为)。只要不同的对象实现了同样接口(同样的方法名和参数签名),它们就可以在代码中被同等对待和互换使用,无需强制它们继承自同一个基类或实现同一个显式接口。
- vs. Java: Java的多态通常基于显式的继承(子类重写父类方法)和接口实现。类型兼容性通常在编译时检查。Python的鸭子类型更为灵活,检查主要发生在运行时。
八、常用魔法方法 (Dunder Methods)
<a name="8-常用魔法方法-dunder-methods--special-methods"></a>
魔法方法(Dunder methods,来自Double Underscore)是以双下划线开头和结尾的方法 (如 __init__, __str__)。它们在Python中有特殊的含义,会在特定情况下被Python解释器自动调用,用于实现特定的对象行为或协议。
8.1 __init__, __str__, __repr__
__init__(self, ...): 对象初始化方法,已讨论。__str__(self): 当对对象使用print()函数或str()内置函数时调用。必须返回一个字符串,作为对象的“非正式”或用户友好的字符串表示。类似于Java的Object.toString()。__repr__(self): 当对对象使用repr()内置函数时调用(交互式解释器中直接输入变量名回车也会触发)。应返回一个“官方的”字符串表示,理想情况下这个字符串是一个有效的Python表达式,可以用来重新创建具有相同值的对象。如果做不到,通常返回一个形如<...some useful description...>的字符串。- 约定: 若定义了
__str__但未定义__repr__,repr()通常会退用__str__。最佳实践是两者都定义,或至少定义一个有用的__repr__(若只定义__repr__,str()也会用它)。
- 约定: 若定义了
8.2 容器与集合类魔法方法 (__len__, __getitem__等)
__len__(self):len(obj)时调用,返回对象“长度”。__getitem__(self, key):obj[key](索引或键访问) 时调用。__setitem__(self, key, value):obj[key] = value时调用。__delitem__(self, key):del obj[key]时调用。__iter__(self): 对对象迭代时 (如for循环) 首先调用,应返回一个迭代器对象。__next__(self): 由迭代器对象实现,迭代每一步调用,返回下一元素。无更多元素时抛StopIteration。
8.3 可调用对象 __call__
__call__(self, *args, **kwargs): 如果类的实例实现了__call__方法,那么这个实例就可以像函数一样被“调用”:instance_obj(arg1, arg2)。
8.4 比较操作符 (__eq__, __lt__等)
__eq__(self, other)(等于==)__ne__(self, other)(不等于!=)__lt__(self, other)(小于<)__le__(self, other)(小于等于<=)__gt__(self, other)(大于>)__ge__(self, other)(大于等于>=) 实现这些方法可以让自定义对象支持比较运算。
8.5 析构器 __del__ (注意事项)
__del__(self): 理论上在对象即将被垃圾回收器销毁前调用。- 警告: 不应依赖
__del__来执行关键的资源清理操作 (如关闭文件、数据库连接、释放锁等)。其调用时机不可靠,也不保证一定会被调用。关键清理应使用try...finally或with语句(上下文管理器)。 - vs. Java: 不完全等同于Java的
finalize()方法,后者也有类似不可靠性和不推荐使用的问题。
九、对象的属性字典 (__dict__) 与 __slots__
<a name="9-对象的属性字典---dict----"></a>
大多数Python的自定义对象实例都有一个名为 __dict__ 的特殊属性。
__dict__: 是一个字典,存储了该实例的所有可写属性及其对应的值。 Pythonclass MyClass: class_var = "I am a class variable" def __init__(self, x, y): self.x = x # 实例变量 self.y = y # 实例变量 obj = MyClass(10, 20) print(obj.__dict__) # 输出 (通常): {'x': 10, 'y': 20} obj.z = 30 # 动态添加实例变量 print(obj.__dict__) # 输出 (通常): {'x': 10, 'y': 20, 'z': 30} print(MyClass.__dict__) # 显示类属性、方法等__slots__: 如果类定义中使用了__slots__特殊属性(一个字符串元组,列出允许的实例属性名),那么该类的实例默认将不再拥有__dict__属性(除非__dict__本身也被列在__slots__中)。这样做可以节省内存,但也意味着不能再给这些实例动态添加__slots__未声明的属性。
I will proceed with the next section: VI. 模块 (Modules) 与 包 (Packages) in the next response.


Comments NOTHING