多态和封装
python 天生支持多态
导引
在其他语言中
class Alipay: def pay(self,money): print('使用支付宝支付了%s元' % money) class Appplepay: def pay(self,money): print('使用Appplepay支付了%s元' % money) def pay(Applepay,pay_obj,int money):#其他语言里面,这里需要传一个数据类型,但是代码里面既有Alipay又有Appplepay,只能传一个。 pay_obj.pay(money)
那么怎么做?
class Payment:pass class Alipay(Payment): def pay(self,money): print('使用支付宝支付了%s元' % money) class Appplepay(Payment): def pay(self,money): print('使用Appplepay支付了%s元' % money) def pay(Payment,pay_obj,int money):#直接以Payment代替 pay_obj.pay(money)
其中class Payment:pass,这就是多态
但是python天生支持多态,所以就不用写这些
class Alipay(): def pay(self,money): print('使用支付宝支付了%s元' % money) class Appplepay(): def pay(self,money): print('使用Appplepay支付了%s元' % money) def pay(pay_obj,money):#直接以Payment代替 pay_obj.pay(money)
动态的定义
多态指的是一类事物有多种形态
比如动物有多种形态:人,狗,猪等
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass 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')
比如上面就是Animal类的多种形态
文件有多种形态:文本文件,可执行文件
import abc class File(metaclass=abc.ABCMeta): #同一类事物:文件 @abc.abstractmethod def click(self): pass class Text(File): #文件的形态之一:文本文件 def click(self): print('open file') class ExeFile(File): #文件的形态之二:可执行文件 def click(self): print('execute file')
多态性
比如上面支付的代码:可以使用payment一种方法同时实现Applepay和Alipay,这个就是多态性
class List: def __len__(self):pass class Tuple: def __len__(self):pass #上面两个类很相似,实现了相同的方法,所以将这种情形叫做鸭子类型 def len(l_t):#因为python是一个强动态类型的语言,如果是其他强类型语言,那么这里就必须指定是list还是tuple,不能直接写参数;为为了方便就必须在代码前面协商一个fu类型来统指代这里 return l_t.__len__() l=list() len(l)
其他语言怎么操作
class Foo:pass class List(Foo): def __len__(self):pass class Tuple(Foo): def __len__(self):pass #上面两个类很相似,实现了相同的方法,所以将这种情形叫做鸭子类型 def len(Foo,l_t):#这里就使用Foo类来代理 return l_t.__len__() l=list() len(l)
python是一个动态强类型的语言:
不崇尚根据继承所得来的相似
只是自己实现自己的代码即可
如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型
list,tuple这种相似,是自己写代码的时候约束的,而不是通过父类约束的,也就是说,list和tuple两种数据类型的操作模式非常类似,但是彼此独立,比如删掉list中的str功能,并不影响tuple,这种关系又被称为松耦合。
优点:松耦合,每个相似的类之间都没有影响
缺点:太随意,如果
接口类和抽象类在python中的应用并不是非常必要
封装
广义上面向对象的封装:代码的保护,面向对象的思想本身就是一种
只让自己的对象能调用自己类中的方法
狭义上的封装——面向对象的三大特征之一
属性和方法都隐藏起来,不让你看见
class Person: def __init__(self,name,password): self.name=name self.password=password alex=Person('alex','alex341') print(alex.password)
假设我们不想让别人打印出密码,但是代码内部仍需要使用则
class Person: def __init__(self,name,password): self.name=name self.__password=password#在password前面加上__,这就能够将password私有,无法打印 alex=Person('alex','alex341') print(alex.password)
调用字典,打印结果是这样
class Person: def __init__(self,name,password): self.name=name self.__password=password#在password前面加上__,这就能够将password私有,无法打印 alex=Person('alex','alex341') print(alex.__dict__) D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py alex341 {'name': 'alex', '_Person__password': 'alex341'} Process finished with exit code 0
那么也就是可以
class Person: def __init__(self,name,password): self.name=name self.__password=password#在password前面加上__,这就能够将password私有,无法打印 alex=Person('alex','alex341') print(alex.__dict__) print(alex._Person__password)#通过_Person__password方法调用
进一步
class Person: def __init__(self,name,password): self.name=name self.__password=password#在password前面加上__,这就能够将password私有,无法打印 def get_pwd(self): return self.__password alex=Person('alex','alex341') print(alex.__dict__) print(alex._Person__password) print(alex.get_pwd()) #新的问题,为何明明是__password,为何生成的是_Person__password? #因为这是在python类的内部才会这样,自动实现转换
那么如何证明只在类内部才有这个功能?
class Person: def __init__(self,name,password): self.name=name self.__password=password#在password前面加上__,这就能够将password私有,无法打印 def get_pwd(self): print(self.__dict__)#得到的是__high而非_Person__high return self.__password alex=Person('alex','alex341') alex.__high=1 print(alex.get_pwd())
私有静态属性、私有对象、私有方法
class Person: __key=123#私有静态属性 def __init__(self,name,password): self.name=name self.__password=password#私有对象 def __get_pwd(self):#私有方法 print(self.__dict__)#得到的是__high而非_Person__high return self.__password alex=Person('alex','alex341') alex.__high=1 print(alex.get_pwd())
当定义了私有以后,只能在类的内部调用,而不能在外部调用,那是因为发生了改变:
比如表面上是将password变为了__password,但是实际上却是变化了_Person__password,发生了变形,所以外面调不到
封装的应用
class Room: def __init__(self,name,length,width): self.name = name self.__length=length#这里做了隐藏 self.__width = width def area(self): return self.__length*self.__width Jin=Room('金老板',2,4) print(Jin.area())
延申
class Room: def __init__(self,name,length,width): self.__name = name self.__length=length#这里做了隐藏 self.__width = width #下面的get和set方法在c++里面很常用,python中用途较少,在实际编程过程中可以根据自己的实际情况选择合适的编程方法 def get_name(self): return self.__name#因为上面已经对name进行了封装,所以必须要写这个函数,这样才能实现在外部调用name def set_name(self,Newname): if type(Newname) is str and Newname.isdigit()==False: self.__name=Newname else: print('名字不合法') def area(self): return self.__length*self.__width Jin=Room('金老板',2,4) print(Jin.area()) Jin.set_name('2') print(Jin.get_name())
新的问题:父类的私有属性能被子类调用吗?
class Foo: __key='123'#这里封装以后,实际结果变为_Foo__key class Son(Foo): print(Foo.__key)#而这里实际上是_Son__key,所以调不到,代码报错 #如果子类能够调用父类,那么代码执行结果就是123,但是结果显示代码报错 D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py Traceback (most recent call last): File "F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py", line 4, in <module> class Son(Foo): File "F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py", line 5, in Son print(Foo.__key) AttributeError: type object 'Foo' has no attribute '_Son__key' Process finished with exit code 1
也就是再次验证,私有化以后只能在自己的类中调用,不能在类的外面,更不能在其他类中调用
会用到私有这个概念的场景:
隐藏起一个属性,不想让类的外部调用;
我想保护这个属性,不想让属性随意改变
我想保护这个属性,不被子类继承