Python面向对象编程入门:类、对象与封装实战
本文深入解析Python中的面向对象编程(OOP)核心概念,涵盖类与对象的定义、实例化及消息传递机制。重点讲解封装、继承和多态三大支柱,详细演示如何通过双下划线实现私有属性,并探讨Python中访问权限的实际约束与命名惯例。结合数字时钟与坐标点计算等代码实例,帮助开发者掌握构建复杂系统的基础技能。
面向对象编程概述
当下的程序员普遍熟悉“面向对象编程”这一术语,常有人询问能否用一句话概括其定义。我们先从较为正式的定义入手进行解析。
面向对象编程的核心在于将数据结构及其处理方法组合为对象(object),将具有相同行为的对象归纳为类(class)。通过封装(encapsulation)隐藏内部实现细节,利用继承(inheritance)实现类的特化(specialization)与泛化(generalization),并借助多态(polymorphism)实现基于对象类型的动态分派。
上述定义可能令人困惑,因此我们参考更通俗的解释,内容源自知乎社区。

注:以上内容源自网络,不代表作者观点,与本站立场无关,相关责任由原作者承担。
此前我们提到“程序是指令的集合”,代码执行时会转化为CPU指令。为简化设计,我们引入函数将独立且重复的代码模块化,通过调用函数实现功能;若函数过于复杂,可进一步拆分为子函数以降低系统复杂度。然而,编程本质是程序员按计算机工作方式控制任务,这与人类思维模式相悖,若编程需抛弃人类思维迎合计算机,将极大削弱编程乐趣,“人人学编程”的愿景也难以实现。更重要的是,开发复杂系统时,代码复杂性会导致开发与维护困难,20世纪60年代末,“软件危机”与“软件工程”概念应运而生。
现实中并无解决上述问题的“银弹”,但20世纪70年代Smalltalk语言引入的面向对象思想(雏形可追溯至Simula语言)带来了希望。该理念认为程序中数据与操作数据的函数构成逻辑整体,即“对象”。通过创建对象并向其发送消息,多对象协同工作即可构建复杂系统以解决现实问题。
注:面向对象并非解决所有软件开发问题的“银弹”,现代高级编程语言(包括Python)均支持多种编程范式。
类与对象的定义
类和对象
简言之,类是对象的蓝图和模板,对象则是类的实例。类代表抽象概念,对象代表具体实体。在面向对象编程中,一切皆为对象,拥有属性和行为,且每个对象唯一并属于特定类。抽取具有共同特征的对象的静态特征(属性)和动态特征(行为),即可定义出“类”。

类的定义方法
定义类
在Python中,使用class关键字定义类,并在类中通过函数定义方法,以描述对象的动态特征。代码如下所示。
class Student(object):
# __init__是一个特殊方法用于在创建对象时进行初始化操作
# 通过这个方法我们可以为学生对象绑定name和age两个属性
def __init__(self, name, age):
self.name = name
self.age = age
def study(self, course_name):
print('%s正在学习%s.' % (self.name, course_name))
# PEP 8要求标识符的名字用全小写多个单词用下划线连接
# 但是很多程序员和公司更倾向于使用驼峰命名法(驼峰标识)
def watch_av(self):
if self.age < 18:
print('%s只能观看《熊出没》.' % self.name)
else:
print('%s正在观看岛国爱情动作片.' % self.name)
注:类中定义的函数通常称为(对象的)方法,即对象可接收的消息。
对象的创建与使用
创建和使用对象
定义类后,可通过以下方式创建对象并发送消息。
def main():
# 创建学生对象并指定姓名和年龄
stu1 = Student('骆昊', 38)
# 给对象发study消息
stu1.study('Python程序设计')
# 给对象发watch_av消息
stu1.watch_av()
stu2 = Student('王大锤', 15)
stu2.study('思想品德')
stu2.watch_av()
if __name__ == '__main__':
main()
访问权限与封装机制
访问可见性问题
具有C++、Java、C#经验的开发者可能关注Student对象的name和age属性的访问权限(可见性)。在多数面向对象语言中,属性常设为私有(private)或受保护(protected),即禁止外部访问,而方法通常为公开(public),因为方法是对象可接收的消息。Python中属性和方法仅有公开和私有两种权限。若需私有属性,命名时可使用两个下划线开头,以下代码可验证这一点。
class Test:
def __init__(self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
# AttributeError: 'Test' object has no attribute '__bar'
test.__bar()
# AttributeError: 'Test' object has no attribute '__foo'
print(test.__foo)
if __name__ == "__main__":
main()
然而,Python并未在语法上严格保证私有属性或方法的私密性,仅通过改名“妨碍”访问。若知晓命名规则,仍可访问,如下代码所示。如此设计源于“We are all consenting adults here”的理念,即程序员应自行负责,且开放优于封闭。
class Test:
def __init__(self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
test._Test__bar()
print(test._Test__foo)
if __name__ == "__main__":
main()
实际开发中,不建议将属性设为私有,因为这可能导致子类无法访问(后续章节会涉及)。因此,多数Python程序员遵循命名惯例:以单下划线开头表示属性受保护,外部代码访问时应保持谨慎。这并非语法规则,单下划线开头的属性和方法仍可被外部访问,故更多是一种暗示或隐喻。
面向对象的核心支柱
面向对象的支柱
面向对象有三大支柱:封装、继承和多态。后两者将在下一章详细阐述,此处先解释封装。我对封装的理解是“隐藏所有可隐藏的实现细节,仅向外界暴露简单的编程接口”。类中定义的方法即是将数据与操作封装起来。创建对象后,只需发送消息(调用方法)即可执行代码,即只需知晓方法名和参数(外部视图),无需了解内部实现细节(内部视图)。
实战练习
练习
练习1:定义一个类描述数字时钟。
class Clock(object):
"""数字时钟"""
def __init__(self, hour=0, minute=0, second=0):
"""初始化方法
:param hour: 时
:param minute: 分
:param second: 秒
"""
self._hour = hour
self._minute = minute
self._second = second
def run(self):
"""走字"""
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0
def show(self):
"""显示时间"""
return '%02d:%02d:%02d' % \
(self._hour, self._minute, self._second)
def main():
clock = Clock(23, 59, 58)
while True:
print(clock.show())
sleep(1)
clock.run()
if __name__ == '__main__':
main()
练习2:定义一个类描述平面上的点,并提供移动点和计算到另一个点距离的方法。
from math import sqrt
class Point(object):
def __init__(self, x=0, y=0):
"""初始化方法
:param x: 横坐标
:param y: 纵坐标
"""
self.x = x
self.y = y
def move_to(self, x, y):
"""移动到指定位置
:param x: 新的横坐标
"param y: 新的纵坐标
"""
self.x = x
self.y = y
def move_by(self, dx, dy):
"""移动指定的增量
:param dx: 横坐标的增量
"param dy: 纵坐标的增量
"""
self.x += dx
self.y += dy
def distance_to(self, other):
"""计算与另一个点的距离
:param other: 另一个点
"""
dx = self.x - other.x
dy = self.y - other.y
return sqrt(dx ** 2 + dy ** 2)
def __str__(self):
return '(%s, %s)' % (str(self.x), str(self.y))
def main():
p1 = Point(3, 5)
p2 = Point()
print(p1)
print(p2)
p2.move_by(-1, 2)
print(p2)
print(p1.distance_to(p2))
if __name__ == '__main__':
main()
注:本章插图源自Grady Booch等著的《面向对象分析与设计》,该书为OOP经典著作,有兴趣的读者可购买阅读以了解更多相关知识。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









