Python 是一种支持多种编程范式的语言,其中面向对象编程 (OOP) 是其核心特性之一。面向对象编程以“对象”作为设计和组织软件的基本单元,通过封装数据和操作来模拟现实世界。在Python中,几乎所有元素都是对象,这使得 OOP 的应用非常直观且强大。
掌握面向对象编程的核心概念,对于构建结构清晰、易于维护的应用程序至关重要。
类 (Class)
类是创建对象的蓝图。它定义了一组属性和方法,这些属性和方法将被该类的所有实例共享。你可以将类理解为一个产品的设计图纸。
class MyClass:
# 类变量
class_variable = "I am shared by all instances"
def __init__(self, instance_variable):
# 实例变量
self.instance_variable = instance_variable
def method(self):
print("This is a method of MyClass")
对象 (Object)
对象是类的具体实例。你可以通过像调用函数一样调用类来创建对象,并通过点运算符(.)来访问对象的属性和方法。
my_object = MyClass("I am unique to this instance")
print(my_object.instance_variable) # 输出: I am unique to this instance
my_object.method() # 输出: This is a method of MyClass
继承 (Inheritance)
继承允许一个类(子类)获取另一个类(父类)的属性和方法。这是实现代码复用和建立层次化设计模式的重要手段。
class ChildClass(MyClass):
def child_method(self):
print("This is a method of ChildClass")
child_object = ChildClass(“Child instance variable”)
child_object.method() # 输出: This is a method of MyClass
child_object.child_method() # 输出: This is a method of ChildClass
封装 (Encapsulation)
封装是指将对象的内部状态和实现细节隐藏起来,只通过有限的接口与外部交互。Python 通过下划线约定来实现一定程度的封装。
- 单下划线
_:通常表示属性或方法是内部使用的(受保护的),但并不严格阻止外部访问。
- 双下划线
__:会触发名称重整,使其难以在类外部被直接访问(私有的),尽管并非绝对不可访问。
多态 (Polymorphism)
多态是指不同的子类对象可以对同一消息(方法调用)做出不同的响应。它允许我们使用统一的接口来操作不同类型的对象,提高了代码的通用性和灵活性。
class AnotherChildClass(MyClass):
def method(self): # 重写父类方法
print(“This is a redefined method in AnotherChildClass”)
another_child = AnotherChildClass(“Another child instance variable”)
another_child.method() # 输出: This is a redefined method in AnotherChildClass
理解多态特性,是编写优雅、可扩展程序的关键。
特殊方法 (Magic Methods)
Python 提供了一系列以双下划线开头和结尾的特殊方法,如 __init__, __str__, __repr__ 等。它们定义了对象在特定操作下的行为,例如初始化、字符串表示、运算符重载等。
class MyStrClass:
def __init__(self, string):
self.string = string
def __str__(self):
return f“My string is: {self.string}”
def __repr__(self):
return f“MyStrClass(‘{self.string}’)”
my_str = MyStrClass(“Hello, world!”)
print(str(my_str)) # 输出: My string is: Hello, world!
print(repr(my_str)) # 输出: MyStrClass(‘Hello, world!’)
代码示例
下面通过几个更贴近实际的例子,加深对上述概念的理解。
示例1:封装实践——手机类
# encoding = utf-8
“””
面向对象第一大特征:封装
基于Python3
“””
class CellPhone:
“””
手机类
“””
def __init__(self, cell_phone_number):
self.cell_phone_number = cell_phone_number
self.battery_percentage = 100
def dial(self, cell_phone_number):
print(“Calling %s” % cell_phone_number)
def send_sms(self, cell_phone_number, message):
print(“Sending %s to %s” % (message, cell_phone_number))
def start_charge(self):
print(“Charging…”)
def stop_charge(self):
print(“Charge Finished”)
if __name__ == ‘__main__’:
P30 = CellPhone(“159xxxxxxxx”)
P40 = CellPhone(“180xxxxxxxx”)
print(“P30 手机号是 %s” % P30.cell_phone_number)
print(“P30 手机还剩余电量 %d” % P30.battery_percentage)
P40.battery_percentage = 50
print(“P40 手机号是 %s” % P40.cell_phone_number)
print(“P40 手机还剩余电量 %d” % P40.battery_percentage)
P30.dial(P40.cell_phone_number)
P40.send_sms(P30.cell_phone_number, “Give u feedback later”)
示例2:继承层次——多种手机类型
# encoding = utf-8
from OOP import CellPhone
class SymbianMobilePhone(CellPhone):
“””
塞班手机
“””
pass
class SmartMobilePhone(CellPhone):
“””
智能手机
“””
def __init__(self, cell_phone_number, os=“Android”):
super().__init__(cell_phone_number)
self.os = os
self.app_list = list()
def download_app(self, app_name):
print(“正在下载应用 %s” % app_name)
def delete_app(self, app_name):
print(“正在删除应用 %s” % app_name)
class FullSmartMobilePhone(SmartMobilePhone):
“””
全面屏智能手机
“””
def __init__(self, cell_phone_number, screen_size, os=“Android”):
super().__init__(cell_phone_number, os)
self.screen_size = screen_size
class FolderScreenSmartMobilePhone(SmartMobilePhone):
“””
折叠屏智能手机
“””
def fold(self):
print(“The CellPhone is folded”)
def unfold(self):
print(“The CellPhone is unfolded”)
示例3:多态与抽象——手机解锁方式演进
# encoding = utf-8
class IPhone:
“””
IPhone基类,具有一个解锁功能
“””
def unlock(self, pass_code, **kwargs):
print(“解锁IPhone”)
return True
class IPhone5S(IPhone):
“””
IPhone5S,unlock功能增加了指纹解锁
“””
def finger_unlock(self, fingerprint):
return True
def unlock(self, pass_code, **kwargs):
fingerprint = kwargs.get(“fingerprint”, None)
if self.finger_unlock(fingerprint):
print(“指纹解锁成功”)
return True
else:
return super().unlock(pass_code)
class IPhoneX(IPhone):
“””
IPhoneX, unlock功能增加刷脸解锁
“””
def face_unlock(self, face_id):
return True
def unlock(self, pass_code, **kwargs):
face_id = kwargs.get(“face_id”, None)
if self.face_unlock(face_id):
print(“通过刷脸解锁成功”)
return True
else:
super().unlock(pass_code)
示例4:使用抽象基类规范接口
from abc import ABCMeta, abstractmethod
class MobilePhone(metaclass=ABCMeta):
@abstractmethod
def unlock(self, credential):
pass
class IPhone(MobilePhone):
def unlock(self, credential):
print(“IPhone解锁”)
class IPhone5S(MobilePhone):
def unlock(self, credential):
print(“5S解锁”)
class IPhoneX(MobilePhone):
def unlock(self, credential):
print(“IPhoneX解锁”)
def test_unlock(phone):
if isinstance(phone, IPhone):
phone.unlock(“……”)
else:
print(“传入的参数必须是MobilePhone类型”)
return False
if __name__ == ‘__main__’:
phone = IPhone()
test_unlock(phone)
创建和使用类
使用类可以模拟现实世界中的各种事物。我们来编写一个表示小狗的简单类 Dog。这个类定义了大多数小狗共有的属性和行为:名字、年龄,以及蹲下和打滚的动作。
class Dog():
“”“一次模拟小狗的简单尝试”“”
def __init__(self, name, age):
“”“初始化属性name和age”“”
self.name = name
self.age = age
def sit(self):
“”“模拟小狗被命令时蹲下”“”
print(self.name.title() + “ is now sitting.”)
def roll_over(self):
“”“模拟小狗被命令时打滚”“”
print(self.name.title() + “ rolled over!”)
__init__() 是一个特殊方法,在每次根据 Dog 类创建新实例时,Python 都会自动调用它。形参 self 必不可少且必须位于首位,它指向实例本身,让实例能够访问类中的属性和方法。在方法内部,我们使用 self.name = name 这样的语句将传入的值赋给实例的属性。通过实例访问的变量称为属性。
Dog 类还定义了 sit() 和 roll_over() 两个方法。由于它们不需要额外的信息,因此只有一个形参 self。根据这个类创建的实例都可以调用这两个方法。
根据类创建实例
我们可以根据 Dog 类创建表示特定小狗的实例。
class Dog():
--snip--
my_dog = Dog(‘willie’, 6)
print(“My dog’s name is “ + my_dog.name.title() + “.”)
print(“My dog is “ + str(my_dog.age) + “ years old.”)
my_dog = Dog(‘willie’, 6) 这行代码让 Python 创建一条名为 ‘willie’、年龄为 6 的小狗。Python 使用我们提供的实参调用 __init__() 方法,并返回这个新创建的实例。
要访问实例的属性,使用句点表示法,如 my_dog.name。要调用实例的方法,同样使用句点表示法,如 my_dog.sit()。
my_dog.sit()
my_dog.roll_over()
输出为:
Willie is now sitting.
Willie rolled over!
创建多个实例
你可以根据需要,从一个类创建任意数量的实例。
class Dog():
--snip--
my_dog = Dog(‘willie’, 6)
your_dog = Dog(‘lucy’, 3)
print(“My dog’s name is “ + my_dog.name.title() + “.”)
print(“My dog is “ + str(my_dog.age) + “ years old.”)
my_dog.sit()
print(“\nYour dog’s name is “ + your_dog.name.title() + “.”)
print(“Your dog is “ + str(your_dog.age) + “ years old.”)
your_dog.sit()
每个实例都是独立的,即使它们具有相同的名字和年龄,Python 也会创建不同的实例。
使用类和实例
创建类后,大部分工作将围绕实例展开,包括修改实例的属性。修改属性值主要有三种方式。
给属性指定默认值
在 __init__() 方法中,可以直接为某些属性设置初始值,这样创建实例时就不必提供这些值。
class Car():
def __init__(self, make, model, year):
“”“初始化描述汽车的属性”“”
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0 # 默认值
def get_descriptive_name(self):
long_name = str(self.year) + ‘ ‘ + self.make + ‘ ‘ + self.model
return long_name.title()
def read_odometer(self):
“”“打印一条指出汽车里程的消息”“”
print(“This car has “ + str(self.odometer_reading) + “ miles on it.”)
my_new_car = Car(‘audi’, ‘a4’, 2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()
输出:
2016 Audi A4
This car has 0 miles on it.
修改属性的值
-
直接修改:通过实例直接访问并设置属性。
my_new_car.odometer_reading = 23
my_new_car.read_odometer() # 输出: This car has 23 miles on it.
-
通过方法修改:定义一个方法来更新属性值,可以在方法中加入逻辑控制(如数据验证)。
class Car():
--snip--
def update_odometer(self, mileage):
“”“将里程表读数设置为指定的值”“”
if mileage >= self.odometer_reading: # 禁止回调里程
self.odometer_reading = mileage
else:
print(“You can’t roll back an odometer!”)
my_new_car.update_odometer(23)
my_new_car.read_odometer()
-
通过方法递增:定义一个方法来增加属性值。
class Car():
--snip--
def increment_odometer(self, miles):
“”“将里程表读数增加指定的量”“”
self.odometer_reading += miles
my_used_car = Car(‘subaru’, ‘outback’, 2013)
my_used_car.update_odometer(23500)
my_used_car.increment_odometer(100)
my_used_car.read_odometer() # 输出: This car has 23600 miles on it.
继承
当你需要编写的类是另一个现有类的特殊版本时,使用继承可以事半功倍。原有的类称为父类(或基类、超类),新类称为子类(或派生类)。子类自动获得父类的所有属性和方法,同时可以定义自己特有的属性和方法。
子类的 __init__() 方法
创建子类时,通常需要先初始化父类的属性。
class Car():
“”“一次模拟汽车的简单尝试”“”
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
long_name = str(self.year) + ‘ ‘ + self.make + ‘ ‘ + self.model
return long_name.title()
def read_odometer(self):
print(“This car has “ + str(self.odometer_reading) + “ miles on it.”)
def update_odometer(self, mileage):
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print(“You can’t roll back an odometer!”)
def increment_odometer(self, miles):
self.odometer_reading += miles
class ElectricCar(Car): # 指定父类名称
“”“电动汽车的独特之处”“”
def __init__(self, make, model, year):
“”“初始化父类的属性”“”
super().__init__(make, model, year) # 调用父类的__init__
my_tesla = ElectricCar(‘tesla’, ‘model s’, 2016)
print(my_tesla.get_descriptive_name()) # 输出: 2016 Tesla Model S
super() 是一个特殊函数,它帮助 Python 找到父类并调用其方法,确保子类包含所有父类的属性。
给子类定义属性和方法
让子类区别于父类,需要添加其特有的属性和方法。
class ElectricCar(Car):
def __init__(self, make, model, year):
super().__init__(make, model, year)
self.battery_size = 70 # 子类特有属性
def describe_battery(self): # 子类特有方法
“”“打印一条描述电瓶容量的消息”“”
print(“This car has a “ + str(self.battery_size) + “-kWh battery.”)
my_tesla = ElectricCar(‘tesla’, ‘model s’, 2016)
my_tesla.describe_battery() # 输出: This car has a 70-kWh battery.
重写父类的方法
如果父类的方法不适合子类,可以在子类中定义一个同名方法来将其重写。
class ElectricCar(Car):
--snip--
def fill_gas_tank(): # 父类可能有这个方法,但电动车不需要
“”“电动汽车没有油箱”“”
print(“This car doesn’t need a gas tank!”)
将实例用作属性
随着类变得复杂,可以考虑将相关属性和方法提取出来,作为一个独立的类,然后将这个类的实例用作原类的属性。这有助于保持代码的清晰和组织性。
class Battery():
“”“一次模拟电动汽车电瓶的简单尝试”“”
def __init__(self, battery_size=70):
“”“初始化电瓶的属性”“”
self.battery_size = battery_size
def describe_battery(self):
print(“This car has a “ + str(self.battery_size) + “-kWh battery.”)
def get_range(self):
“”“打印一条消息,指出电瓶的续航里程”“”
if self.battery_size == 70:
range = 240
elif self.battery_size == 85:
range = 270
message = “This car can go approximately “ + str(range)
message += “ miles on a full charge.”
print(message)
class ElectricCar(Car):
“”“电动汽车的独特之处”“”
def __init__(self, make, model, year):
super().__init__(make, model, year)
self.battery = Battery() # 将Battery实例作为属性
my_tesla = ElectricCar(‘tesla’, ‘model s’, 2016)
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
输出:
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.
导入类
为了让代码更整洁、易于维护,可以将类存储在独立的模块(.py 文件)中,然后在主程序中导入所需模块。
1. 导入单个类
假设我们将 Car 类保存在 car.py 文件中。
# car.py
“”“一个可用于表示汽车的类”“”
class Car():
--snip-- # Car类的定义
在主程序中:
from car import Car
my_new_car = Car(‘audi’, ‘a4’, 2016)
print(my_new_car.get_descriptive_name())
2. 从一个模块导入多个类
如果 car.py 文件中还定义了 Battery 和 ElectricCar 类。
from car import Car, ElectricCar
my_beetle = Car(‘volkswagen’, ‘beetle’, 2016)
my_tesla = ElectricCar(‘tesla’, ‘roadster’, 2016)
3. 导入整个模块
import car
my_beetle = car.Car(‘volkswagen’, ‘beetle’, 2016)
my_tesla = car.ElectricCar(‘tesla’, ‘roadster’, 2016)
4. 导入模块中的所有类(不推荐)
from module_name import * 这种方式不明确,容易引发名称冲突,应尽量避免。
5. 在一个模块中导入另一个模块
当类分散在不同模块且存在依赖时,可以在模块中导入其他模块的类。例如,electric_car.py 需要用到 car.py 中的 Car 类。
# electric_car.py
“”“一组用于表示电动汽车的类”“”
from car import Car # 导入需要的类
class Battery():
--snip--
class ElectricCar(Car):
--snip--
Python标准库
Python 标准库包含大量实用的模块和类。例如,collections 模块中的 OrderedDict 类,它保持了键值对的添加顺序,而普通字典 dict 不记录顺序。
from collections import OrderedDict
favorite_languages = OrderedDict() # 创建一个空的有序字典
favorite_languages[‘jen’] = ‘python’
favorite_languages[‘sarah’] = ‘c’
favorite_languages[‘edward’] = ‘ruby’
favorite_languages[‘phil’] = ‘python’
for name, language in favorite_languages.items():
print(name.title() + “‘s favorite language is “ + language.title() + “.”)
输出将严格按照添加顺序(Jen, Sarah, Edward, Phil)进行。
编码规范
遵循良好的编码规范能使你的代码更专业、更易读。
- 类名:采用驼峰命名法,即每个单词的首字母大写,不含下划线(如
ElectricCar)。
- 实例名和模块名:采用小写字母加下划线的格式(如
my_tesla, electric_car.py)。
- 文档字符串:每个类定义后都应紧跟一个文档字符串,简要描述类的功能。模块开头也应有一个模块级的文档字符串。
- 空行使用:在类中,使用一个空行分隔方法;在模块中,使用两个空行分隔不同的类。
- 导入顺序:先导入标准库模块,空一行,再导入自己编写的模块。
- 代码组织:开始时可以让代码简单,一切运行正确后,再考虑将类拆分到独立的模块中。
掌握 Python 面向对象编程是成为高级开发者的重要一步。通过在 云栈社区 与其他开发者交流实践,你可以更快地将这些核心概念转化为解决实际问题的能力。