
(1)基本概念
面向对象编程(Object-Oriented Programming, OOP)是一种编程范式,Python完全支持OOP。其中,类(class)是创建对象(object)的蓝图或模板,定义了对象的属性(Attributes)和方法(Methods);对象是类的实例,具有类定义的属性和方法;属性是对象存储的数据,方法是对象可以执行的操作(类中定义的函数)。
Python中的整型、浮点型、字符串等都是预先定义好的内置类,还可以自定义类。在下面的代码中,狗就是一个类,某一个狗就是一个对象。此外,在Python3中定义类时可以不添加(object)
,例如class Dog(object):
,定义一个类时,不加object,称为经典类,加了object,称为新式类,但是新的Python版本已经不区分经典类和新式类。
python
# 定义一个类 Dog
class Dog:
pass
# 创建一个对象(类的实例化)
mg_dog = Dog()
类有一个名为 init () 的特殊方法(构造函数),该方法在类实例化时会自动调用,在类的方法中必须有一个额外的第一个参数,名称为self,self用来代表类的实例,并不是类本身。以下示例创建了Dog的实例属性:
python
class Dog:
# 初始化方法(构造函数)
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
# 创建一个对象(类的实例化)
my_dog = Dog("旺财", 2)
# 调用
print(f"{my_dog.name}汪汪叫!")
# __dict__方法,以字典形式展示所有属性和对应的值
print(my_dog.__dict__)
除了实例属性外,还有类属性。类属性不属于某一个实例,是属于所有的实例共享的属性。例如在上面示例中添加一个类属性,表明狗是犬科。
python
class Dog:
Family = "犬科" # 类属性
def __init__(self, name, age, family):
if family != self.Family: # 检查传入的family参数,更加直接的方法是使用 Dog.Family
raise ValueError(f"狗的科属应该是'{self.Family}',你输入的是'{family}'")
self.name = name
self.age = age
# 正确创建
my_dog = Dog("旺财", 3, "犬科")
# 错误创建会报错
try:
wrong_dog = Dog("咪咪", 2, "猫科")
except ValueError as e:
print(f"错误:{e}")
在上述代码中,使用的 self.Family
访问类属性,这里涉及到 Python 的 属性查找顺序(Attribute Lookup Order) 。如果 self 有 Family 实例属性,则返回它;如果没有,继续查找类属性;Python 的实例会自动继承类属性,如果类属性存在,则返回它。虽然 self.Family
可以访问类属性,但更清晰的写法是 直接通过类名访问 ,使用Dog.Family
。
(2)实例方法、类方法、静态方法
在 Python 面向对象编程中,有三种主要的方法类型:实例方法、类方法和静态方法。它们在使用方式和应用场景上有重要区别。其中,实例方法是一个普通的函数,类方法和静态方法都是通过函数装饰器的方式实现的;实例方法需要传入self,类方法需要传入cls参数,静态方法无需传入self参数或者是cls参数,但不等同于不能传入参数。
实例方法 的第一个参数必须是 self
,表示类的实例,可以访问和修改实例属性和类属性,而大多数常规方法都是实例方法。例如将上面的代码修改:
python
class Dog:
# 初始化方法(构造函数)
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
# 实例方法
def jiao(self):
print(f"{self.name}汪汪叫!")
myDog = Dog('旺旺', 3)
# 调用实例方法
myDog.jiao()
类方法 是操作类属性的方法,使用@classmethod
装饰器,第一个参数必须是 cls
,表示类本身;可以访问和修改类属性,但不能访问实例属性,可以被类和实例调用。
类方法的核心优势:无需创建实例即可调用,并能返回类的新实例,非常适合实现工厂模式或多种构造逻辑。示例如下:
python
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def jiao(self):
print(f"{self.name}汪汪叫!")
# 添加类方法:创建导盲犬
@classmethod
def create_guide_dog(cls):
return cls(name="导盲犬大黑", age=4) # 使用cls创建新实例
# 直接通过类名调用类方法,无需创建实例
guide_dog = Dog.create_guide_dog()
# 测试类方法创建的对象
print(f"{guide_dog.name} 年龄: {guide_dog.age}岁") # 输出:导盲犬小Q 年龄: 2岁
guide_dog.jiao() # 输出:导盲犬小Q汪汪叫!
# 原始实例仍然可用,但是要先创建实例,再调用实例方法
myDog = Dog('旺旺', 3)
myDog.jiao() # 输出:旺旺汪汪叫!
在上面的例子中,没有使用任何实例,而是直接通过Dog
类调用了create_guide_dog
方法。但是,类方法内部通常会创建一个类的实例(如上面例子中返回了cls(name="导盲犬小Q", age=2)
),或者进行与类相关的操作(不依赖实例状态)。所以调用类方法的目的往往是为了创建实例或者处理类级别的数据。
静态方法 (Static Method)使用 @staticmethod 装饰器定义,不能访问类属性或实例属性,只是属于类的命名空间。
python
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
# 静态方法示例
@staticmethod
def human_to_dog_years(human_years):
# 尝试在静态方法中访问实例属性 会失败,例如下一句注释掉的内容:
# print(f"尝试访问实例属性: {self.name}的年龄是{self.age}岁")
"""将人类年龄转换为狗狗年龄(静态方法)"""
# 狗狗年龄转换公式:前两年每年相当于人类10.5年,之后每年相当于人类4年
if human_years <= 0:
return 0
elif human_years <= 2:
return human_years * 10.5
else:
return 21 + (human_years - 2) * 4
human_age = 5
dog_age = Dog.human_to_dog_years(human_age)
print(f"人类 {human_age} 岁相当于狗狗的 {dog_age:.1f} 岁")
(3)面向对象编程基本特征
在Python中,面向对象编程(OOP)的基本特性是:封装、继承、多态 ,此外,有的说是四大基本特征,还包括了抽象。
特性 | 核心思想 | Python关键实现 |
---|---|---|
封装 | 隐藏内部细节,保护数据 | __私有变量 、公有方法 |
继承 | 代码复用,层次化扩展 | class Child(Parent): |
多态 | 同一接口,不同行为 | 鸭子类型、方法重写 |
抽象 | 定义规范,分离接口与实现 | abc.ABC + @abstractmethod |
继承 (Inheritance)
继承是子类继承父类的属性和方法,实现代码复用和层次化扩展;支持单继承和多层继承。例如:定义一个新的类JinMao,继承Dog类的属性,创建后的实例myJM即是Dog类又是JinMao类,也能使用父类的方法等。但是往往子类有自己独特的特征,会再重写函数。
python
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
class JinMao(Dog):
pass
myJM=JinMao("小金",5)
print(myJM.__dict__)
# 打印查看类型
print(type(myJM))
多态 (Polymorphism)
同一操作作用于不同对象时,都能够正常处理,可产生不同的行为;通过方法重写和接口统一实现。
python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("子类必须实现此方法")
class Dog(Animal):
def speak(self): # 重写父类方法
return "汪汪!"
class Cat(Animal):
def speak(self):
return "喵喵!"
dog1 = Dog("阿黄")
cat1 = Cat("咪咪")
# 多态实现,不同的类不同的输出
def animal_sound(animal):
print(animal.speak()) # 只要对象有speak()方法即可运行
animal_sound(dog1) # 输出: 汪汪!
animal_sound(cat1) # 输出: 喵喵!
封装 (Encapsulation)
将数据(属性)和操作数据的方法(行为)捆绑在一个单元(类)中,并隐藏内部实现细节;通过访问控制(如私有变量)保护数据完整性。
- 使用
_
(单下划线)表示受保护成员(约定俗成,非强制) - 使用
__
(双下划线)表示私有成员(名称修饰,实际变为_类名__变量名
)
简单的示例程序如下所示,其中使用单下划线保护了变量_name,使用双下划线将age设置为私有变量。针对被封装起来的变量,如果要修改等是需要使用装饰器,并且将变量名(函数名)设置为相同,例如下面代码中使用装饰器@property
和 @age.setter
定义了两个函数age来获取变量和修改变量。
python
class User(object):
def __init__(self,name,age):
self._name = name # 受保护的变量
self.__age = age # 私有变量
'''把函数当做变量去使用:
@property
def 变量名() #获取变量
@age.setter
def 变量名() #修改变量
'''
@property # 获取变量
def age(self):
return self.__age
@age.setter # 变量的修改器
def age(self,age):
if isinstance(age,int):
self.__age = age
else:
raise Exception('年龄只能是整数')
def show_infos(self):
print('大家好,我是%s,我今年%d' %(self._name,self.__age))
mia = User('mia',24)
'''
print(mia.age)
mia.age=25
print(mia.age)
'''
print(mia.age)
mia.age = '二十五'
print(mia.age)
抽象 (Abstraction)
抽象:隐藏复杂实现细节,仅暴露必要接口;通过抽象类定义规范,强制子类实现特定方法。Python中抽象的实现要通过abc模块,并且通过装饰器,将类变成抽象类,抽象类不能被直接实例化,要想使用抽象类,必须继承该类并实现该类的所有抽象方法。
python
from abc import ABC, abstractmethod
class Shape(ABC): # 抽象基类
@abstractmethod
def area(self): # 抽象方法
pass
class Circle(Shape):#圆形继承了Shape类
def __init__(self, radius): #添加了半径参数
self.radius = radius
def area(self): # 继承抽象类,必须实现所有的抽象方法
return 3.1415926 * self.radius ** 2
# 使用
circle = Circle(5)
print("%.2f" % circle.area()) # 输出: 78.54
# shape = Shape() # 报错:不能实例化抽象类
(4)魔法方法
在 Python 中,魔法方法是指那些以双下划线开头和结尾的特殊方法;它们是 Python 的内置方法,由 Python 解释器在特定场景下自动调用,对应于 Python 对象的各种运算符,在一定程度上其实就是类似于封装的理念,例如在用str()
时会自动调用__str__()
函数。这些方法让自定义类可以像内置类型一样工作,是 Python 面向对象编程的核心特性之一。
python
class User(object):
def __init__(self,name): # 构造函数,最常见魔法函数
print('__init__被调用')
self.name = name
def __str__(self): #重写str函数
return '我的名字是%s'%self.name
def __add__(self, other): #调用+时,执行__add__方法
return self.name + other.name
def __eq__(self, other): #判断是否相等时,调用
return self.name == other.name
mia = User('mia')
print(str(mia)) #调用重写的__str__
print(mia) #print也是默认执行了__str__方法
print(1+3)
print('hi '+'mia')
jack = User('mia')
print(mia+jack)
print(6==7)
print(mia==jack)
部分常用的魔法方法如下:
方法 | 作用 | 触发场景 |
---|---|---|
__init__ |
对象初始化 | obj = Class() |
__str__ / __repr__ |
字符串表示 | print(obj) / repr(obj) |
__len__ |
获取长度 | len(obj) |
__getitem__ |
索引/切片访问 | obj[key] |
__add__ / __eq__ |
运算符重载 | obj1 + obj2 / obj1 == obj2 |
__enter__ / __exit__ |
上下文管理 | with obj as x: |
__call__ |
对象作为函数调用 | obj(...) |