一、什么是面向对象编程?
面向对象编程(Object-Oriented Programming,简称 OOP)是一种重要的编程风格,它与其他编程风格(如面向过程、指令式编程、函数式编程)共同构成了主流的编程范式。
- 面向过程:以 C 语言为代表,强调任务执行的顺序和过程,通过函数分步实现任务,适用于简单任务。
- 面向对象:将任务拆分为不同对象,每个对象封装特定数据和行为,通过对象交互完成任务,更注重程序设计和可维护性,适用于复杂任务。
| 特性 | 面向过程 | 面向对象 |
|---|---|---|
| 基本单位 | 函数或过程 | 对象(通过类实例化) |
| 数据和行为的关系 | 数据和行为分开 | 数据和行为封装在对象中 |
| 设计方式 | 强调功能和线性执行过程 | 强调对象交互和消息传递 |
| 可扩展性 | 较差,难以应对复杂结构 | 较好,易于扩展和修改 |
| 易维护性 | 复杂系统中难以维护 | 模块化程度高,便于维护 |
面向对象的三大核心基石是:封装、继承、多态。
二、封装(Encapsulation)
封装是指将类的属性私有化,仅通过公开方法对外提供访问接口,从而保证数据安全并屏蔽实现细节。
1. 类的概念
类是对具有相同特征(属性)和行为(方法)的事物的抽象描述。
- 特征(属性):通常是名词(如狗的年龄、品种)。
- 行为(方法):通常是动词(如狗的 “叫”、“跑”)。
类是抽象的模板,而对象是类的具体实例(例如,“狗” 是类,“我家的金毛犬” 是对象)。
2. 类的定义与对象创建
(1)类的定义
使用 class 关键字定义类,类名采用大驼峰命名法(如 Dog、PersonInfo)。
python
class Dog:
# 类的属性和方法定义
pass
(2)__init__ 初始化方法
创建对象时自动调用,用于定义和初始化对象的属性。方法的第一个参数必须是 self(代表当前对象)。
python
class Dog:
def __init__(self, name, age):
self.name = name # 定义属性name
self.age = age # 定义属性age
(3)对象的创建
通过类名加括号实例化对象,参数需与 __init__ 方法匹配(无需传入 self)。
python
# 实例化对象
my_dog = Dog("Buddy", 3)
print(my_dog.name) # 访问对象属性,输出:Buddy
3. 方法的定义
类中定义的函数称为方法,第一个参数必须是 self,其他用法与函数一致。
python
class Dog:
def __init__(self, name):
self.name = name
def bark(self): # 定义方法bark
print(f"{self.name} is barking!")
my_dog = Dog("Buddy")
my_dog.bark() # 调用方法,输出:Buddy is barking!
4. 常用魔术方法
魔术方法是 Python 中以双下划线 __ 开头和结尾的特殊方法,用于实现对象的特定行为。
-
__str__:返回对象的字符串表示,便于打印和查看。
必须返回字符串类型,调用str(obj)时自动触发。python
class Dog: def __init__(self, name): self.name = name def __str__(self): return f"Dog(name={self.name})" my_dog = Dog("Buddy") print(my_dog) # 输出:Dog(name=Buddy) -
__eq__:自定义对象的相等性比较(默认比较内存地址)。python
class Dog: def __init__(self, name): self.name = name def __eq__(self, other): return self.name == other.name # 比较名字是否相同 dog1 = Dog("Buddy") dog2 = Dog("Buddy") print(dog1 == dog2) # 输出:True(默认情况下为False) -
__del__:对象被销毁时自动调用(析构方法,较少使用)。
5. 属性私有化
为保证数据安全,可将属性或方法私有化,限制外部直接访问。
- 单下划线
_:受保护的属性 / 方法,仅允许本类和子类使用(君子协议,非强制)。 - 双下划线
__:私有属性 / 方法,仅允许本类使用。外部需通过_类名__属性名访问(不推荐)。
(1)通过 getter 和 setter 访问私有属性
python
class Dog:
def __init__(self, age):
self.__age = age # 私有属性
def get_age(self): # getter方法
return self.__age
def set_age(self, new_age): # setter方法
if new_age > 0:
self.__age = new_age
my_dog = Dog(3)
print(my_dog.get_age()) # 输出:3
my_dog.set_age(5)
print(my_dog.get_age()) # 输出:5
(2)property 装饰器(推荐)
简化 getter 和 setter 的调用,使属性访问更自然。
python
class Dog:
def __init__(self, age):
self.__age = age
@property
def age(self): # 相当于getter
return self.__age
@age.setter
def age(self, new_age): # 相当于setter
if new_age > 0:
self.__age = new_age
my_dog = Dog(3)
print(my_dog.age) # 直接访问,输出:3
my_dog.age = 5 # 直接赋值,输出:5
6. 静态方法与类方法
- 静态方法:与类和对象均无关,用
@staticmethod装饰,无需self参数。 - 类方法:与类相关,用
@classmethod装饰,第一个参数为cls(代表当前类)。
两者均可通过类直接调用(推荐),也可通过对象调用(不推荐)。
python
class Dog:
species = "Canine" # 类属性(所有对象共享)
@staticmethod
def info(): # 静态方法
print("Dogs are mammals.")
@classmethod
def get_species(cls): # 类方法
return cls.species
Dog.info() # 调用静态方法,输出:Dogs are mammals.
print(Dog.get_species()) # 调用类方法,输出:Canine
7. 反射函数
在程序运行时,通过字符串动态操作对象的属性或方法:
hasattr(obj, name):判断对象是否有指定属性 / 方法。getattr(obj, name):获取对象的属性 / 方法。setattr(obj, name, value):设置对象的属性。delattr(obj, name):删除对象的属性。
python
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print("Woof!")
my_dog = Dog("Buddy")
print(hasattr(my_dog, "name")) # 输出:True
print(getattr(my_dog, "name")) # 输出:Buddy
setattr(my_dog, "age", 3) # 动态添加属性age
print(my_dog.age) # 输出:3
三、继承(Inheritance)
继承允许子类继承父类的属性和方法,实现代码复用和扩展。Python 支持多继承(用逗号分隔多个父类)。
1. 基本用法
python
class Animal: # 父类
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name} is eating.")
class Dog(Animal): # 子类继承Animal
def bark(self):
print(f"{self.name} is barking.")
my_dog = Dog("Buddy")
my_dog.eat() # 继承父类方法,输出:Buddy is eating.
my_dog.bark() # 子类自有方法,输出:Buddy is barking.
2. 子类属性初始化
子类初始化时需先初始化父类属性,可通过 super() 调用父类的 __init__ 方法。
python
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 初始化父类属性
self.breed = breed # 子类自有属性
my_dog = Dog("Buddy", "Golden Retriever")
print(my_dog.name, my_dog.breed) # 输出:Buddy Golden Retriever
3. 方法重写
当父类方法不满足子类需求时,子类可重写父类方法,通过 super() 可调用父类原方法。
python
class Animal:
def make_sound(self):
print("Some sound")
class Dog(Animal):
def make_sound(self):
super().make_sound() # 调用父类方法
print("Woof!")
my_dog = Dog()
my_dog.make_sound()
# 输出:
# Some sound
# Woof!
4. 多继承与方法查找顺序
多继承时,若多个父类有同名方法,Python 会按MRO(方法解析顺序) 查找(从左到右,深度优先)。
python
class A:
def func(self):
print("A's func")
class B(A):
pass
class C(A):
def func(self):
print("C's func")
class D(B, C): # 继承B和C
pass
d = D()
d.func() # 输出:C's func(按MRO:D → B → C → A)
5. 抽象类
抽象类是包含抽象方法(仅定义接口,无实现)的类,用于制定标准,且不允许实例化。
需继承 abc.ABC 类,并通过 @abc.abstractmethod 装饰抽象方法。子类必须重写所有抽象方法,否则仍为抽象类。
python
import abc
class Animal(abc.ABC): # 抽象类
@abc.abstractmethod
def make_sound(self): # 抽象方法
pass # 无实现
class Dog(Animal):
def make_sound(self): # 必须重写
print("Woof!")
# animal = Animal() # 报错:无法实例化抽象类
dog = Dog()
dog.make_sound() # 输出:Woof!
四、多态(Polymorphism)
多态指同一方法通过不同对象调用时表现出不同行为,主要通过方法重写实现。Python 天然支持多态(“鸭子类型”:只要对象有对应方法,即可调用,无需显式继承)。
python
class Dog:
def make_sound(self):
print("Woof!")
class Cat:
def make_sound(self):
print("Meow!")
def animal_sound(animal): # 统一接口
animal.make_sound()
dog = Dog()
cat = Cat()
animal_sound(dog) # 输出:Woof!
animal_sound(cat) # 输出:Meow!
五、元类(Metaclass)
元类是类的类,用于控制类的创建过程。所有类的类型都是 type(元类的基类)。
1. 用 type 创建类
type 是 Python 的内置元类,可动态创建类:type(类名, 父类元组, 类属性字典)。
python
# 等价于 class MyClass: pass
MyClass = type("MyClass", (), {})
obj = MyClass()
2. 自定义元类
通过继承 type 自定义元类,重写 __new__ 或 __init__ 方法控制类的创建。
python
class MyMeta(type):
def __new__(cls, name, bases, attrs):
# 在类创建前修改属性(如添加前缀)
new_attrs = {f"my_{k}": v for k, v in attrs.items()}
return super().__new__(cls, name, bases, new_attrs)
class MyClass(metaclass=MyMeta): # 指定元类
x = 10
obj = MyClass()
print(obj.my_x) # 输出:10(属性被自动添加前缀)
总结
面向对象编程通过封装、继承和多态三大特性,实现了代码的模块化、复用性和可维护性,尤其适合复杂系统的开发。理解类与对象的关系、掌握魔术方法、继承机制和多态特性,是灵活运用 OOP 的关键。元类作为进阶概念,可用于深度定制类的行为,进一步拓展 Python 的编程能力。
详解&spm=1001.2101.3001.5002&articleId=149546903&d=1&t=3&u=6f17817bbd664d39bc83a495a78a4e15)
1751

被折叠的 条评论
为什么被折叠?



