Python面向对象类继承中发生的私有属性访问错误问题

2023-11-19

按照Python100days项目中的该方法来访问私有属性,可正常访问到:

class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    test._Test__bar()
    print(test._Test__foo)


if __name__ == "__main__":
    main()

但是,当下面的项目中使用objectname._classname__attributionname的方式就访问不到对象u = Ultraman('骆昊', 1000, 120)的私有属性__name了。代码中使用了print('name: ', u._Ultraman__name),出现AttributeError: _Ultraman__name

"""
"""
@author: Zhangsan
@date: 2023/4/3 17:31
@file: day08_eg1.py
@Software: Pycharm
"""
# !/usr/bin/env python3
# coding=utf-8

from abc import ABCMeta, abstractmethod
from random import randint, randrange

class Fighter(object, metaclass=ABCMeta):

    __slots__ = ('_name', '_hp')

    def __init__(self, name, hp):
        self._name = name
        self._hp = hp

    @property
    def name(self):
        return self._name

    @property
    def hp(self):
        return self._hp

    @hp.setter
    def hp(self, hp):
        self._hp = hp if hp >= 0 else 0

    @property
    def alive(self):
        return self._hp > 0

    @abstractmethod
    def attack(self, other):
        pass


class Ultraman(Fighter):

    __slots__ = ('_name', '_hp', '_mp')

    def __init__(self, name, hp, mp):
        """

        :param name:
        :param hp: 生命值
        :param mp: 魔法值
        """
        super().__init__(name, hp)
        self._mp = mp

    def attack(self, other):
        other.hp -= randint(15, 25)  #zh:为什么是“other.hp”,而不是“other._hp”  # 是不是因为在对象的外部访问不到私有属性,使用使用getter和setter方法
        print('other._hp: ', other._hp)

    def huge_attack(self, other):
        """若自身魔法值充足(大于50),则至少打掉对方50点或四分之三的血量

        :param other: 被攻击的对象
        :return: 使用成功,返回True;否则,返回False
        """
        if self._mp >= 50:
            self._mp -= 50
            injury = other.hp * 3 // 4
            injury = injury if injury >= 50 else 50
            other.hp -= injury
            return True
        else:
            self.attack(other)
            return False
    def magic_attack(self, others):
        if self._mp >= 20:
            self._mp -= 20
            for temp in others:
                if temp.alive:
                    temp.hp -= randint(10, 15)
            return True
        else:
            return False

    def resume(self):
        incr_point = randint(1, 10)
        self._mp += incr_point
        return incr_point

    def __str__(self):
        return f'~~~{self._name}奥特曼~~~\n生命值:{self._hp}\n' \
               f'生命值:{self._hp}\n魔法值:{self._mp}\n'

class Monster(Fighter):

    __slots__ = ('_name', '_hp')

    def attack(self, other):
        other.hp -= randint(10, 20)

    def __str__(self):
        return f'~~~{self._name}~~~\n生命值:{self._hp}\n'

def is_ang_alive(monsters):
    """判断有没有小怪兽是活着的"""
    for monster in monsters:
        if monster.alive > 0:
            return True
    return False

def select_alive_one(monsters):
    """选择一只活着的小怪兽"""
    monsters_len = len(monsters)
    while True:
        index = randrange(monsters_len)
        monster = monsters[index]
        if monster.alive > 0:
            return monster

def display_info(ultraman, monsters):
    """显示奥特曼和小怪兽的信息"""
    print(ultraman)
    for monster in monsters:
        print(monster)

def main():
    u = Ultraman('骆昊', 1000, 120)

    print('name: ', u._Ultraman__name)

    m1 = Monster('狄仁杰', 250)
    m2 = Monster('白远方', 500)
    m3 = Monster('王大锤', 750)
    ms = [m1, m2, m3]
    fight_round = 1
    while u.alive and is_ang_alive(ms):
        print('===========第%02d回合===========' % fight_round)
        m = select_alive_one(ms)  # 选中一只小怪兽
        skill = randint(1, 10)  # 通过随机数来随机选取使用的技能
        if skill <= 6:  # 60%的概率使用普通攻击
            print('%s使用普通攻击打了%s.' % (u.name, m.name))
            u.attack(m)
            print('%s的魔法值恢复了%d点' % (u.name, u.resume()))
        elif skill <= 9:  # 30%的概率使用魔法攻击
            if u.magic_attack(ms):
                print('%s使用了魔法攻击.' % u.name)
            else:
                print('%s使用魔法攻击失败.' % u.name)
        else:  # 10%的概率使用究极必杀技
            if u.huge_attack(m):
                print(f'{u.name}使用了究极必杀技虐杀了{m.name}')
            else:
                print('%s使用了普通攻击打了%s' % (u.name, m.name))
                print('%s的魔法值恢复了%d点' % (u.name, u.resume()))
        if m.alive > 0:
            print('%s回击了%s' %(m.name, u.name))
            m.attack(u)
        display_info(u, ms)  # 每个回合结束显示奥特曼和小怪兽的信息
        fight_round += 1
        if u.alive > 0:
            print(f'{u.name}奥特曼胜利了')
        else:
            print('小怪兽胜利了')


if __name__ == '__main__':
    main()

目前尚未找到原因,先记录下(2023.04.04)

解决方法

由于上述代码中使用到了一个抽象类Figher和它对应的两个继承类Ultraman和Monster,因而在类的内部或外部中使用_classname_attribute来调用类的私有属性时,会出现AttributeError。其主要原因是:继承类中的私有属性是源自抽象类的私有属性,当在继承类中使用基类的私有属性时需要使用_baseclassname_attribute来调用类的私有属性,而不是_inheritingclassname_attribute来调用类的私有属性。
下面先分析基类Fighter(抽象类)和它的两个继承类Ultraman和Monster的具体情况。

Fighter:  __slots__ = ('__name', '__hp')  
          # 故假设存在私有属性: _Fighter__name 和 _Fighter__hp
Ultraman:  __slots__ = ('__name', '__hp', '__mp')  
          # 重点:故在Ultraman类中实际存在的私有属性是: _Fighter__name 、 _Fighter__hp、               _Ultraman__mp  (而不是_Ultraman__name)
Fighter:  __slots__ = ('__name', '__hp')  
          # 故存在私有属性: _Fighter__name 和 _Fighter__hp

上述过程可通过debug发现如下结构:
在这里插入图片描述
发现在Ultraman类中_Ultraman__name为TracebackError
修改后的正确代码如下:(**此处代码和上述代码中命名私有属性的方式不同:**上面将_name视为私有属性,下面代码中将__name视为私有属性)

from abc import ABCMeta, abstractmethod
from random import randint, randrange

class Fighter(object, metaclass=ABCMeta):

    __slots__ = ('__name', '__hp')

    def __init__(self, name, hp):
        self.__name = name
        self.__hp = hp

    @property
    def name(self):
        return self.__name

    @property
    def hp(self):
        return self.__hp

    @hp.setter
    def hp(self, hp):
        self.__hp = hp if hp >= 0 else 0

    @property
    def alive(self):
        return self.__hp > 0

    @abstractmethod
    def attack(self, other):
        pass


class Ultraman(Fighter):

    __slots__ = ('__name', '__hp', '__mp')

    def __init__(self, name, hp, mp):
        """

        :param name:
        :param hp: 生命值
        :param mp: 魔法值
        """
        super().__init__(name, hp)
        self.__mp = mp

    def attack(self, other):
        other.hp -= randint(15, 25)  #zh:为什么是“other.hp”,而不是“other._hp”  # 是不是因为在对象的外部访问不到私有属性,使用使用getter和setter方法
        # print('other.__hp: ', other.__hp)
        print('other.__hp: ', other.hp)


    def huge_attack(self, other):
        """若自身魔法值充足(大于50),则至少打掉对方50点或四分之三的血量

        :param other: 被攻击的对象
        :return: 使用成功,返回True;否则,返回False
        """
        if self.__mp >= 50:
            self.__mp -= 50
            injury = other.hp * 3 // 4
            injury = injury if injury >= 50 else 50
            other.hp -= injury
            return True
        else:
            self.attack(other)
            return False
    def magic_attack(self, others):
        if self.__mp >= 20:
            self.__mp -= 20
            for temp in others:
                if temp.alive:
                    temp.hp -= randint(10, 15)
            return True
        else:
            return False

    def resume(self):
        incr_point = randint(1, 10)
        self.__mp += incr_point
        return incr_point

    def __str__(self):
        return f'~~~{self._Fighter__name}奥特曼~~~\n生命值:{self._Fighter__hp}\n' \
               f'魔法值:{self._Ultraman__mp}\n'

class Monster(Fighter):

    __slots__ = ('__name', '__hp')

    def attack(self, other):
        other.hp -= randint(10, 20)

    def __str__(self):
        return f'~~~{self._Fighter__name}~~~\n生命值:{self._Fighter__hp}\n'

def is_ang_alive(monsters):
    """判断有没有小怪兽是活着的"""
    for monster in monsters:
        if monster.alive > 0:
            return True
    return False

def select_alive_one(monsters):
    """选择一只活着的小怪兽"""
    monsters_len = len(monsters)
    while True:
        index = randrange(monsters_len)
        monster = monsters[index]
        if monster.alive > 0:
            return monster

def display_info(ultraman, monsters):
    """显示奥特曼和小怪兽的信息"""
    print(ultraman)
    for monster in monsters:
        print(monster)

def main():
    u = Ultraman('骆昊', 1000, 120)

    # print('name: ', u._Ultraman__name)  # zh:AttributeError: _Ultraman__name
    print('name: ', u._Fighter__name)

    m1 = Monster('狄仁杰', 250)
    m2 = Monster('白远方', 500)
    m3 = Monster('王大锤', 750)
    ms = [m1, m2, m3]
    fight_round = 1
    while u.alive and is_ang_alive(ms):
        print('===========第%02d回合===========' % fight_round)
        m = select_alive_one(ms)  # 选中一只小怪兽
        skill = randint(1, 10)  # 通过随机数来随机选取使用的技能
        if skill <= 6:  # 60%的概率使用普通攻击
            print('%s使用普通攻击打了%s.' % (u.name, m.name))
            u.attack(m)
            print('%s的魔法值恢复了%d点' % (u.name, u.resume()))
        elif skill <= 9:  # 30%的概率使用魔法攻击
            if u.magic_attack(ms):
                print('%s使用了魔法攻击.' % u.name)
            else:
                print('%s使用魔法攻击失败.' % u.name)
        else:  # 10%的概率使用究极必杀技
            if u.huge_attack(m):
                print(f'{u.name}使用了究极必杀技虐杀了{m.name}')
            else:
                print('%s使用了普通攻击打了%s' % (u.name, m.name))
                print('%s的魔法值恢复了%d点' % (u.name, u.resume()))
        if m.alive > 0:
            print('%s回击了%s' %(m.name, u.name))
            m.attack(u)
        display_info(u, ms)  # 每个回合结束显示奥特曼和小怪兽的信息
        fight_round += 1
        if u.alive > 0:
            print(f'{u.name}奥特曼胜利了')
        else:
            print('小怪兽胜利了')


if __name__ == '__main__':
    main()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python面向对象类继承中发生的私有属性访问错误问题 的相关文章

随机推荐

  • 一个java程序员4年4个工作的经历

    今天看到一个帖子说程序员怎么提升自己的技能 里面说了十多项 对自己感觉能有用的有以下几点 读书 读代码 读博客 写博客 回答别人的问题 参加一个开源项目 参加培训 研讨会 回答问题当中给自己的思考 一个毕业生 做java转正了4k 一个室友
  • 为什么要选择云原生数据库

    为什么要选择云原生数据库 前言 1 传统数据库 1 1 传统数据库概念 1 2 传统数据库优缺点 1 2 1 优点 1 2 2 缺点 2 云原生数据库 2 1 云原生数据库概念 2 2 云化代表未来 2 3 云原生数据库的优势 2 3 1
  • 【MATLAB】字符串的处理及矩阵的初步学习

    欢迎访问我的个人网站 reality2ideal xyz 内容在CSDN和个人网站上同步更新 字符串处理 字符串矩阵 gt gt ch 123456 qwerty ch 2 6 char 数组 123456 qwerty 字符串矩阵的列数要
  • 转载:R语言绘图—图形标题、坐标轴设置

    R语言绘图是通过函数命令及相应参数设置实现的 如plot x y plot为绘图函数命令 x y则是绘图参数 指定了绘图的数据向量 但这种最基本的绘图设置很难满足个性化绘图的要求 我们需要根据需要对图形元素进行设置 图形元素是各类图形的基本
  • 生成带干扰线的验证码

    import java awt Color import java awt Font import java awt Graphics2D import java awt Transparency import java awt image
  • vue吸顶导航栏_vue2组件系列第四十二节:NavBar 导航栏

    NavBar就是程序顶部的内容 相当于网站里的面包屑的作用 准备工作 创建一个页面 NavBar vue 在router js里配置NavBar页面的路由 path navbar name navbar component gt impor
  • 左右手坐标系区别和联系

    本文是分析 所谓的右手坐标系转换为左手坐标系需要的 z轴取反 x轴取反 或者改变摄像机位置 渲染绕序改变 其中的进一步的原因 参考文章 https msdn microsoft com en us library bb204853 28VS
  • 真伪定时器

    首先观察一下下面两组代码区别在哪里 第一组代码 setInterval gt 1 5s 的同步逻辑 1000 第二组代码 function fn setTimeout gt 1 5s 的同步逻辑 fn 1000 fn 两组代码都有定时功能
  • Java实体类详解及使用方法

    在Java编程中 实体类 Entity Class 是一种经常使用的类类型 实体类用于表示真实世界中的对象 通常与数据库中的表格相对应 本文将详细介绍Java实体类的概念 特点以及使用方法 什么是实体类 实体类是指用于表示和存储真实世界中的
  • 【论文精读】A view-free image stitching network based on global homography-基于全局单应的无视图图像拼接网络

    论文链接地址 代码链接地址 关于本文的代码 我已经调试过了 在调试过程中遇到的错误 我也做了一些总结 有需要的可以参考这篇博文 A view free image stitching network based on global homo
  • Spring Boot集成控制反转

    Most of the time dependency injection is the first thing that comes to mind whenever the concept of inversion of control
  • idea 2021.1安装 与 常用配置

    前置说明 该文档是基于idea 2021 1版本编写的 一 下载安装 官方下载地址 https www jetbrains com idea download other html 二 常用的设置 显示工具栏 设置tab选项卡换行 设置代码
  • Unity 打开时一直busy怎么办

    查看网络连接 比如360流量球或者任务管理器内的网络 如果能看到unity在下载东西或网络占用高 则表明可能是unity在下载在线资源 查看 工程目录 Package manifest json 文件是否存在国外地址 可能是由于网络原因连不
  • RabbitMq——发布确认高级和消息回退

    发布确认高级 消息在传递过程中 我们需要确定消息状态信息 开启发布确认高级模式 消息传递结束后会返回传递结果信息 若发送失败的消息 该消息会被存入缓存中 定时任务发送失败消息 交换机收到消息后 缓存会删除该信息 如果只开启发布确认模式的话
  • java多线程的意义

    https www zhihu com question 332042250
  • 前缀和与差分(分析与模板)

    前缀和 处理数组公式 s i s i 1 num i 输出区间和公式 s r s l 1 模板 include
  • kMeans算法(K均值聚类算法)

    机器学习中有两类的大问题 一个是分类 一个是聚类 分类是根据一些给定的已知类别标号的样本 训练某种学习机器 使它能够对未知类别的样本进行分类 这属于supervised learning 监督学习 而聚类指事先并不知道任何样本的类别标号 希
  • 【100%通过率 】【华为OD机试真题 c++ 】最大数字【 2023 Q1 A卷

    华为OD机试 题目列表 2023Q1 点这里 2023华为OD机试 刷题指南 点这里 题目描述 给定一个由纯数字组成以字符串表示的数值 现要求字符串中的每个数字最多只能出现2次 超过的需要进行删除 删除某个重复的数字后 其它数字相对位置保持
  • Android 模拟器 Genymotion 安装配置与 ARM 支持

    简介 Genymotion是一款基于x86架构的Android模拟器 由于系统启动速度 应用运行速度远远快于Android SDK自带模拟器而受到广泛应用 优缺点 优点 1 模拟器启动速度快 比AVD快很多 2 应用运行速度快 3 跨平台
  • Python面向对象类继承中发生的私有属性访问错误问题

    按照Python100days项目中的该方法来访问私有属性 可正常访问到 class Test def init self foo self foo foo def bar self print self foo print bar def