首页 > 其他分享 >面向对象的三大特性、封装的介绍及属性、装饰器property、员工管理系统

面向对象的三大特性、封装的介绍及属性、装饰器property、员工管理系统

时间:2024-05-08 20:33:55浏览次数:13  
标签:__ .__ name self 面向对象 def print property 三大

【一】面向对象的三大特性

  • 封装:
    • 封装指的就是把数据与功能都整合到一起
  • 继承
  • 多态

【二】什么是封装

  • 封装是对具体对象的一种抽象
    • 意思就是将某部分功能和代码隐藏起来,在程序外边看不到,只能在程序内部使用

【三】为什么要封装?

  • 封装数据的主要原因是:保护隐私(把不想别人知道的东西封装起来)

【四】封装的方法

  • 自己的身体其实也有封装的部分
  • 自己有并且自己能用但是别人看不到也用不了
  • 方法:
    • 在变量名前面加 __

【五】封装隐藏属性

【1】数据属性

  • Python的Class机制采用双下划线开头的方式将属性隐藏起来(设置成私有的)
  • 但其实这仅仅只是一种变形操作,类中所有双下滑线开头的属性都会在类定义阶段、检测语法时自动变成_类名__属性名的形式:
class Person:
    # 变形为_Person__NAME
    __NAME = "Dream"

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __init__(self):
        # 变形为self._Person__age
        self.__age = 18

    # 变形为_Foo__run
    def __run(self):
        print('__f1 run')

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def walk(self):
        # 变形为self._Person__run()
        self.__run()


print(Person.__N)  
# 报错AttributeError:类Person没有属性__N

obj = Person()
print(obj.__x)  
# 报错AttributeError:对象obj没有属性__x
class Person:
    # 在变量名前面加 __ : 在类初始化对象的时候会对当前变量名进行变形 变形成 _Person__变量名
    __SCHOOL_NAME = '清华'

    def __init__(self, name):
        self.name = name


student = Person(name='dream')
print(student.name)
# 查看类的名称空间
# {'_Person__SCHOOL_NAME': '清华',}
print(Person.__dict__)
print(student.__SCHOOL_NAME) # 找不到 
print(student._Person__SCHOOL_NAME) # 能找到

【2】函数属性

  • 在类内部是可以直接访问双下滑线开头的属性的,比如self.__f1(),因为在类定义阶段类内部双下滑线开头的属性统一发生了变形。
class Person:
    # 在变量名前面加 __ : 在类初始化对象的时候会对当前变量名进行变形 变形成 _Person__变量名
    __SCHOOL_NAME = '清华'

    def __init__(self, name):
        self.name = name

    # 函数属性 : 在类初始化对象的时候会对当前变量名进行变形 变形成 _Person__变量名
    def __change_name(self):
        self.name = 'nb_' + self.name


student = Person(name='dream')

print(student.name)
# 查看类的名称空间
# {'_Person__SCHOOL_NAME': '清华','_Person__change_name': <function Person.__change_name at 0x000001D09384C0D0>,}
print(Person.__dict__)

print(student.__change_name())

# 当我们在对象中调用相关属性的时候会发现,找不到
class Person:
    # 变形为_Person__NAME
    __NAME = "Dream"

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __init__(self):
        # 变形为self._Person__age
        self.__age = 18

    # 变形为_Foo__run
    def __run(self):
        print('__f1 run')

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def walk(self):
        # 变形为self._Person__run()
        self.__run()  # 等同于 self._Person__run()
        print(self.__NAME)  # 等同于 print(self._Person__age)

【3】变形操作只会发生一次

  • 变形操作只在类定义阶段发生一次,在类定义之后的赋值操作,不会变形。
class Person:
    # 变形为_Person__NAME
    __NAME = "Dream"

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __init__(self):
        # 变形为self._Person__age
        self.__age = 18

    # 变形为_Foo__run
    def __run(self):
        print('__f1 run')

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def walk(self):
        # 变形为self._Person__run()
        self.__run()  # 等同于 self._Person__run()
        print(self.__NAME)  # 等同于 print(self._Person__age)


# 修改类的变量
Person.__NAME = "Hope"

# 查看内存空间 --- 发现并没有被修改
print(Person.__dict__)
# {'__module__': '__main__', '_Person__NAME': 'Dream', '__init__': <function Person.__init__ at 0x00000260E28E13F0>, '_Person__run': <function Person.__run at 0x00000260E28E1B40>, 'walk': <function Person.walk at 0x00000260E28E1BD0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, '__NAME': 'Hope'}

# 实例化得到类对象
person = Person()
# Dream

# 修改对象变量
person.__sex = "男"
person.__age = 22

# 查看对象的名称空间 --- 新增了相应的键值
print(person.__dict__)
# {'_Person__age': 18, '__sex': '男', '__age': 22}

# 可以直接根据键取值
print(person.__sex)
# 男

【六】开放接口

  • 定义属性就是为了使用,所以隐藏并不是目的

【1】隐藏数据属性

  • 将数据隐藏起来就限制了类外部对数据的直接操作,然后类内部应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制
class Teacher:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def set_info(self, name, age):
        # 修改名字和修改年龄之前要校验当前的格式是否正确
        # 名字前面必须 + lj_
        if not name.startswith('lj_'):
            raise ValueError("名字必须是 lj 前缀")
        # 年龄必须是数字且大于 0
        if not age.isdigit():
            raise ValueError("年龄必须是数字")
        if int(age) < 0:
            raise ValueError("年龄超出常人,建议回炉重造!")
        self.__name = name
        self.__age = age

    # 做一个接口查看当前讲师的个人信息
    def tell_info(self):
        print(f"当前讲师是 :>>>> {self.__name} 年龄是 :>>>> {self.__age}")


teacher = Teacher(name="dream", age=18)
# teacher.tell_info()
#修改当前讲师的姓名和年龄
teacher.set_info(name='lj_opp',age='1')
teacher.tell_info()

【2】隐藏函数属性

  • 目的的是为了隔离复杂度
    • 例如ATM程序的取款功能,该功能有很多其他功能组成,
      • 比如插卡、身份认证、输入金额、打印小票、取钱等
    • 而对使用者来说,只需要开发取款这个功能接口即可,其余功能我们都可以隐藏起来
class ATM:
    # 插卡
    def __card(self):
        print('插卡')

    # 身份认证
    def __auth(self):
        print('用户认证')

    # 输入金额
    def __input(self):
        print('输入取款金额')

    # 打印小票
    def __print_bill(self):
        print('打印账单')

    # 取钱
    def __take_money(self):
        print('取款')

    # 取款功能
    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()


obj = ATM()
obj.withdraw()
# 插卡
# 用户认证
# 输入取款金额
# 打印账单
# 取款

【3】总结

  • 隐藏属性和开放接口本质上都是为了明确的区分内外,在类内部可以随意修改我的代码,但是在类的外部不允许使用者修改代码
  • 类外部只需要拿到一个接口,只要接口名不变,里面的逻辑就可以随意实现
  • 接口只要基础不变,代码就可以任意修改

【七】装饰器 property

【1】什么是property

  • property 是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
  • 将函数的返回值作为数据属性返回
class Student:
    def __init__(self, name):
        self.name = name

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

student = Student(name='chosen')
print(student.name) # chosen
print(student.vip_name) # chosen

【2】BMI例子

  • MI指数是用来衡量一个人的体重与身高对健康影响的一个指标
  • 成人的BMI数值:
    • 过轻:低于18.5
    • 正常:18.5-23.9
    • 过重:24-27
    • 肥胖:28-32
    • 非常肥胖, 高于32
  • 计算公式
    • 体质指数(BMI)=体重(kg)÷身高^2(m)
    • EX:70kg÷(1.75×1.75)=22.86
  • 身高或体重是不断变化的
    • 所以在每次要查看BMI值都需要通过计算才能得到,但很明显BMI听起来更像是一个特征而不是功能,所以就有了装饰器 property
    • 可以将类中的函数“伪装成”对象的数据属性,对象在访问该特殊属性时会触发功能的执行,然后将返回值作为本次访问的结果
class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)


obj = People('dream', 62, 1.70)

# 触发方法bmi的执行,将obj自动传给self,执行后返回值作为本次引用的结果
# 正常我们调用对象的方法应该是 obj.bmi() 但是这里我们没有 加 () 就调用成功了
print(obj.bmi)
# 21.453287197231838

【3】为什么要使用property

  • 在所有编程语言中都有三种封装方式
    • public : 公开
    • protected : 对外不公开,对内公开
    • private : 对谁都不公开
class Foo:
    def __init__(self, val):
        # 将属性隐藏起来
        self.__NAME = val

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

    @name.setter
    def name(self, value):
        # 在设定值之前进行类型检查
        if not isinstance(value, str):
            raise TypeError('%s must be str' % value)
        # 通过类型检查后,将值value存放到真实的位置self.__NAME
        self.__NAME = value

    @name.deleter
    def name(self):
        raise PermissionError('Can not delete')


f = Foo('dream')
print(f.name)

# 触发name.setter装饰器对应的函数name(f,'Hope')
f.name = 'Hope'  

# 触发name.setter对应的的函数name(f,123),抛出异常TypeError
f.name = 123  

# 触发name.deleter对应的函数name(f),抛出异常PermissionError
del f.name 
class Foo:
    def __init__(self, val):
        # 将属性隐藏起来
        self.__NAME = val

    def get_name(self):
        return self.__NAME

    def set_name(self, value):
        # 在设定值之前进行类型检查
        if not isinstance(value, str):
            raise TypeError('%s must be str' % value)
        # 通过类型检查后,将值value存放到真实的位置self.__NAME
        self.__NAME = value

    def del_name(self):
        raise PermissionError('Can not delete')

    # 不使用装饰器,而是使用 包装的 形式
    name = property(get_name, set_name, del_name)


f = Foo('dream')
print(f.name)

# 触发name.setter装饰器对应的函数name(f,'Hope')
f.name = 'Hope'

# 触发name.setter对应的的函数name(f,123),抛出异常TypeError
f.name = 123

# 触发name.deleter对应的函数name(f),抛出异常PermissionError
del f.name

【八】员工管理系统(使用类方法)

import hashlib
import json
import os
import random

class Tools(object):
    def __init__(self):
        self.BASE_DIR = os.path.dirname(__file__)
        self.DB_DIR = os.path.join(self.BASE_DIR, 'data')
        os.makedirs(self.DB_DIR, exist_ok=True)
        self.file_path = os.path.join(self.DB_DIR, 'emp_data.json')

    def check_data_type(self):
        ...

    def get_username_password(self):
        username = input("请输入用户名 :>>>> ").strip()
        password = input("请输入 密码 :>>>> ").strip()
        return username, password

    # 把函数属性编程数据属性 property
    # salt() --- @property ---> salt
    @property
    def salt(self):
        code = ''
        for i in range(5):
            num = random.randint(0, 9)
            alf = chr(random.randint(65, 90))
            add = random.choice([num, alf])
            code = "".join([code, str(add)])
        return code

    def encrypt_password(self, salt=None, password=None):
        # 将所有数据变为字符串
        original_data = str(salt) + str(password)
        # 将字符串转换为二进制
        encrypted_data = original_data.encode()
        # 生成md5对象
        md5 = hashlib.md5()
        # nd5加密数据
        md5.update(encrypted_data)
        # 返回 32 为 16 进制的字符串
        return md5.hexdigest()

    def read_data(self):
        try:
            # 读取文件路径
            # 读取文件数据
            with open(file=self.file_path, mode='r', encoding='utf-8') as fp:
                data = json.load(fp)
            return data
        except:
            return {}

    def save_data(self, data):
        with open(file=self.file_path, mode='w', encoding='utf-8') as fp:
            json.dump(obj=data, fp=fp, ensure_ascii=False)


class Worker(object):
    # 初始化属性
    def __init__(self):
        self.func_menu = f'''
    ------------------ 功能菜单 ------------------ 
                        1.注册
                        2.登录
                        3.添加员工信息
                        4.删除员工信息
                        5.查看员工信息
                        q.退出系统
        '''
        self.func_dict = {
            '1': self.register,
            '2': self.login,
            '3': self.add_emp,
            '4': self.del_emp,
            '5': self.check_emp,
        }
        self.tools = Tools()
        self.login_dict = {}

    # 校验当前用户是否存在!
    def __exist_user(self, username):
        # 读取到所有的用户信息 {'dream':{'name':"dream","age":18}}
        emp_data_dict_all = self.tools.read_data()
        # 根据用户名获取到指定的用户字典 {'name':"dream","age":18}
        emp_data_dict = emp_data_dict_all.get(username)
        if not emp_data_dict:
            return False
        else:
            return True

    # 登录
    def login(self):
        print(f'欢迎来到登录功能!')
        # 获取用户名和密码
        username, password = self.tools.get_username_password()
        # 校验用户名是否存在
        exist_user = self.__exist_user(username)
        if not exist_user:
            return False, f'当前用户 {username} 不存在!请先注册!'
        # 存在则继续登录
        user_data_dict_all = self.tools.read_data()
        user_data_dict = user_data_dict_all.get(username)
        salt = user_data_dict.get('salt')
        old_password = user_data_dict.get('password')
        new_password = self.tools.encrypt_password(salt=salt, password=password)
        if old_password != new_password:
            return False, f'当前密码错误!'
        # 初始化登录字典
        self.login_dict['username'] = username
        return True, f'当前用户 {username} 登录成功!'

    # 注册
    def register(self):
        print(f"欢迎来到注册功能!")
        user_data_dict_all = self.tools.read_data()
        # 需要用户名和密码
        username, password = self.tools.get_username_password()
        # 校验当前用户名和密码是否存在
        user_exist = self.__exist_user(username=username)
        # 用户存在咋不允许注册,去登录
        if user_exist:
            return False, f'当前用户 {username} 已存在!请登录!'

        # 获取到密码加密需要用到盐
        salt = self.tools.salt

        # 对密码进行加密
        encrypted_password = self.tools.encrypt_password(salt=salt, password=password)
        # 更新所有人的用户数据
        user_data_dict_all[username] = {
            'username': username,
            'password': encrypted_password,
            'salt': salt
        }
        self.tools.save_data(data=user_data_dict_all)
        return True, f'当前用户 {username} 注册成功!'

    def login_auth(func):
        def inner(*args, **kwargs):
            # print(func)  # <function Worker.add_emp at 0x00000187E1466430>
            # print(kwargs) # {}

            self = args[0]
            # print(args)  # (<__main__.Worker object at 0x00000187E116A760>,)
            # print(self.login_dict) # {'username': 'dream'}

            if not self.login_dict.get('username'):
                return False,'请先登录!'
            else:
                return func(*args, **kwargs)

        return inner

    # 添加员工
    @login_auth  # login_auth = login_auth(add_emp)
    def add_emp(self):
        ...

    # 删除员工
    def del_emp(self):
        print(self) # <__main__.Worker object at 0x000001EFAAA8C640>
        ...

    # 查看员工信息
    def check_emp(self):
        ...

    def main(self):
        while True:
            print(self.func_menu)
            func_id = input("请输入功能ID :>>>> ").strip()
            if not func_id.isdigit():
                print(f"当前ID格式不正确!")
                continue
            if func_id not in self.func_dict:
                print(f"当前ID不存在!")
                continue
            func = self.func_dict[func_id]
            flag, msg = func()
            if not flag:
                print(msg)
                continue
            else:
                print(msg)


if __name__ == '__main__':
    s = Worker()
    s.main()

标签:__,.__,name,self,面向对象,def,print,property,三大
From: https://www.cnblogs.com/chosen-yn/p/18180812

相关文章

  • 20.面向对象【四】
    【一】抽象类抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化#所有继承父类的子类必须重写父类的某些方法,这个父类就叫抽象类importabcclassAnimal(metaclass=abc.ABCMeta):def__init__(self,color,foot):self.color=colorself.f......
  • Java面向对象04——三大特性之多态
    多态1、什么是多态在Java中,多态是面向对象编程中的一个重要概念,它允许不同类型的对象对同一方法进行不同的实现。具体来说,多态性指的是通过父类的引用变量来引用子类的对象,从而实现对不同对象的统一操作。2、多态实现的条件在Java中,要实现多态性,就必须满足以下条件:继承关......
  • 19.面向对象【三】
    【一】继承1)概念继承是一种创建新类的方式,新建的类可以继承一个或多个类的属性可以继承父类的所有属性和方法,实现代码的去重classStudent(School):#继承的类叫父类School#新建的类叫子类Student2)单继承classPerson(object):height=170weight=7......
  • C#中面向对象的一些基础概念
    案例所创建的.cs如下:OOP--ObjectOrientedProgramming实例化类baseclassbc=newbaseclass();subclasssc=newsubclass();bc.Func();sc.Func();里氏转换子类对象可以直接赋值给父类变量子类可以调用父类对象,但是父类只有调用自己父类中如果是子类对象,则......
  • qt 属性控件 使用qt提供的源码 qtpropertybrowser(D:\Qt\5.15.2\Src\qttools\src
    效果:   直接将头文件h和源文件cpp文件添加到项目中。cmakeLists.txt:file(GLOBqtpropertybrowser${QTPROPERTYBROWSER_DIR}/*.cpp${QTPROPERTYBROWSER_DIR}/*.h)include_directories("${QTPROPERTYBROWSER_DIR}")设置了源文件路径 只有一个cpp文件:#includ......
  • Java面向对象编程概念
    面向对象编程(OOP)概念,如类、对象、继承、封装、多态概念:面向对象编程(Object-OrientedProgramming,简称OOP)是一种程序设计范型或编程范式。这种范式使用“对象”来设计应用程序和系统的各个部分。在面向对象编程中,万物皆对象,程序被视作一系列对象的集合,这些对象通过消息传递来交互......
  • 解决报错:Could not set property 'id' of 'class com.north.domain.Book' with value
    报错原因问题描述:因为MyBatis-Plus默认的id自增策略使用的雪花算法org.mybatis.spring.MyBatisSystemException:nestedexceptionisorg.apache.ibatis.reflection.ReflectionException:Couldnotsetproperty'id'of'classcom.north.domain.Book'withvalue'1......
  • Mysql权限管理,备份与三大范式
    mysql访问权限系统表mysql的权限由四个表来控制权限,分别是user表,db表,tables_priv表,columns_priv表表名作用user存放用户账号、密码、主机信息和全局权限db数据库级别的权限表tables_priv表级别的权限表columns_priv列级权限表procs_priv函数/存储过......
  • 07.面向对象编程
    Java的核心思想就是OOP(面向对象编程)1.面向对象面向对象编程:Object-OrientedProgramming,OOP)本质:以类的方式组织代码,以对象的组织(封装)数据2.对象的创建2.1.创建与初始化对象创建对象:new使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化......
  • DRF之三大认证
    一、认证1、自定义认证在前面说的APIView中封装了三大认证,分别为认证、权限、频率。认证即登录认证,权限表示该用户是否有权限访问接口,频率表示用户指定时间内能访问接口的次数。整个请求最开始的也是认证。(1)需求登陆认证用户登陆成功--》签发token以后需要登陆才能访问的......