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