Python 面向对象编程详解
核心概念 Python 面向对象编程涵盖类与对象、继承、多态、封装、魔术方法等。
1. 什么是面向对象
面向对象是一种编程思想,把程序看作「对象的集合」,和面向过程(按步骤写函数)对立:
- 面向过程:关注怎么做,拆分步骤、函数;
- 面向对象:关注谁来做,拆分事物(类),封装属性和行为。 两个核心基础概念:
- 类 Class:模板、图纸,定义一类事物共有属性、方法;
- 实例 Instance:根据类创建出来的真实对象,每个实例独立拥有自己的数据。
示例:
Person是类(模板);p1 = Person()是实例(具体的人)
# 定义类
class Person:
pass
# 创建实例
p1 = Person()
p2 = Person()
2. 类的核心组成:属性 + 方法
2.1. 属性(变量)
分为两类:
1)实例属性:每个实例独有,写在 __init__ 构造函数里
2)类属性:属于类本身,所有实例共享,写在类内部最上方
class Person:
# 类属性(全局共享)
species = "人类"
# 构造方法:创建实例自动执行
def __init__(self, name, age):
# 实例属性(每个对象独立)
self.name = name
self.age = age
p1 = Person("张三", 20)
p2 = Person("李四", 22)
print(p1.name) # 张三(实例属性)
print(Person.species) # 人类(类属性)
print(p1.species) # 人类,实例可以读取类属性
# 修改类属性,全部实例同步变化
Person.species = "智人"
print(p2.species) # 智人
2.2. 方法(函数)
方法第一个参数必须是 self,代表当前实例本身。
2.2.1. (1)实例方法(最常用)
只能由实例调用,可读写实例属性、类属性
class Person:
def __init__(self, name):
self.name = name
# 实例方法
def say_hello(self):
print(f"我是{self.name}")
p = Person("小明")
p.say_hello()
2.2.2. (2)类方法 @classmethod
用 @classmethod 装饰,第一个参数 cls 代表类本身,不能访问实例属性,只能操作类属性
class Person:
count = 0
def __init__(self):
Person.count += 1
@classmethod
def get_count(cls):
return cls.count
print(Person.get_count())
2.2.3. (3)静态方法 @staticmethod
@staticmethod,无默认参数,既不能访问实例,也不能访问类,单纯挂靠在类里的普通函数
class Math:
@staticmethod
def add(a, b):
return a + b
print(Math.add(1,2))
2.3. 三种方法面试区分总结
| | | | | | |
|---|---|---|---|---|---|
|类型|装饰器|首参数|访问实例属性|访问类属性|调用方式|
|实例方法|无|self|✅ 可以|✅ 可以|实例.方法()|
|类方法|@classmethod|cls|❌ 不行|✅ 可以|类/实例均可调用|
|静态方法|@staticmethod|无|❌ 不行|❌ 不行|类/实例均可调用|
3. 四大特性(面试必考核心)
3.1. 封装 Encapsulation
把数据和操作数据的方法打包到类内部,隐藏内部细节,对外暴露简单接口。
Python 没有严格私有,靠命名规范区分:
name:公有,外部随便读写_name:单下划线,约定私有,不建议外部直接访问__name:双下划线,名称改写(name mangling),外部无法直接访问
class Person:
def __init__(self, age):
self.__age = age # 私有属性
# 对外提供读取接口
def get_age(self):
return self.__age
# 对外提供修改接口,可增加校验逻辑
def set_age(self, new_age):
if 0 < new_age < 150:
self.__age = new_age
else:
print("年龄非法")
p = Person(20)
print(p.get_age())
p.set_age(200) # 提示非法
好处:控制数据修改逻辑,防止脏数据。
3.2. 继承 Inheritance
子类复用父类代码,还能扩展新功能;语法 class 子类(父类):
# 父类
class Animal:
def eat(self):
print("动物吃东西")
# 子类 Dog 继承 Animal
class Dog(Animal):
def bark(self):
print("汪汪叫")
d = Dog()
d.eat() # 继承父类方法
d.bark() # 自己独有方法
3.2.1. 重写 override
子类和父类同名方法,优先执行子类自己的逻辑:
class Dog(Animal):
def eat(self):
# 先调用父类原有逻辑
super().eat()
print("狗啃骨头")
super() 代表父类对象,用于调用父类构造/方法。
3.2.2. 多重继承
Python 支持一个类同时继承多个父类:class A(B,C)
MRO 查找顺序(面试高频):方法解析顺序,用 类名.__mro__ 查看,从左到右深度优先查找。
3.3. 多态 Polymorphism
同一个方法,不同子类实现不同逻辑,调用时自动匹配对应实现。
核心:只要拥有同名方法,就能统一调用。
class Cat(Animal):
def eat(self):
print("猫吃鱼")
def feed(animal):
animal.eat() # 统一接口,自动适配不同子类
feed(Dog())
feed(Cat())
3.4. 抽象(拓展,中级面试)
用 abc 模块定义抽象基类,强制子类必须实现指定方法,否则无法实例化。
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def eat(self):
pass
# 子类必须重写eat,否则报错
4. 常用魔术方法(Magic Method,面试高频)
以 __xx__ 命名,实例触发对应行为自动执行
__init__(self):构造方法,创建实例时执行,初始化属性__new__(cls):创建实例对象(在__init__之前,单例模式常用)__del__(self):析构方法,对象被销毁时执行__str__(self):print(对象)输出人类可读字符串__repr__(self):开发调试用,精准还原对象__enter__ / __exit__:实现with上下文管理器(文件操作原理)__getitem__:支持obj[0]下标取值__call__:对象可以像函数一样obj()调用 示例:
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Person(name={self.name})"
p = Person("张三")
print(p) # 自动调用 __str__
5. 属性装饰器 @property
把方法伪装成属性,不用加括号调用,兼顾封装与简洁读写
class Person:
def __init__(self, age):
self.__age = age
# 读
@property
def age(self):
return self.__age
# 写
@age.setter
def age(self, val):
if 0 < val < 150:
self.__age = val
p = Person(10)
print(p.age) # 不用括号
p.age = 20
6. 面试高频问答(必背)
self是什么?
代表当前实例对象,调用实例方法时自动传入,用来访问实例属性。- 类属性和实例属性区别?
类属性所有实例共享;实例属性每个对象独立,互不干扰。 super()作用?
调用父类的构造函数或方法,解决多重继承代码复用。- 单下划线
_x、双下划线__x区别?_x约定私有,仅提示;__x触发名称改写,外部无法直接访问。 - 实例方法/类方法/静态方法使用场景?
- 实例方法:需要操作对象自身数据;
- 类方法:只操作类全局数据,不需要实例;
- 静态方法:和类、实例都无关,仅逻辑归类。
- MRO 多重继承查找规则?
按__mro__顺序,从左到右深度优先,保证父类只执行一次。 __str__和__repr__区别?str给用户看;repr给开发者调试,还原对象代码。- 面向对象三大/四大特性?
封装、继承、多态;抽象是扩展特性。 - 什么是多态?好处是什么?
统一调用接口,不同子类不同实现,代码易扩展、解耦。 - 装饰器 @property 作用?
将方法转为属性,简洁读写,同时在赋值时增加数据校验逻辑。
关联文档