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(...)
相关推荐
程序员小远几秒前
Pytest+Selenium UI自动化测试实战实例
自动化测试·软件测试·python·selenium·测试工具·ui·pytest
景天科技苑13 分钟前
【Rust线程池】如何构建Rust线程池、Rayon线程池用法详细解析
开发语言·后端·rust·线程池·rayon·rust线程池·rayon线程池
桃白白大人14 分钟前
今日Github热门仓库推荐 第八期
人工智能·python·github
~央千澈~17 分钟前
Go、Node.js、Python、PHP、Java五种语言的直播推流RTMP协议技术实施方案和思路-优雅草卓伊凡
java·python·go·node
液态不合群22 分钟前
JavaScript 编年史:探索前端界巨变的幕后推手
开发语言·前端·javascript
荼蘼1 小时前
用Python玩转数据:Pandas库实战指南(二)
开发语言·python·pandas
油丶酸萝卜别吃1 小时前
JS深度克隆对象(克隆时包含函数)
开发语言·javascript·ecmascript
牛客企业服务1 小时前
AI面试与传统面试的核心差异解析——AI面试如何提升秋招效率?
java·大数据·人工智能·python·面试·职场和发展·金融
你我约定有三1 小时前
RabbitMQ--@RabbitListener及@RabbitHandle
java·开发语言·后端·rabbitmq
leese2331 小时前
docker操作
java·开发语言