Python基础6:面向对象编程

(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(...)
相关推荐
固定资产管理系统软件9 分钟前
商务局RFID固定资产管理系统的应用价值与落地要点解析
大数据·python
码农颜30 分钟前
6.3 主函数流程剖析
开发语言·php
研☆香1 小时前
聊一聊前端的常见字 关键字
开发语言·前端·javascript
lancyu1 小时前
关于多轮对话机器人的上下文Token优化和解决方案
人工智能·python·深度学习·机器学习·chatgpt·机器人·prompt
草莓橙子碗2 小时前
Claude 使用指南
开发语言·python
工具分享2 小时前
陪跑跟品爆单AI选品助手,高效跟品会赔本吗?
人工智能·python
COOLMO研究AI2 小时前
Python 如何实现 AI API 的提示词(Prompt)版本管理与热更新
人工智能·python·prompt
老郑聊AI业财智造2 小时前
Transformer 技术架构与源码分析
人工智能·python·深度学习·语言模型·架构·transformer·软件工程
longxiaozhang62 小时前
C# Array类:数组其实没那么难
开发语言·c#
程序员三藏2 小时前
自动化测试用例编写详解
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例