Python面向对象编程入门:类、对象与封装实战

Evan
Evan
发布时间:2026-10-13| 4人浏览过
原创

本文深入解析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经典著作,有兴趣的读者可购买阅读以了解更多相关知识。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。