今日所学:

 

一、多态

1、定义:多态是指对象如何通过他们共同的属性和动作来操作及访问,而不需要考虑他们具体的类。

多态表明了动态绑定的状态,不同的对象调用同一种方法,python本身就是多态的。

2、多态和继承的关系:

多态是基于继承而来的。

多态的继承有两层含义:(1)改变  (2)扩展

多态是类的这两层意义的恶一个具体的实现机制,即调用不同的实例化的对象下的相同的方法,实现的过程不一样。

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

    def func(obj):
        obj.talk()

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

p1=People()    #实例化
d1=Dog()
z1=Pig()

p1.talk()      #子类调用基类的属性
d1.talk()
z1.talk()

p1.func()     #也可以定义一个接口函数func(),然后在该函数调用talk方法,则在调用func时就相当于调用talk函数
d1.func()     #这就是多态,基于继承而来,且不同实例化的对象在调用方法时实现方式有多种
z1.func()


>>>
say hello
say wangwang
say aoao
say hello
say wangwang
say aoao

python本身就是多态的。

#str,list,tuple都是序列类型
s=str('hello')
l=list([1,2,3])
t=tuple((4,5,6))

print(s.__len__())
print(l.__len__())
print(t.__len__())
#两种不同的调用方法,平时所用的len()函数就相当于调用了对象下的__len__函数
print(len(s))
print(len(l))
print(len(t))

>>>
5
3
3
5
3
3

 

二、封装

1、三种含义:

(1)类就是一个麻袋,本身就是一个封装

(2)用单下划线或双下划线把属性定义为私有(约定说外部不能调用,但仍是可以调用的)

(3)内部的实现逻辑,外部无法知晓,这是真正意义上的封装

python 多态 python多态的含义和作用_多态

python 多态 python多态的含义和作用_下划线_02

#封装的用法:明确区分内外。把自己的方法定义为私有的。(在函数名前加__)
正常情况:
class A:
    def fa(self):
        print("from A")

    def test(self):
        self.fa()

class B(A):
    def fa(self):
        print("from B")

b1=B()
b1.test()    #若父类和子类同名,则会先在子类中寻找
#如果只要调用父类的函数,则就可以使用封装,把自己的方法定义为私有的。
封装后
class A:
    def __fa(self):     #在加__后,会自动定义成_类名__fa,在属性字典中即可看到
        print("from A")

    def _mu(self):      #单下划线也可以用来表示私有,但只是一种约定而已,外部仍可以调用得到
        print("我是单下划线")

    def test(self):
        self.__fa()

class B(A):
    def fa(self):
        print("from B")

b1=B()
b1.test()
b1._mu()      
b1._A__fa()    #子类不能访问__fa(),但可以访问_A__fa()。
print(A.__dict__)


>>>
from B
from A
我是单下划线
from A
{'__module__': '__main__', '_A__fa': <function A.__fa at 0x000001E1E685B670>, '_mu': <function A._mu at 0x000001E1E685B700>, 'test': <function A.test at 0x000001E1E685B790>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

View Code

2、封装的真谛在于明确地区分内外,封装的属性可以直接在内部(类中)使用,而不能被外部(调用者)直接使用。

然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要为其开辟接口,即在类中设置接口函数(访问函数),让外部能够间接地用到我们隐藏起来的属性

#类的设计者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self):            #接口函数,对外提供的接口,隐藏了内部的实现细节
        return self.__width * self.__length    #内部可以调用内部的逻辑,但外部使用者则不能看到该实现逻辑


#使用者
r1=Room('卧室','egon',20,20,20)
a=r1.tell_area() #使用者调用接口tell_area
print(a)       

--> 400

#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
        return self.__width * self.__length * self.__high


#对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
r1=Room('卧室','egon',20,20,20)
b=r1.tell_area()
print(b)

-->8000

3、封装不要滥用,如真需要隐藏起来才封装,如果先隐藏了,后期要通过一个个接口函数去调出来方法,相当于把类打了一个个孔,最终写出来的类会乱七八糟的。

4、封装可以用来封装数据、方法,其中封装方法目的是为了隔离复杂度。

#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
#隔离了复杂度,同时也提升了安全性

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()

a=ATM()
a.withdraw()

 

三、反射(自省)

1、反射指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

2、4个可以实现自省的函数

#1、hasattr()判断object中有没有一个name字符串对应的方法或属性,返回值是一个布尔值
class A:
    def __init__(self,name):
        self.name=name

    def test(self):
        print("我的名字是%s" %self.name)

class B(A):
    def __init__(self):
        pass


b1=B()
print(hasattr(b1,"test"))
print(hasattr(b1,"test1111"))

>>>
True
False

 

#2、getattr()是用来获取属性
class A:
    def __init__(self,name):
        self.name=name

    def test(self):
        print("我的名字是%s" %self.name)


b1=A("alex")

print(getattr(b1,"test"))   #获取属性test()
func=getattr(b1,"test")     #可为其赋予函数,然后运行该函数
func()
#getattr还有第三个参数,可用来传入若找不到属性可输出的内容,默认报错
print(getattr(b1,"asflka","找不到啊"))    #第三个参数设置找不到是输出的内容


>>>
<bound method A.test of <__main__.A object at 0x000002AB12C9AA60>>
我的名字是alex
找不到啊

 

#3、setattr()是用来设置属性
class A:
    def __init__(self,name):
        self.name=name

    def test(self):
        print("我的名字是%s" %self.name)


b1=A("alex")
setattr(b1,"happy","asjdhaf")     #三个参数:类名、实例;属性名;属性内容。缺一不可
print(b1.__dict__)
setattr(b1,'show_name',lambda self:self.name+'sb')     #属性内容也可设置为函数
print(b1.show_name(b1))        #在添加属性后,也可以用同样的方法去调用

>>>
{'name': 'alex', 'happy': 'asjdhaf'}
alexsb

 

#4、delattr()是用来删除属性
class A:
    def __init__(self,name):
        self.name=name

    def test(self):
        print("我的名字是%s" %self.name)


b1=A("alex")
setattr(b1,"happy","asjdhaf")     #三个参数:类名、实例;属性名;属性内容。缺一不可
print(b1.__dict__)
setattr(b1,'show_name',lambda self:self.name+'sb')     #属性内容也可设置为函数
print(b1.show_name(b1))        #在添加属性后,也可以用同样的方法去调用

delattr(b1,"show_name")      #删除属性,类名/实例;属性名
print(b1.__dict__)


>>>
{'name': 'alex', 'happy': 'asjdhaf'}
alexsb
{'name': 'alex', 'happy': 'asjdhaf'}

 

以上。