AI项目博客
记录 AI 工程实践与大模型应用

Python 学习笔记 17|面向对象 · 权限控制

6. 权限控制

6.1. 三种访问权限

在 Python 中,我们可以给属性赋予三种权限,分别是:公有属性、受保护属性、私有属性。

下面是详细梳理👇

权限类型 定义方式 在当前类内部访问 在子类内部访问 在类外部访问
公有属性 属性名 ✅ 能 ✅ 能 ✅ 能
受保护属性 _属性名 ✅ 能 ✅ 能 ⚠️ 能(但不推荐)
私有属性 __属性名 ✅ 能 ❌ 不能 ❌ 不能

测试『类外部』访问:

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}")

p1 = Person('张三', 18, '110101199001011234')
print(p1.name)

# 类的外部,仅能访问公有属性
# 注意:如果在类的外部,强制访问【受保护的属性】,也能访问,但最好别这么做
print(p1._age)

# 强制访问【私有属性】会报错
# print(p1.__idcard)

# 扩展:Python保护【私有属性】的方式,是重命名,例如:__idcard属性,会被重命名为:_Person__idcard
print(p1.__dict__)
print(p1._Person__idcard)

6.2. getter 与 setter

在面向对象编程中,我们会把一些内部数据保护起来,但同时还想提供一个“安全的通道”让外部访问。这时候我们就用到:

  • getter:读取属性的方法
  • setter:修改属性的方法

在 Python 中:通过@property和@xxx.setter语法,把普通方法变成像属性一样使用的方法。

class Person:
    def __init__(self, name, age, idcard):
        self.name = name        # 公有属性:当前类内部、子类内部、类外部,可都可访问
        self._age = age         # 受保护属性:当前类内部、子类内部,可以访问
        self.__idcard = idcard  # 私有属性:仅能在当前类内部访问

    # 注册 age 属性的 getter 方法:当访问 Person 实例的 age 属性时,age方法会自动调用
    @property
    def age(self):
        return self._age

    # 注册 age 属性的 setter 方法:当给 Person 实例的 age 属性赋值时,age方法会自动调用
    @age.setter
    def age(self, value):
        if value <= 120:
            self._age = value
        else:
            print('年龄非法,已将年龄变为最大值120')
            self._age = 120

    # 注册 idcard 属性的 getter 方法:当访问 Person 实例的 idcard 属性时,会自动调用此方法
    @property
    def idcard(self):
        return self.__idcard[:6] + '********' + self.__idcard[-4:]

    # 注册 idcard 属性的 setter 方法,当 idcard 被修改时调用,内部会禁止修改身份证号并给出提示。
    @idcard.setter
    def idcard(self, value):
        print("抱歉,身份证号不允许随意修改,如有特殊需求,请联系管理员!")

p1 = Person('张三', 18, '110101199001011234')
print(p1.age)
print(p1.idcard)

# 测试修改age
p1._age = 19
print(p1.age)

# 测试修改idcard
p1.idcard = 'asd'

7. 魔法方法

  • 概念:以 __xxx__ 命名的特殊方法(双下划线开头和结尾)。
  • 特点:不需要手动调,在特定场景由 Python 自动调用。

几个常用的魔法方法:

方法 调用时机
1️⃣__str__ 当调用 print(对象)或str(对象) 时
2️⃣__len__ 当调用len(对象)时
3️⃣__lt__ 当执行对象1 < 对象2时
4️⃣__gt__ 当执行对象1 > 对象2时
5️⃣__eq__ 当执行对象1 == 对象2时
6️⃣__getattr__ 当访问不存在的属性时
# 类中以双下划线开头和结尾的方法,叫魔法方法(魔术方法)。
# 魔法方法不需要手动调用,Python会在特定场景自动调用。
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    # __str__ 方法,执行时机:当调用 print(对象)或str(对象) 时
    def __str__(self):
        return f'姓名:{self.name}, 年龄:{self.age}, 性别:{self.gender}'

    # __len__ 方法,执行时机:当调用len(对象)时
    def __len__(self):
        return len(self.__dict__)

    # __lt__方法,执行时机:当执行 对象1 < 对象2 时
    def __lt__(self, other):
        return self.age < other.age

    # __gt__方法,执行时机:当执行 对象1 > 对象2 时
    def __gt__(self, other):
        return self.age > other.age

    # __eq__方法,执行时机:当执行 对象1 == 对象2 时
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    # __getattr__方法,执行时机:当访问了对象不存在的属性时
    def __getattr__(self, item):
        print(f'您访问的{item}属性,不存在!')

# 创建Person实例
p1 = Person('张三', 18, '男')
p2 = Person('李四', 22, '女')

# 测试代码
print(p1)
print(str(p1))
print(len(p1))
print(p1 < p2)
print(p1 > p2)
print(p1 == p2)
print(p1.address)

8. object 类

  • object是所有类的最终祖先,所有的类,无论写与不写,都继承自object。
  • object提供了所有对象都会有的一组最基本方法,例如:
方法名 作用简述
__init__(self) 在对实例对象初始化时调用
__str__(self) print(obj)或 int(obj)时调用
__class__ 返回对象所属的类
……

备注:上述这些方法,如果我们不去重写,Python 会自动继承并使用默认版本。

# Python 中,所有的类都继承了 object 类,即:object 类是所有类的顶层父类。
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

# 验证一下:所有的类都继承了 object 类
print(issubclass(Person, object))
print(issubclass(int, object))
print(issubclass(str, object))
print(issubclass(list, object))
print(issubclass(tuple, object))
print(issubclass(bool, object))

# 因为 object 是所有类的父类,所以 Python 中的所有对象,都间接是 object 类的实例。
p1 = Person('张三', 18, '男')
print(isinstance(p1, object))
print(isinstance(100, object))
print(isinstance('hello', object))
print(isinstance(True, object))
print(isinstance(None, object))
print(isinstance([10, 20, 30], object))
print(isinstance({'吃饭','睡觉'}, object))

# 所有对象都继承了 object 类所提供的:各种属性和方法,从而保证每个对象都具备统一的基本能力。
for key in object.__dict__:
    print(key)

p1 = Person('张三', 18, '男')
print(p1.__dict__)  # 打印对象自己身上的东西
print(dir(p1))      # 打印对象能访问到的东西

print(p1.__str__())
print(p1)

9. 多态

9.1. 何为多态

多态就是“多种形态”,即:不同的对象调使用同一个方法名调用方法时,会表现出不同的行为。

9.2. 标准多态

  • 基于继承实现多态,又叫:『传统多态』或『标准多态』。
  • 常见于Java、C++ 这样的强类型语言中,Python支持『标准多态』。
# 多态是指:同一个方法名,在不同的对象上调用时,能呈现出不同的行为。
# Python中支持:标准多态、鸭子多态

# 标准多态:
class Animal:
    def speak(self):
        print('动物正在发出声音')

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

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

# 注意Pig类没有继承Animal类
class Pig:
    def speak(self):
        print('哼哼哼!')

# make_sound函数要求:传入的对象,必须是 Animal 类型(或其子类型),才能保证可以调用到sepak方法
def make_sound(animal:Animal):
    animal.speak()

# 创建实例对象
a1 = Animal()
d1 = Dog()
c1 = Cat()

# 多态的体现:同一函数,不同对象 → 不同行为
make_sound(a1)  # 动物正在发出声音
make_sound(d1)  # 汪汪汪!
make_sound(c1)  # 喵喵喵!

# 按标准多态规则:Pig 没有继承 Animal,类型不匹配(会出现类型警告)
p1 = Pig()
make_sound(p1)  # 在其它语言中会报错,虽然 Python 中能运行,但这不属于标准多态

9.3. 鸭子多态

鸭子类型:如果一个东西看起来像鸭子,叫起来也像鸭子,那它就是鸭子,鸭子类型指一种编程风格,它并不依靠查找对象类型,来确定其是否具有正确的实现,而是直接调用或使用其方法或属性。

官方文档地址:https://docs.python.org/zh-cn/3.13/glossary.html#term-duck-typing

  • 特点:不需要继承,只要传进来的对象,有对应实现就可以。
  • Python 中支持“鸭子多态”。
# 核心理念:如果一个东西看起来像鸭子,叫起来也像鸭子,那它就是鸭子。
# 鸭子类型是一种编程风格,它不检查对象的类型,只关注对象能否“做某件事”(是否有对应的方法)。

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

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

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

class Fish:
    def speak(self):
        print('咕噜噜!')

# 不再对animal的类型做限制,animal可以是任何类型,只要能调用speak方法就可以
def make_sound(animal):
    animal.speak()

# 创建实例对象
d1 = Dog()
c1 = Cat()
p1 = Pig()
f1 = Fish()

make_sound(d1)
make_sound(c1)
make_sound(p1)
make_sound(f1)

10. 抽象类

  • 概念:抽象类(Abstract Class) 是一种 不能被直接实例化 的类,通常作为“规范”,让子类去继承并实现其中定义的抽象方法,本身只定义规范,不需要提供完整实现。
  • 例如:动物会叫、飞行器会飞、支付方式会支付。
from abc import ABC, abstractmethod

#【抽象类】是一种不能直接实例化的类,它通常作为“规范”,让子类去继承,并实现其中定义的【抽象方法】。

# MustRun类一旦继承了ABC类,那MustRun类就是【抽象类】了
class MustRun(ABC):
    # run方法一旦被@abstractmethod装饰后,就变成了【抽象方法】
    @abstractmethod
    def run(self):
        pass

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

    def run(self):
        print(f'我叫{self.name},我在努力的奔跑!')

p1 = Person('张三', 18, '男')
p1.run()

11. 小练习

完成一个学生成绩管理小案例

具体代码如下:

from datetime import datetime

# 定义Person类
class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

class Student(Person):
    # 计数器
    count = 0

    def __init__(self, name, age, gender):
        super().__init__(name, age, gender)
        Student.count += 1
        # 给每个学生添加stu_id属性,格式为:年份-序号,序号靠计数器增长。
        self.stu_id = f'{datetime.now().year}{Student.count:03d}'
        # 给学生添加成绩,格式为: {'数学':90, '语文':80, '英语':70}
        self.scores = {}

    # 给当前学生添加成绩
    def add_score(self, subject, score):
        # 给指定学生添加成绩,subject是学科,score是成绩
        self.scores[subject] = score

    # 计算平均分
    def calcu_avg(self):
        if self.scores:
            # 计算平均成绩
            return sum(self.scores.values()) / len(self.scores)
        else:
            return 0

    # 魔法方法
    def __str__(self):
        return f'{self.name}({self.age}-{self.gender}),成绩:{self.scores},平均分:{self.calcu_avg():.1f}'

class Manager:
    def __init__(self):
        self.stu_list = []

    # 添加学生
    def add_student(self):
        name = input('请输入姓名:')
        age = int(input('请输入年龄:'))
        gender = input('请输入性别:')
        # 创建学生实例对象
        stu = Student(name, age, gender)
        # 将当前学生添加到stu_list列表中
        self.stu_list.append(stu)
        print(f'添加成功!学号是:{stu.stu_id}')

    # 删除学生
    def del_student(self):
        sid = input('请输入学号:')
        # target用于保存要删除的学生
        target = None
        # 遍历所有学生,找到要删除的学生,并交给target变量
        for stu in self.stu_list:
            if stu.stu_id == sid:
                target = stu
        # 如果找到了要删除的学生,就调用remove方法移除该学生
        if target:
            self.stu_list.remove(target)
            print('删除成功!')
        # 如果未找到要删除的学生
        else:
            print('学号有误,删除失败!')

    # 展示所有学生
    def show_all_student(self):
        # 如果当前stu_list中有学生,就遍历展示
        if self.stu_list:
            for stu in self.stu_list:
                print(stu)
        # 如果当前stu_list中没有学生,就打印:暂无学生!
        else:
            print('暂无学生!')

    # 给指定学生设置成绩
    def set_score(self):
        sid = input('请输入学号:')
        # 遍历stu_list列表
        for stu in self.stu_list:
            # 如果当前学生学号,与输入的sid相等
            if stu.stu_id == sid:
                # 输入成绩字符串,格式为:学科-分数,学科-分数
                score_str = input('清输入成绩(学科-分数,学科-分数)')
                # 将输入的多个成绩,按照逗号拆分,形成成绩列表
                score_list = score_str.replace(',', ',').split(',')
                # 循环成绩列表,依次添加成绩
                for item in score_list:
                    # 获取科目与成绩
                    subject, score = item.split('-')
                    subject = subject.strip()
                    score = float(score.strip())
                    # 调用add_score方法,添加科目,成绩
                    stu.add_score(subject, score)
                print('添加成功!')
                # 结束循环,同时结束set_score函数
                return
        # 若程序能执行到此处,证明在stu_list中没有找到与sid对应的学生
        print('学号有误!')

    # 提供主菜单
    def run(self):
        while True:
            print('************学生管理************')
            print('1. 添加学生')
            print('2. 删除学生')
            print('3. 查看所有学生')
            print('4. 录入成绩')
            print('5. 退出')

            chocie = input('请输入操作编号:')
            if chocie == '1':
                self.add_student()
            elif chocie == '2':
                self.del_student()
            elif chocie == '3':
                self.show_all_student()
            elif chocie == '4':
                self.set_score()
            elif chocie == '5':
                print('再见!')
                break
            else:
                print('输入有误!')

m1 = Manager()
m1.run()

12. 内存分析

内存分为两个部分:栈内存、堆内存;变量在栈内存中,对象在堆内存中。

备注:强烈建议各位参考视频教程学习本小节

1️⃣Python 中变量里保存的不是存数据,而是指向堆中对象的引用(内存地址)。

2️⃣不可变对象:重新赋值会创建新对象

int 类的实例对象,是不可变对象,所以修改变量 a 时,会创建新对象,不会影响其他引用(b)

Python 中常见的不可变对象有:int 、float 、bool 、str 、tuple 、frozenset 、None。

Python 中常见的可变对象有:list 、dict 、set 、自定义类的实例对象。

3️⃣ 可变对象:修改内容不改变地址

4️⃣自定义类对象的内存表示

赞(0)
未经允许不得转载:AI项目博客 » Python 学习笔记 17|面向对象 · 权限控制

评论 抢沙发

AI项目博客

专注 AI 工程化落地 —— 覆盖智能体、大模型、Java AI 与 Python 的学习笔记,以及 AI 平台的项目实战记录。

关于本站文章归档

登录

找回密码

注册