博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python基础 ( 十 ) —— 面向对象(多态、封装、反射、动态导入)
阅读量:5162 次
发布时间:2019-06-13

本文共 6396 字,大约阅读时间需要 21 分钟。

#面向对象的三大特性

1 继承(上一章的内容)

2 多态

python本身就是多态的

3 封装

# 多态

#不同类的实例化对象,调用同一个方法(执行的逻辑不同),而不用考虑他们具体的类,例如:

字符对象s和列表对象l都调用了同一个__len__的方法(都继承于他们的父类即str和list),却不需要考虑他们属于不同类

s = str(111)          #相当于str这个类将数字1传入,实例化出来一个字符串对象l = list('abc')       #相当于list这个类将'abc'传入,实例化出来一个列表对象len(s)                #相当于字符对象s调用了字符类下的一个__len__的方法,即s.__len__()print(s.__len__())len(l)                #同理print(l.__len__()) print(dir(str))     #里面有__len__ print(dir(list))    #里面有__len__

#其实len函数相当于

def len(obj) :    obj.__len__()

#多态反应的是一个执行时候的状态

#再举一个例子加深理解

class H20 :    def __init__(self,temperature):        self.temperature = temperature    def trans(self):        if self.temperature < 0 :            print('freezing')        if self.temperature > 0 and self.temperature < 100 :            print('flowing')        if self.temperature > 100 :            print('boiling')class liquid(H20):    passclass solid(H20) :    passs1 = liquid(110)s2 = liquid(20)s1.trans()                          #都调用了同一种方法,却属于不同类s2.trans()def trans(obj) :             #其实这里定义了这个函数,才叫真正的多态。当你不调用时,没差别;而当你调用这个函数时,不管是什么类实例化出来的对象,都能执行同一套方法。    obj.trans()trans(s1)trans(s2)

 到此,我们也就明白到底什么是多态了:不同子类继承了父类的共同的函数属性,并且不同子类实例化出来的不同对象都调用父类的函数属性,这种动态的过程,就叫做多态。其实多态的基础就是就是继承,就是一种继承的体现方式。

换句话说,继承解决的是代码重用的问题,而把这个优势体现出来的方式就叫做多态,即父类定义的函数,要考虑到多个子类的继承问题(需要是有意义的)

#封装

#封装是一种思想(本质就是要区别内外,内部就是类里面,外部就是调用者;外部调用者无需知道内部逻辑),装就是把东西都放到一个袋子里,封就是把袋子封口,将东西隐藏起来,也就是定义一个类,往里面定义数据属性和函数属性,这就叫装(装到了__dict__里)

#那么什么是封呢?

(类中定义私有的,只想让其在类内部被访问,外部不需访问,但需要给外部定义一个接口函数,用于外部访问内部)

1 在一个py文件里定义的类,由其他py文件调用,其实就已经有隐藏的含义了,调用者并不知道其内部逻辑,只需要用就行

2 通过python约定俗成的命名方法来命名,达到隐藏的效果:

3 为封装到内部的逻辑提供一个外部访问的接口

# _开头的变量如 _abc 代表这个属性不该被外部调用者调用

class A :    _b = '你不该在外部调用这个数据属性'    def __init__(self):        passa = A()print(a._b)                             #还是能调用到这个_b,因为这只是文字上的约束,python并没有内部逻辑帮你阻止调用                                        #但你看到_开头的就要明白,这个属性是不该被你调用的

#__开头的变量如__abc (__abc__这样末尾有__的不算),python会给你自动重命名为_类名__abc

class A : __b = '你调用不到我,会报错'    def __init__(self):        print('但我可以在这里调',self.__b) def C(self):                  #接口函数(访问函数)        print(self.__b,'内部就能调用')a = A()# print(a.__b)                             #调用不到__b,会报错 ,因为以及被python自动重命名为_A__b了,可以在__dict__中找到print(A.__dict__)print(a._A__b)a.C()                          #外部调用者通过调用接口函数,访问到了在内部被封装的属性

#注意事项:

要把什么变量做成私有的需要深思熟虑(不然后期只能通过不断的添加接口函数来弥补,在一个项目里把私有的变量名再改回去是不现实的)

 #来几个真正封装的例子

class cal_area :    def __init__(self,name,width,length):        self.name = name        self.__width = width        self.__length = length    def result(self):        print('%s的面积为%s平方米' %(self.name,self.__length*self.__width))    def port(self):        return self.__width,self.__lengths = cal_area('home',100,100)s.result()print(s.port())                             #没法直接调用内部私有的__width和__length,只能通过新写的接口函数(也叫访问函数)来调用

 #反射(又称自省)

 #主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

四个可以实现反射的函数(适用于类和对象):

hasattr(object,name):判断对象中有没有一个叫做name的方法或属性(数据属性或函数属性),

或者换句话说,能不能调用到name的方法或属性,返回bool值.                    注意name是个字符串!

a = 'abc'print(hasattr(a,'__len__'))                #可以传入实例化对象print(hasattr(str,'__len__'))              #也可以传入一个类class A:    def __init__(self,name):        self.__name = name    def __test(self):        passprint(hasattr(A,'__test'))                 #hasattr检测的是能被调用的方法或属性,这里做了封装所以没找到

getattr(object,name,default=None):获取对象或者类中能被调用的方法或者属性

a = 'abc'b = getattr(a,'__len__')                                #在实例化对象a中,获取到叫做'__len__'的属性,并且可以调用.(没有则会报错)d = getattr(a,'adflkj','如果找不到,得到这里的值')      #这样写找不到也不会报错e = getattr(a,'adsf',default=None)print(b())print(d)c = getattr(str,'__len__')print(c(a))#其实getattr(object,name)就等于object.name

setattr(object,key,value):给对象或类设置方法或属性

class A:    def __init__(self,name):        self.name = name    def test(self):        passa = A('abc')setattr(a,'name0','ssy')print(a.__dict__)#相当于直接a.name = 'alex'print(a.__dict__)#也可传入函数属性def test(self) :    print(self.name)setattr(A,'test',test)a.test()

delattr(object,key):删除对象或类的属性或方法,相当于 del A.key

class A:    def __init__(self,name):        self.name = name    def test(self):        passa = A('abc')delattr(a,'name')#相当于直接del a.nameprint(a.__dict__)

 #反射的好处:

当多人开发一个项目时候,你需要调用别人负责实现的的类,但是对方没实现,甚至都没定义这时候怎么办?

难道要等到他实现了通知你,你才开始写代码么?这时候就可以用hasattr来进行一个判断

from XXX import Aa = A('abc')       #调用别人的类来实例化if hasattr(a,'你要调用的方法') :    b = getattr(a,'你要调用的方法')    b()            #运行调用的方法#如果不存在,就接着下面的逻辑

这样,当你调用的类不存在时,就跳过;存在的时候即调用;你这段调用的代码其实就已经写好了,不论这个类有没有被实现。

换句话说就是你不用等别人实现这个类,再回过头来写这个调用逻辑了。

#动态导入

#以一个字符串的名字的形式导入模块

#动态导入,可以导入字符串名字的模块。但是无论你调用多少层,总是获取最顶层a = __import__('module_test.bin')       #导入相当于执行了一遍文件.print(a)                                #获取的不是你导入的最低层bin,而是顶层module_testa.bin.test()                            #所以得这么才能调用到,但是为了能调用到,__import__里还是得写到最低层,即'module_test.bin'#导入的过程相当于执行了一次文件,下面拿以前的导入方式来距离from module_test import binbin.test()                              #bin下定义的函数得通过bin这个类调用,不能直接test()#或者直接把模块里的所有东西导入到当前,那就可以直接test()调用了from module_test.bin import *           # * 跟linux里的通配符一个意思test()#但是这种用*的方法没法导入_开头的函数_test()                                 #没找到这个函数#不过python没有在真正逻辑层面上限制你调用,你可以这么调_testfrom module_test.bin import test,_test_test()#或者用__import__调用也可以a.bin._test()#另一种更好的方法动态导入import importliba = importlib.import_module('module_test.bin')a.test()a._test()

 #为什么这里要讲动态导入呢?因为动态导入就是基于反射做的。而模块其实就是一个类

比如 importlib.import_module('module_test.bin'),相当于从importlib这个类里找到一个import_module方法;

这个方法能从当前类(文件夹就是一个类)下找到module_test这个类,再从中找到bin这个类

这也就解释了为什么pycharm创建文件夹的时候,会生成一个__init__的文件,并且自动执行,其实它就是用来实例化出对象(文件夹里的文件)的

 # __getattr__ 、 __setattr__ 、 __delattr__      

(注意:形如  __XXX__ 都是类的内置方法,当不自己设置__XXX__的时候用内置的,设置跟他名字一样的__XXX__则用你设置的)

 (常用)#__getattr__ : 当对象或类调用不存在的方法时,会自动运行类下的__getattr__方法 (如果不像下面自己设置__getattr__则调用内置的结果是输出报错信息到屏幕)

(不常用)#__delattr__: 类或对象在删除属性时,会自动运行__delattr__

(不常用)#__setattr__: 类或对象在设置属性时,会自动运行__setattr__

class Foo :    name = 'abc'    def __init__(self,x):        self.x = x    def __getattr__(self, item):        print('__getattr__被执行')    def __delattr__(self, item):        print("__delattr__被执行")    def __setattr__(self, key, value):        print("__setattr__被执行")        # self.key = value              #不能这么设置属性,因为这里一设置又会触发__setattr__的执行,然后再设置,无限循环        self.__dict__[key] = value      #这样设置才对a = Foo(1)a.adfdaf                                #类或对象调用不存在的属性时,会自动运行__getattr__del a.name                              #类或对象在删除属性时,会自动运行__delattr__a.name1 = 'cba'                         #触发__setattr__

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/Matrixssy/p/10911952.html

你可能感兴趣的文章
python3.6.3安装步骤,适用linux centos系统
查看>>
没有终结点在侦听可以接受消息的*这通常是由于不正确的地址或者 SOAP操作导致的...
查看>>
HTML5---15.网络接口
查看>>
接收xml请求流并解析字符串的例子
查看>>
中文字符串分隔的注意问题
查看>>
zip打包是去掉路径
查看>>
常用的经典jquery代码[转]
查看>>
正则判断
查看>>
转--RTP如何打包H264数据
查看>>
IOC及AOP实现原理
查看>>
CocoaPods安装和使用教程
查看>>
WordPress博客搭建与问题总结
查看>>
C#中 property 与 attribute的区别
查看>>
POJ 1988 Cube Stacking(带权并查集)
查看>>
VMware vSphere虚拟化-VMware ESXi 5.5组件安装过程记录
查看>>
HDU 4960 Handling the past 2014 多校9 线段树
查看>>
时间管理-未经思考的人生不值得过
查看>>
cf 749D Leaving Auction
查看>>
[习题]验证控件(Validator)搭配 当地语系(Localization)
查看>>
XidianOJ 1213 小V的滑板鞋
查看>>