Day22 面向对象(继承封装)

面向对象有三大特性:封装,继承,多态

1.继承

  1 .什么是继承: 继承是一种关系,再程序中,继承描述的就是类与类之间的关系,例如A继承了B,A就可以使用B已经存在的方法和属性,A就称为是B的子类,B也就是父类,也称为基类

  2.继承的特点:继承的一方可以直接使用被继承一方已经有的方法和属性.可以重用已经有的代码.提高代码的重用性

  3:继承语法格式:

class A:
    pass

class B(A):  # B类继承了A类
    pass

class C(B,A): # C类同时继承了B类与A类
    pass

  3.抽象. 再我们使用继承时不要盲目的去写要先进行抽象,抽象就是否将多个子类中相同的部分进行抽取,形成一个新的类,这个过程就时抽象

  4.继承的使用流程: 1 先抽象再继承        2,继承一个已经存在的类,扩展或时修改原始的功能

  5.属性的查找顺序:  先从对象自己本身找,然后再是自身所在类中,再找所在类的父类中查找.层层递进,最终层为Object,若找不到则报错

class A:
    num = 1

class B(A):
    num = 2


d = B()
d.num = 3
print(d.num)   # 3
class A:
    num = 1

class B(A):
    num = 2


d = B()
print(d.num)  # 2
class A:
    num = 1

class B(A):
    # num = 2
    pass



d = B()
print(d.num)  # 1

  6.派生: 当一个子类中出现了与父类中不同的内容是,这个子类就称之为派生类,通常子类都会有一些新的代码.所以一般的子类都是派生类

  7.覆盖: 当子类出现了和父类名称完全一致的属性或方法时.子类在定义这些相同的方法的时候就会父类的这些方法进行覆盖  也称为重写overrides

  8.子类中访问父类的内容  通过super()  语法

方式1:
super(当前类名称,self).你要调的父类的属性或方法
方式2:
super().你要调的父类的属性或方法
方式3:   # 与继承无关
类名称.你要调的父类的属性或方法(self)  

  注意: 当继承一个现有的类,并且覆盖了父类的__init__方法时,必须要在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数

  9 : 练习,实现一个可以限制元素类型的列表

class MyList(list):
    def __init__(self):
        super().__init__()


    def append(self,name):
        if type(name) is str:
            print("不能是字符串")
        else:
            super().append(name)

m = MyList()
m.append('6666')
print(m[0])

  10: 组合:  组合是描述两个对象什么有什么的关系,当两个类之间没有太大的关系,完全不属于同类,如果我们使用继承就不符合正常逻辑了.这是我们又需要去访问令一个类中的方法或属性,这时候我们就可以使用组合,使用组合最终目的也是提高代码的复用性,降低代码的耦合度

class Phone:
    def __init__(self,kind,price):
        self.kind = kind
        self.price = price
    def call(self):
        print('拨号中')


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

    def with_call(self):
        print('我要打电话')


iphone = Phone('苹果',6000)
s1 = Stu('小明',18,iphone)
s1.iphone.call()

   11.新式类与经典类

    新式类:任何显式或隐式的继承了object的类就称为新式类,在python3中所有的类都是新式类

    经典类: 没有继承object的类,只有在python2中出现

    当出现菱形继承是. 都是先深度,当遇到相同父类时就广度 ,可使用类中__mro__方法查看查找顺序

class Init(object):

    def __init__(self, v):
        print("init")
        self.val = v

class Add2(Init):

    def __init__(self, val):
        print("Add2")
        super(Add2, self).__init__(val)
        print(self.val)
        self.val += 2

class Mult(Init):

    def __init__(self, val):
        print("Mult")
        super(Mult,self).__init__(val)
        self.val *= 5

class HaHa(Init):

    def __init__(self, val):
        print("哈哈")
        super(HaHa, self).__init__(val)
        self.val /= 5
class Pro(Add2,Mult,HaHa):
    pass
class Incr(Pro):

    def __init__(self, val):
        super(Incr, self).__init__(val)
        self.val+= 1
# Incr Pro Add2 Mult HaHa Init
p = Incr(5)
print(p.val)  # Add2 Mult 哈哈 init 5.0 8.0
c = Add2(2)
print(c.val)  #   Add2  init 2  4
print()
print(Incr.__mro__)  # (<class '__main__.Incr'>, <class '__main__.Pro'>, <class '__main__.Add2'>, <class '__main__.Mult'>, <class '__main__.HaHa'>, <class '__main__.Init'>, <class 'object'>)

  13:习题:

# 程序员,拥有,姓名,性别,年龄,工资,和编程技能
# 项目经理必须有程序员晋升而来,拥有奖金,和管理技能
# 请使用面向对象来表达这种关系
# 选做需求,让程序员和项目经理都能调用save将对象序列化到文件
import pickle
import os


class BaseClass:  # 我们希望可以将存取操作抽取出来.以降低程序的耦合度

    def save(self):
        # 先判断系统是否由想要存放的文件夹
        if not os.path.exists(self.__class__.__name__):
            # 如果没有此文件则创建
            os.makedirs(self.__class__.__name__)
        path = os.path.join(self.__class__.__name__, self.name)
        with open(path, 'wb') as f:
            pickle.dump(self, f)
            f.flush()

    @classmethod
    def select(cls, name):
        path = os.path.join(cls.__name__, name)
        if os.path.exists(path):
            with open(path, 'rb', ) as f:
                return pickle.load(f)


class Coder(BaseClass):
    def __init__(self, name, gender, age, balance):
        self.name = name
        self.gender = gender
        self.age = age
        self.balance = balance

    def program(self):
        print(f"我是{self.name},我会编程")


class Pm(Coder, BaseClass):

    def __init__(self, name, gender, age, balance, prize):
        super().__init__(name, gender, age, balance)
        self.prize = prize

    def menage(self):
        print(f"我会管理")


p = Pm('小明', '', 18, 18000, 6000)
p.save()
code1 = Pm.select('小明')
print(type(code1))
code1.menage() 

2.封装

  1. 封装 : 封装就是将复杂丑陋的隐私的细节隐藏到内部,也就是隐藏内部的实现细节,并提供给外部访问的接口

  2. 封装的目的:    1. 为了保证关键数据的安全性   2.对外部隐藏实现细节,隔离复杂度

  3. 封装的使用环境    1. 当一些数据不希望被外界直接修改时  2. 当有一些函数或属性不希望被外界使用的时候

  4. 使用方法:  私用化:  在属性或方法前加双下划线   特点: 外界不能直接访问   内部依然可以使用

class A:
    def __init__(self,name, age):
        self.name = name
        self.__age = age  # 私有属性

    def __say(self):  # 私有化方法
        print("from __say")


    def test(self):    # 在自身内可以直接访问私有属性与私有方法
        print(self.__age,)
        self.__say()

    def say(self):
        print('from say')


class B(A):
    pass

a = B('小明',18)

print(a.name)   # 小明
print(a.age)  # 报错.'B' object has no attribute 'age'
a.__say()  # 报错  'B' object has no attribute '__say'
a.say()   # from say
a.test()   # 18  from __say

  5. 在外界访问私有的内容,属性与函数虽然被封装了,,但是有时候我们还是需要在外界访问.这时候我们就可以通过定义方法来完成对私有属性的修改与访问

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


    def get_id_card(self):
        return self.__id_card


    def alter_id_card(self, new_id_card):
        if len(new_id_card) == 18 or len(new_id_card) == 15:
            print("修改成功")
            self.__id_card = new_id_card
        else:
            print("错误")


f = Person('a', 14, '11111111111')
f.id_card = 2222222  # 在外界临时修改,重新定义,内部并没有修改
print(f.id_card)  # 2222222

print(f.get_id_card())  # 在外部通过方法获取内置的私有方法
f.alter_id_card('222222222222222')  # 在外部定义方法来修改私有属性
print(f.id_card)    # 2222222
print(f.get_id_card())  # 222222222222222  内部修改成功

  6.在通过方法来修改或访问属性,本身并没有是什么问题,但是这给对象的使用者带来了麻烦,使用者必须要知道那些是普通属性,哪些是私有属性,并且使用不同的方式法来调用他们  ,然后python 就给我们提供了3个有关的装饰器

@property    获取属性方法
2  @key,setter  修改属性方法
3  @key.deleter 删除属性方法
@property可以将私有属性伪装成普通属性,与普通属性的访问方法一致
key是被property装饰的方法名称,也就是属性的名称,内部会创建一个对象,变量名称就是函数名称
所以再是由setter与deleter时必须保证使用对象的名称去调用方法所以时key.setter
class Person:
    def __init__(self,name,age ,id_card):
        self.name = name
        self.age = age
        self.__id_card = id_card


    @property
    def id_card(self):
        return self.__id_card

    @id_card.setter
    def id_card(self, new_id_card):
        if len(new_id_card) == 18 or len(new_id_card) == 15:
            print("修改成功")
            self.__id_card = new_id_card
        else:
            print("错误")


    @id_card.deleter
    def id_card(self):
        print('不能删')


f = Person('a',14,'11111111111')

print(f.id_card)  # 11111111111
f.id_card = "222222222222222"
print(f.id_card)  # 222222222222222
del f.id_card  # 不能删

  7.封装的原理: 就是再加载类的时候,把__方法或属性 替换成了_类名__方法或属性....  封装的实现原理本质就时替换了变量名称

  8.property 可以用来实现计算属性     计算属性的值不能直接获得,必须通过计算才能获得

# kg/m**2

class Person:

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

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


jay = Person(170,60)
print(jay.bmi)   # 0.0020761245674740486

  9.接口类:接口就是一组功能的集合,但是接口中仅包含功能的名字,不包含具体的实现代码.接口本质就时一套协议标准,遵循的对象就能被调用,接口的目的就时为了提高扩展性

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)

  10 抽象类 指的就是包含抽象方法,没有具体函数体方法的类.可以限制子类必须类中定义的抽象方法  导入abc模块

import abc
class A(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def open(self):
        pass
    @abc.abstractmethod
    def close(self):
        pass
    @abc.abstractmethod
    def  read(self):
        pass
    @abc.abstractmethod
    def write(self):
        pass

class Mouse(A):
    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(A):
    def open(self):
        print("键盘开机.....")

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

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

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

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

    总结:接口类就是一套协议规范,明确子类们应该具备哪些方法功能

       抽象类就是导入abc模块,强制要求子类必须按照协议中规定来实现

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

原文地址:https://www.cnblogs.com/yanglingyao/p/11251748.html