今日所学:
一、多态
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)内部的实现逻辑,外部无法知晓,这是真正意义上的封装
#封装的用法:明确区分内外。把自己的方法定义为私有的。(在函数名前加__)
正常情况:
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'}
以上。