Python面向对象高级特性:权限控制、属性管理、魔法方法、多态与抽象类

在Python中,面向对象编程(OOP)提供了丰富的特性,使得代码更加模块化、可维护和可扩展。本文将深入探讨Python中的权限控制属性访问控制(getter/setter)魔法方法object类多态(标准多态与鸭子类型) 以及抽象类。通过理论讲解和代码实例,帮助您掌握这些高级特性。


一、权限控制:公有、受保护与私有属性

Python通过命名约定来区分属性的访问权限,但这并非严格的强制机制,而是一种约定。

权限类型 命名方式 访问范围 示例
公有属性 name 任何地方均可访问(类内部、子类、外部) self.name
受保护属性 _age 类内部和子类内部可访问(外部不建议访问) self._age
私有属性 __idcard 仅当前类内部可访问(外部不能直接访问) self.__idcard

代码示例:

class Person:
    def __init__(self, name, age, idcard):
        self.name = name          # 公有属性
        self._age = age           # 受保护属性
        self.__idcard = idcard    # 私有属性

    def speak(self):
        # 类内部可以访问所有属性
        print(f'我是{self.name}, 今年{self._age},身份证{self.__idcard}')

class Student(Person):
    def hello(self):
        # 子类内部可以访问公有和受保护属性,但不能访问私有属性
        print(f'我是{self.name}, 今年{self._age},身份证{self.__idcard}')  # 这里会报错

p1 = Person('张三', 19, '123456789012345')
p1.speak()                      # 输出:我是张三, 今年19,身份证123456789012345

# 外部访问
print(p1.name)                  # 公有属性 -> 正常
print(p1._age)                  # 受保护属性 -> 可访问但不建议
# print(p1.__idcard)            # 私有属性 -> 报错 AttributeError

print(p1.__dict__)              # 查看实例属性字典,私有属性被重命名为 _Person__idcard

注意:私有属性实际上是通过名称修饰(name mangling)实现的,会被重命名为 _类名__属性名,因此外部仍可通过 p1._Person__idcard 访问,但这违背了封装原则,不推荐使用。


二、属性访问控制:getter和setter

为了对属性的读取和修改进行控制(如添加校验逻辑),可以使用 @property 装饰器实现getter和setter。

代码示例:

python

class Person:
    def __init__(self, name, age, idcard):
        self.name = name
        self._age = age
        self.__idcard = idcard

    # age属性的getter
    @property
    def age(self):
        return self._age

    # age属性的setter(添加范围校验)
    @age.setter
    def age(self, a):
        if a <= 120:
            self._age = a
        else:
            print('年龄不合适')

    # idcard属性的getter(返回脱敏后的身份证)
    @property
    def idcard(self):
        return self.__idcard[:6] + '*******' + self.__idcard[-4:]

    # idcard属性的setter
    @idcard.setter
    def idcard(self, idcard):
        self.__idcard = idcard

p1 = Person('李四', 18, '1402456789123')
print(p1.age)          # 通过getter获取 -> 18
p1.age = 21            # 通过setter修改
print(p1.age)          # 21

print(p1.idcard)       # 脱敏显示 -> 140245*******9123
p1.idcard = '1402456789111'
print(p1.idcard)       # 140245*******9111

使用getter/setter可以在属性访问时添加业务逻辑,而不需要修改外部调用代码。


三、魔法方法

魔法方法(特殊方法)是以双下划线开头和结尾的方法,由Python在特定情况下自动调用。以下是常用魔法方法的总结:

魔法方法 触发时机 示例用途
__init__ 创建对象时 初始化属性
__str__ print(对象)str(对象) 返回用户友好的字符串
__len__ len(对象) 返回对象的长度
__lt__ 对象1 < 对象2 定义小于比较逻辑
__gt__ 对象1 > 对象2 定义大于比较逻辑
__eq__ 对象1 == 对象2 定义相等比较逻辑
__getattr__ 访问不存在的属性时 提供默认行为或抛出提示

代码示例:

python

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f'{self.name} {self.age}'

    def __len__(self):
        return len(self.__dict__)   # 返回实例属性个数

    def __lt__(self, other):
        return self.age < other.age

    def __gt__(self, other):
        return self.age > other.age

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    def __getattr__(self, item):
        print(f'你访问的 {item} 不存在')

p1 = Person('王五', 20)
p2 = Person('赵六', 19)

print(p1)           # 调用 __str__ -> 王五 20
print(p1 > p2)      # 调用 __gt__ -> True
print(p1 < p2)      # 调用 __lt__ -> False
print(p1 == p2)     # 调用 __eq__ -> False
print(p1.height)    # 访问不存在的属性 -> 触发 __getattr__,输出提示

四、object类:所有类的最终祖先

在Python中,每个类都隐式或显式地继承自 objectobject 是所有类的基类,提供了许多默认实现的方法。

python

import types

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

print(issubclass(Person, object))   # True
print(issubclass(int, object))      # True
print(isinstance(Person(), object)) # True

# 查看 object 类的所有成员
for key in object.__dict__:
    print(key)

所有内置类型(int、str、list等)也都是 object 的子类,因此Python中任何对象都是 object 的实例。


五、多态

多态允许不同的对象调用同一个方法,表现出不同的行为。Python支持两种多态形式:标准多态(继承关系中的方法重写)和鸭子类型多态(不关注类型,只关注行为)。

5.1 标准多态

python

class Animal:
    def speak(self):
        print('动物正在发出声音')

class Dog(Animal):
    def speak(self):
        print('汪汪汪')

class Cat(Animal):
    def speak(self):
        print('喵喵喵')

def make_sound(animal: Animal):
    animal.speak()

a1 = Animal()
a2 = Dog()
make_sound(a1)   # 动物正在发出声音
make_sound(a2)   # 汪汪汪

5.2 鸭子类型多态

鸭子类型不关心对象的具体类型,只关心它是否有某个方法或属性。“如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。”

python

class Pig:
    def speak(self):
        print('哼哼哼')

def make_sound(animal):
    # 不限制类型,只要能调用 speak() 方法即可
    animal.speak()

p = Pig()
make_sound(p)   # 哼哼哼

只要传入的对象有 speak() 方法,就能正常工作,这就是鸭子类型的灵活性。


六、抽象类

抽象类用于定义规范,不能被实例化,通常作为父类让子类继承,并强制子类实现抽象方法。Python中通过 abc 模块实现。

关键元素 作用
ABC 抽象类的基类
@abstractmethod 装饰器,标记抽象方法,子类必须实现

代码示例:

python

from abc import ABC, abstractmethod

class MustRun(ABC):
    @abstractmethod
    def run(self):
        """子类必须实现 run 方法"""
        pass

# m = MustRun()   # 抽象类不能实例化,会报错

class Person(MustRun):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print(f'{self.name} 正在跑步,年龄 {self.age}')

p = Person('小明', 18)
p.run()   # 小明 正在跑步,年龄 18

如果子类没有实现抽象方法,则实例化时会抛出 TypeError


七、总结

本文详细介绍了Python面向对象的高级特性:

  • 权限控制:通过命名约定实现公有、受保护、私有属性,保障封装性。

  • 属性访问控制:使用 @property 和 setter 对属性进行精细控制。

  • 魔法方法:借助特殊方法让对象支持内置操作(字符串表示、比较、长度等)。

  • object类:所有类的根,提供基础功能。

  • 多态:标准多态利用继承重写,鸭子类型强调行为而非类型。

  • 抽象类:通过 ABC 和 @abstractmethod 定义接口规范。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐