day25 面向对象之多态和鸭子类型

1.封装方法

如何封装:给方法名称前面加上双下划线

    # ATM 的取款功能
# 1.插入银行卡 2.输入密码 3.选择取款金额  4.取款

class ATM:
    def __insert_card(self):
        print("插入银行卡...")
    def __input_pwd(self):
        print("输入密码...")
    def __select_money(self):
        print("选择取款金额...")
    def withdraw(self):
        self.__insert_card()
        self.__input_pwd()
        self.__select_money()
        print("取款成功!....")
    
atm = ATM()
atm.withdraw() # 外部调用这个简单的接口 就能完成一系列复杂的操作
# atm.select_money()  #直接调用内部的方法 是没有意义的无法完成整个功能

# 当然用户按照流程一一调用也可以完成功能 但是太麻烦
# atm.insert_card()
# atm.input_pwd()
# atm.select_money()

封装方法的好处:
    1.提高安全性
    2.隔离复杂度(将复杂的内容隔离到内部,外部只留下简单的接口,对于使用者,难度降低)

什么时候用封装

当一个类中某些属性或方法,不应该让外界直接访问,那就应该将其进行封装

封装的实现原理

本质上python并没有强行限制你的访问,而是通过语法转换来实现的封装

class Person:
    def __init__(self,name,sex,age,idCard):
        self.name = name
        self.sex = sex
        self.__age = age
        self.__idCard = idCard
    
    def get_idCard(self):
        return self.__idCard

    def __test(self):
        pass

    print("aaaaaaaaaa")

p = Person("比尔盖茨","男",20,"322323232332332")

# print(p.__idCard)
p.__idCard = "XXXXXX"
print(p.__idCard)

print(p.get_idCard())

print(p.__dict__)
print(Person.__dict__)

p.__xxxxxxxxxxxx = 1

print(p.__dict__)



1.通过__dict__可以发现,私有的属性名称前自动加上了_类名,python就是通过这样的转换方式来实现封装
2.只有在类的内部的双下划线才会被自动转换,并且这个转换过程只执行一次,在类丁已完成后,在添加的双下划线开头的
3.父类中私有的方法,子类中无法使用

子类无法覆盖父类的私有方法

class  A:
    def f(self):
        self.__f1()   #_A__f1

    def __f1(self):
        print("A  __f1")

class B(A):
    def __f1(self):  # _B__f1
        print("B __f1")

    def f2(self):
        self.f()

b = B()
b.f2()

# 之所以无法覆盖 是因为 子类和父类中的私有方法 名称必然不相同 所以无法覆盖   子类的方法一定子类独有的 因为名称不同


class A:
__age = 10

# 绕过封装的限制直接访问  这是毫无意义的
print(A._A__age)

使用property可以将一个方法伪装成一个属性

当一些属性的值不是固定的,而是通过计算得来的时候,我们必须为这个属性增加方法,才能完成计算,但是一旦使用方法后,该属性的访问,就变成了方法调用,很明显与其他的属性访问方式不同,这样会给使用者造成迷惑
所有需要将这个方法伪装成普通属性,这就用到了Property

property可以将方法伪装成属性,利用这个特点,我们也可以将其使用到封装中
之前没有这个装饰器我们需要为私有的属性,提供两个方法,但是这样一来访问私有属性时的方式就发生了变化
这时候可以使用property来进行伪装,使得访问私有属性与访问普通属性的方式一致
另外 property还提供了 setter(用于修改属性的值)  和 deleter(删除属性的值)

# BIM案例:
class Person:
    def __init__(self,name,weight,height):
        self.name = name
        self.weight = weight
        self.height = height
        # self.bmi = weight/(height*height)
    # def bmi(self):
    #     return self.weight / (self.height * self.height)
    @property
    def bmi(self):
    return self.weight / (self.height * self.height)

p = Person("尔晴",50,1.5)
# print(p.bmi)
# p.weight = 90
# print(p.bmi)

# 现在 虽然可以实现需求 但是我们把一个属性变成了一个行为 这是不合理的
# print(p.bmi())
# p.weight = 90
# print(p.bmi())

#  使用property装饰器 可以将一个方法伪装成一个属性
print(p.bmi)
p.height += 0.2
print(p.bmi)


class Student:
def __init__(self,name,sex,idCard):
    self.name = name
    self.sex = sex
    self.__idCard = idCard

def get_idCard(self):
    return self.__idCard

def set_idCard(self,new_id):
    self.__idCard = new_id

@property # 需要掌握
def idCard(self):
    return self.__idCard

@idCard.setter #了解的
def idCard(self,new_id):
    self.__idCard = new_id

@idCard.deleter # 了解的
def idCard(self):
    print("身份证属性被删除了.....")
    del self.__idCard

stu = Student("尔康","男","323254554554")

# print(stu.get_idCard())

# stu.set_idCard("xxxx")

print(stu.get_idCard()) # 使用装饰器前
print(stu.name) # 普通属性的访问

print(stu.idCard) # 使用装饰器后

stu.idCard = "aaaaaaa" # 使用装饰器后的修改操作

print(stu.idCard)

del stu.idCard

print(stu.__dict__)
print(Student.__dict__)

封装的特点

被隐藏的内容 在内部是可以直接访问,外部无法访问

2. 多态

什么是多态
    多种状态,形态
    生活中具备多种形态的事物,水(水蒸气,冰,液态水),奥特曼,数码宝贝
    一种事物,具备多种形态或状态,就称之为多态
    官方解释:不同对象,可以响应同一方法,并作出不同的行为,产生不同结果
    
    如何实现多态?
        让几个不同类拥有相同的父类,这样一来他们就具备了相同的方法,每个子类覆盖父类的方法
class Animal:
def eat(self):
    print("动物在吃东西...")
def sleep(self):
    print("动物在睡觉...")
def drink(self):
    print("动物需要水.....")

class Person(Animal):
    def eat(self):
        print("人吃粮食...")

class Pig(Animal):
    def eat(self):
        print("猪吃饲料...")

class Dog(Animal):
    def eat(self):
        print("狗吃骨头...")

person = Person()
pig = Pig()
dog = Dog()

person.eat()
pig.eat()
dog.eat()

#假设你学习了C1驾照  意味着 所有C1类的汽车都能开  因为每种C1汽车的驾驶方式相同

# 当使用了多态之后 对象的使用者不需要关系这个对象具体的实现,只需要知道该对象属于哪个基类,就能直接使用它
# 如此扩展性变高了

class Phone:
def call(self):
    print("手机就能打电话..")

def send_msg(self):
    print("手机能发短信..")

class WindowsPhone(Phone):
    def call(self):
        print("拨号打电话..")

def send_msg(self):
    print("输入号码发短信..")

class IPhone(Phone):
    def call(self):
        print("拨号打电话..")

def send_msg(self):
    print("输入号码发短信..")

# 可以定义一个方法接受一个手机为参数 无论是是类型的手机 都可以被使用

def CALL(phone):
    phone.call()
    
wp = WindowsPhone()
ipx = IPhone()

CALL(wp)
CALL(ipx)

# 系统内置的方法有很多都体现了多态

print(len("abc"))
print(len([1,2,3,4,]))
print(len({"name":"123","sex":"man"}))

print("abc".__len__())
print([1,2,3,4,].__len__())
print({"name":"123","sex":"man"}.__len__())
print(len({1,23,4,5}))

多态值abc模块

多态是多个类的对象拥有相同的方法,但是我们没有从语法严格要求说必须提供这些方法,子类完全可以不提供这些方法
#现在要做的就是 严格要求 子类必须实现父类声明的方法

import abc 
#abc是abstract class ,是抽象类的缩写,抽象的意思是 不清晰 不具体 看不懂

不清晰不具体的类,就是抽象类

使用abc模块来限制子类的步骤
1.为类中指定元类为abc.ABCMeta
2.在相应的方法上加上abc.abstractmethod

class Animal(metaclass=abc.ABCMeta):

@abc.abstractmethod
def eat(self):
    pass

@abc.abstractmethod
def drink(self):
    pass

class Cat(Animal):
    def eat(self):
        print("猫爱吃鱼肉...")

def drink(self):
    print("用舌头舔..")

class Dog(Animal):
    def eat(self):
        print("狗爱吃骨头...")
    def drink(self):
        print("用舌头舔..")

class Pig(Animal):
    def eat(self):
        print("猪 爱吃草...")

def drink(self):
    print("用嘴吸的..")
    
p = Pig()
# p.eat()

c = Cat()
# c.eat()

# 多态的好处 完全不需要考虑得到的对象时声明类型 只要知道了其基类中的内容就能使用
def feeding(animal):
    animal.eat()
    animal.drink()

feeding(c)
feeding(p)
# 多态中的基类 相当于(协议 标准 规范) 要求子类必须满足这些标准

多态练习

import abc

# 电脑基类
class Computer(metaclass=abc.ABCMeta):

@abc.abstractmethod
def open(self):
    pass

@abc.abstractmethod
def shutdown(self):
    pass

class DesktopComputer(Computer):
    def open(self):
        print("台式机正在启动....")

def shutdown(self):
    print("台式机正在关机....")

class Worker:
    def working(self,pc):
        # 先开机
        pc.open()
        print("工作中.....")
        pc.shutdown()

w1 = Worker()

dp = DesktopComputer()

w1.working(dp)

# 增加了笔记本电脑
class BookComputer(Computer):

def open(self):
    print("笔记本正在启动....")

def shutdown(self):
    print("笔记本正在关机....")

bc = BookComputer()
w1.working(bc)

class PadComputer(Computer):
    def open(self):
        print("平板正在启动....")

    def shutdown(self):
        print("平板正在关机....")

bc = PadComputer()
w1.working(bc)

3. 鸭子类型

python推崇简单的编程方式
鸭子类型,如果一个对象叫声像鸭子,走路也像鸭子,那就把它当成鸭子

对应到代码中,就是只要你的行为一样,那就把你当成一个类型来看

class Duck:

def bark(self):
    print("鸭子嘎嘎叫...")

def run(self):
    print("摇摇晃晃走....")

class Chicken:
    def bark(self):
        print("鸡咯咯叫...")

    def run(self):
        print("摇摇晃晃走....")

def test(obj):
    obj.bark()
    obj.run()

duck = Duck()
c = Chicken()

test(duck)
test(c)

如果你足够自觉,你可以不适用abc模块,也不需要公共的基类,自觉地将方法名都写成一样,同样可以实现多态
这种方式称之为鸭子类型
原文地址:https://www.cnblogs.com/shanau2/p/10137415.html