面向对象基础之封装

 封装

​ 什么是封装,就是将复杂的丑陋的,隐私的细节隐藏到内部,对外提供简单的使用接口

​ 对外隐藏内部实现细节,并提供访问的接口

为什么需要封装

​ 两个目的

​ 1.为了保证 关键数据的安全性

​ 2.对外部隐藏实现细节,隔离复杂度

 

什么时候应该封装

​ 当有一些数据不希望外界可以直接修改时

​ 当有一些函数不希望给外界使用时,

 

 如何使用:

语法:

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

p = Person("1111111111111","jack",29)

p.id_number = "222"

 

被封装的内容的特点:

​ 1.外界不能直接访问

​ 2.内部依然可以使用

权限

学习了封装后就可以控制属性的权限

在python只要两种权限,

1.公开的.默认就是公开的

2.私有的,只能由当前类自己使用

 

在外界访问私有的内容

属性虽然被封装了,但是还是需要使用的,在外界如何访问

通过定义方法类完成对私有属性的修改和访问


这是一个下载器类,需要提供一个缓存大小这样的属性
缓存大小不能超过内存限制

class Downloader:
def __init__(self,filename,url,buffer_size):
self.filename = filename
self.url = url
self.__buffer_size= buffer_size

def start_download(self):
if self.__buffer_size <= 1024*1024:
print("开始下载....")
print("当前缓冲器大小",self.__buffer_size)
else:
print("内存炸了! ")


def set_buffer_size(self,size):
#可以在方法中添加额外的逻辑
if not type(size) == int:
print("大哥 缓冲器必须是整型")
else:
print("缓冲区大小修改成功!")
self.__buffer_size = size

def get_buffer_size(self):
return self.__buffer_size

d = Downloader("葫芦娃","http://www.baicu.com",1024*1024)


# 通过函数取修改内部封装的属性
d.set_buffer_size(1024*512)

# 通过函数访问内部封装的属性
print(d.get_buffer_size())

d.start_download()

 

 

这样一来我们可以在外界修改这个关键数据时,做一些限制

 

property装饰器

通过方法来修改或访问属性,本身没什么问题,但是这给对象的使用者带来了麻烦.

使用必须知道哪些是普通属性,哪些是私有属性,需要使用不同的方式来调用他们

property装饰就是为了使得调用方式一致

有三个相关的装饰器


1.property 该装器用在获取属性的方法上
2.@key.setter 该装器用在修改属性的方法上
3.@key.deleter 该装器用在删除属性的方法上


注意:key是被property装饰的方法的名称 也就是属性的名称
内部会创建一个对象 变量名称就是函数名称
所以在使用setter和deleter时 必须保证使用对象的名称取调用方法
所以是 key.setter
```

案例:

class A:
def __init__(self,name,key):
self.__name = name
self.__key = key

@property
def key(self):
return self.__key

@key.setter
def key(self,new_key):
if new_key <= 100:
self.__key = new_key
else:
print("key 必须小于等于100")


@key.deleter
def key(self):
print("不允许删除该属性")
del self.__key

a = A("jack",123)
print(a.key)
a.key = 321
print(a.key)
```

 

python实现封装的原理

就是在加载类的时候,把__替换成了 _类名__

python一般不会强制要求程序必须怎么怎么的,

封装:

对外部隐藏内部的实现细节,并提供访问的接口

好处:

​ 1.提高安全性

​ 2.隔离复杂度

语法:将要封装的属性或方法名称前加上双下划线

访问被隐藏的属性:

​ 提供用于访问和修改的方法

使用property装饰器可以将一个方法伪装成普通顺属性,报纸属性之间调用方法一致

封装的实现原理 ,替换变量名称

 

 property 可以用来实现计算属性

计算属性指的是:属性的值,不能直接获得,必须通过计算才能获取

例如:正方形求面积

class Space:
def __init__(self,width):
self.width = width

@property
def area(self):
return self.width * self.width
s = Space(10)
print(s.area)

s.width = 20
print(s.area)

 

 

练习: 定义一个类叫做person
包含三个属性 身高 体重 BMI
BMI的值需要通过计算得来 公式 体重 / 身高的平方

class Person:

def __init__(self,height,weight):
self.height = height
self.weight = weight

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


p = Person(175,50)
print(p.BMI)
```

 

接口 (了解)

接口是一组功能的集合,但是接口中仅包含功能的名字,不包含具体的实现代码

接口本质是一套协议标准,遵循这个标准的对象就能被调用

接口目的就是为了提高扩展性:

例如电脑提前指定制定一套USB接口协议,只要你遵循该协议,你的设备就可以被电脑使用,不需要关心到底是鼠标还是键盘

案例:

class USB:
def open(self):
  pass

def close(self):
  pass

def read(self):
  pass

def write(self):
  pass

class Mouse(USB):
  def open(self):
    print("鼠标开机.....")

  def close(self):
    print("鼠标关机了...")

  def read(self):
    print("获取了光标位置....")

  def write(self):
    print("鼠标不支持写入....")


def pc(usb_device):
  usb_device.open()
  usb_device.read()
  usb_device.write()
  usb_device.close()

m = Mouse()
# 将鼠标传给电脑
pc(m)

class KeyBoard(USB):
  def open(self):
    print("键盘开机.....")

  def close(self):
    print("键盘关机了...")

  def read(self):
    print("获取了按键字符....")

  def write(self):
    print("可以写入灯光颜色....")

# 来了一个键盘对象
k = KeyBoard()
pc(k)
```

 

在上述案例中,PC的代码一旦完成,后期无论什么样的设备 只要遵循了USB接口协议,都能够被电脑所调用

接口主要是方便了对象的使用者,降低使用者的 学习难度,只要学习一套使用方法,就可以以不变应万变

问题:

如果子类没有按照你的协议来设计,也没办法限制他,将导致代码无法运行

 

抽象类

指的是包含抽象方法(没有函数体的方法)的类,

作用:可以限制子类必须类中定义的抽象方法

 

最后:python一般不会限制你必须怎么写,作为一个优秀的程序员,就应该自觉遵守相关协议

所以有了鸭子类型这么一说:

如果这个对象长得像鸭子,走路像鸭子,那就他是鸭子

你只要保证你的类按照相关的协议类编写,也可以达到提高扩展性的目的

案例:

class Mouse:
  def open(self):
    print("鼠标开机.....")

  def close(self):
    print("鼠标关机了...")

  def read(self):
    print("获取了光标位置....")

  def write(self):
    print("鼠标不支持写入....")

 

def pc(usb_device):
  usb_device.open()
  usb_device.read()
  usb_device.write()
  usb_device.close()

m = Mouse()
# 将鼠标传给电脑
pc(m)

class KeyBoard:
  def open(self):
    print("键盘开机.....")

  def close(self):
    print("键盘关机了...")

  def read(self):
    print("获取了按键字符....")

  def write(self):
    print("可以写入灯光颜色....")


# 来了一个键盘对象
k = KeyBoard()
pc(k)

class UDisk:
  def open(self):
    print("U盘启动了...")

  def close(self):
    print("U盘关闭了...")

  def read(self):
    print("读出数据")

  def write(self):
    print("写入数据")

u = UDisk()
pc(u)

 

 

接口是一套协议规范,明确子类们应该具备哪些功能

抽象类是用于强制要求子类必须按照协议中规定的来实现

然而,python不推崇限制你的语法, 我们可以设计成鸭子类型,既让多个不同类对象具备相同的属性和方法

对于使用者而言,就可以以不变应万变,轻松的使用各种对象

小练习:

面向对象基础之封装
'''
用面向对象实现 植物大战僵尸游戏

1.定义一个僵尸Zombie类,该类可以实例化出多种僵尸对象,僵尸对象产生默认都有 名字name、血量HP、防具armor
    -- 名字:普通僵尸 | 路障僵尸 | 铁桶僵尸
    -- 血量:默认就是100,不需要外界提供
    -- 防具:不需要外界提供,从名字中分析确定,防具的值是一个列表,从名字分析得到
            -- ['无', 0] | ['路障', 5] | ['铁桶', 15]  => [防具名, 防具的防御值]
    -- 通过@property的getter、setter方式,对外提供防具的两个访问接口armor_name与armor_count
            -- armor_name可以取值、赋值、删除值:通过一个
                -- eg: 普通僵尸对象.armor_name = '铁桶',不仅改变了防具名
                --     普通僵尸对象的名字name也会变成 铁桶僵尸
            -- armor_count只可以取值
'''
class Zombie:
    def __init__(self,name):
        self.name = name
        self.HP = 100
        if name not in ['普通僵尸','路障僵尸','铁桶僵尸']:
            self.name = '普通僵尸'
            self.__armor = ['无',0]
        elif self.name.startswith('普通'):
            self.name = '普通僵尸'
            self.__armor = ['普通',0]
        elif self.name.startswith('路障'):
            self.name = '路障僵尸'
            self.__armor = ['路障', 5]
        elif self.name.startswith('铁桶'):
            self.name = '铁桶僵尸'
            self.__armor = ['铁桶', 15]
    @property
    def armor_name(self):
        return self.__armor

    @armor_name.setter
    def armor_name(self,name):
        if name not in ['无','路障','铁桶']:
            return
        if name == '无':
            name = '普通'
            self.__armor = ['无',0]
        elif name == '路障':
            self.__armor = ['路障',0]
        else:
            self.__armor = ['铁桶',0]

        self.name = name + '僵尸'
    @armor_name.deleter
    def armor_name(self):
        del self.__armor
    @property
    def armor_count(self):
        return self.__armor[1]

'''
2.定义一个角色User类,该类有名字name属性、以及打僵尸的beat方法
    -- 名字:随意自定义
    -- beat:该方法需要传入一个僵尸对象
            -- 在方法内部可以实现:某某用户攻击了某某个僵尸,僵尸损失多少血,还剩多少血
            -- 每一次攻击,都固定扣除25滴血,但是不同的僵尸会被防具相应抵消掉一定的伤害值
            -- 循环攻击僵尸,3s攻击一次,僵尸被击杀后,打印 某某用户击杀了某某个僵尸 并结束方法
'''
class User:
    def __init__(self,name):
        self.name = name
    def beat(self,zombie):
        u_name = self.name
        z_name = zombie.name
        z_HP = zombie.HP
        low_HP = 25 - zombie.armor_count
        while z_HP > 0:
            import time
            time.sleep(0.5)
            z_HP -= low_HP
            print(f'{u_name}攻击了{z_name},僵尸损失了{low_HP}滴血,还剩{z_HP}滴血')
        print(f'{u_name}击杀了{z_name}')

'''
3.定义一个Game类,该类有一个name属性,属性值为 "植物大战僵尸" ,该类中有一个start方法,通过Game.start()来启动游戏
    -- 游戏一开始先显示游戏的名字 植物大战僵尸游戏
    -- 会随机产生三种僵尸,总共产生三个作为要被击杀的对象
    -- 生成一个有角色名的角色,依次去击杀随机产生的每一只僵尸
        -- 开始击杀第一只僵尸 => 某某用户攻击了某某个僵尸,僵尸损失多少血,还剩多少血 => 某某用户击杀了某某个僵尸 => 第一只僵尸已被击杀完毕 => 开始击杀第二只僵尸 ... => 第三只僵尸已被击杀完毕
'''

import random

class Ganme:
    name = '东东大战僵尸'
    @classmethod
    def start(cls):
        print(cls.name)
        for i in range(1,5):
            name = random.choice(['普通僵尸','路障僵尸','铁桶僵尸'])
            zombie = Zombie(name)
            user = User('东东')
            print(f'准备开始打第{i}只僵尸')
            user.beat(zombie)
            print(f'第{i}只僵尸已被击杀')
Ganme.start()
View Code

 

上一篇:Python weekly -- Things you’re probably not using in Python 3 – but should


下一篇:js下载blob文件