面向对象进阶

判断一个对象是不是什么类型可以使用isinstance(对象名,类型名):

1 class Foo:
2     pass
3 obj = Foo()                #用类Foo实例化一个对象obj
4 print(isinstance(obj,Foo)) #判断实例化出来的对象obj是不是类Foo,结果返回为True
5 x = [ ]            #新建一个空列表
6 print(isinstance(x,list))  #判断x是不是一个列表,结果返回为True

结果截图:

 

判断一个类的父类的方法:

类名.__base__

判断一个类是不是另一个类的子类的方法:

issubclass(类名,类名) #返回值为Ture或False

反射

反射:以字符串的形式操作对象的相关属性,Python中的一切事物都是对象(都可以使用反射)

hasattr(对象名,属性名):以一个对象的字典形式判断是否有某个属性名,如果有就返回True,没有就返回False

getattr(对象名,属性名,得不到属性名的时候的返回值):以一个对象的字典形式获得某个属性名的值,如果是类或对象的数据属性,直接返回属性值,如果获得的是类的方法或者对象的绑定方法,这时候返回的就是方法的内存地址。

setattr(对象名,属性名,值):以一个对象的字典形式添加一个属性和对应的值

delattr(对象名,属性名):以一个对象的字典形式删除一个属性和对应的值

 1 class People:
 2     country = 'China'
 3     def __init__(self,name):
 4         self.name = name
 5     def walk(self):
 6         print('%s is walking'%self.name)
 7 p = People('egon')        #将类People实例化出一个属性名为egon的对象p
 8 print(People.__dict__)    #查看People这个类中的属性,以一个字典的形式返回
 9 
10 #hasattr(对象名,属性名)
11 hasattr(p,'name')  #如果有这个属性,返回True这句的原理是:print('name' in p.__dict__),也就是将属性name看做一个字典里的键
12 print(hasattr(p,'country'))       #同上,打印True
13 print(hasattr(People,'country'))  #同上,打印True
14 print(hasattr(People,'__init__')) #同上,打印True
15 
16 res = getattr(p,'country')       #在p这个对象的字典中得到对应键(country)的值给res
17 print(res)                       #打印结果为China
18 f = getattr(p,'walk')            #在p这个对象的字典中得到对应键(walk)的值给f
19 print(f)                         #打印f的结果为:<bound method People.walk of <__main__.People object at 0x0168FB70>>,这是一个绑定方法的名字
20 f1 = getattr(People,'walk')      #在People这个类的字典中得到对应键(walk)的值给f1
21 print(f1)                        #打印f1的结果为:<function People.walk at 0x01CD33D8>,这是一个类中的函数
22 f()                              #一个绑定方法的名字加上括号就能够运行,可以得到结果为:egon is walking
23 f1(p)                            #一个函数而不是绑定方法,所以需要手动传入一个对象参数进去,这样得到结果为:egon is walking

 

getattr(对象名,属性名,得不到属性名的时候的返回值):

当getattr()得不到属性名的时候,会将第三个参数内的值返回,以上的代码为基础举例说明:

print(getattr(p,'xxxxxxxx','无此属性'))

setsttr(对象名,属性名,值),以上面的代码为基础继续举例说明:

print(p.__dict__)
setattr(p,'age',18)
print(p.__dict__)

结果截图:

delattr(对象名,属性名),以上面的代码为基础继续举例说明:

print(p.__dict__)
delattr(p,'name')
print(p.__dict__)

结果截图:

 

 反射当前模块的属性:

 这里补充一点,我们所编写的源码文件的两种用途:

1、把一个文件当做一个程序独立运行,被称为脚本文件
2、把一个文件当做模块来到导入

下面举例详细说明反射当前模块属性是怎么一回事儿;

 1 #反射当前模块的属性
 2 import sys
 3 class Foo:
 4     pass
 5 def s1():
 6     print('s1')
 7 def s2():
 8     print('s2')
 9 this_module = sys.modules[__name__]    #拿到当前的模块
10 print(this_module)
结果截图:

拿到模块后可以判断是不是含有某某函数或属性

print(hasattr(this_module,'s1'))
print(getattr(this_module,'s2'))

结果截图:

print(this_module.s1)
print(this_module.s2)

反射的用途:
1、将字符串反射成命令来执行
2、可插拔机制

 将字符反射成命令执行

实际举例:

 1 def add():
 2     print('add')
 3 def change():
 4     print('change')
 5 def search():
 6     print('search')
 7 def delete():
 8     print('delete')
 9 
10 func_dic = {
11     'add':add,
12     'change': change,
13     'search':search,
14     'delete':delete
15 }
16 
17 while True:
18     cmd = input('>>:').strip()
19     if not cmd:continue
20     if cmd in func_dic:             #这句的本质就是:hasattr()
21         func = func_dic.get(cmd)    #这句的本质就是:func = getattr()
22         func()

所以以上代码我们还可以修改成:

 1 import sys
 2 def add():
 3     print('add')
 4 def change():
 5     print('change')
 6 def search():
 7     print('search')
 8 def delete():
 9     print('delete')
10 this_modules = sys.modules[__name__]
11 while True:
12     cmd = input('>>:').strip()
13     if not cmd:continue
14     if hasattr(this_modules,cmd):        
15         func = getattr(this_modules,cmd)   
16         func()
可插拔机制
可以通过判断其他模块有没有这个功能,如果有,可以先调用,如果没有可以先完成这个模块的功能,等另一个模块的功能完成编写后,调用方的代码不用更改

下面我们以一个ftp来实际举例说明:

客户端代码:

class FtpClient:
    'ftp客户端,但是还么有实现具体的功能'
    def __init__(self,addr):
        print('正在连接服务器[%s]' %addr)
        self.addr=addr
    def test(self):
        print('test')

服务端代码:

import ftpclient
f1=ftpclient.FtpClient('192.168.1.1')  #实例化一个客户端IP地址为192.168.1.1的对象
if hasattr(f1,'get'):  #判断这个对象里面有没有get属性这个绑定方法
    func=getattr(f1,'get')   #如果有就得到这个方法的内存地址并赋值给func
    func()                  #执行得到的get方法
else:
    print('其他逻辑')      #如果没有get绑定方法,就执行这里面的内容,我们可以自己写一个get方法,或者进行里面的逻辑

这样我们的运行结果是这样的:

如果后期客户端加入了get方法,这样服务端的代码无需做更改:

class FtpClient:
    'ftp客户端,但是还么有实现具体的功能'
    def __init__(self,addr):
        print('正在连接服务器[%s]' %addr)
        self.addr=addr
    def test(self):
        print('test')
    def get(self):
        print('get------->')

服务端运行结果:

通过字符串导入模块的方法:

 第一种:

m=input('请输入您要导入的模块:')
m1 = __import__(m)
print(m1.time())

第二种:

import importlib
t = importlib.import_module('time')
print(t.time())

两种结果一样:

attr系列: __setattr__,__getattr__,__delattr__

 __setattr__:添加/修改属性的时候会触发他的运行

class Foo:
    def __init__(self,name):
        self.name = name
    def __setattr__(self, key, value):
        print('key:%s,value:%s'%(key,value))
f1 = Foo('egon')        

执行结果为:

从以上结果我们可以看出,当__setattr__和__init__同时存在时,__setattr__被触发的优先级要高于__init__,所以我们看到了执行的结果

我们再添加一个新的属性来分析一下:

class Foo:
    def __init__(self,name):
        self.name = name
    def __setattr__(self, key, value):
        print('key:%s,type:%s'%(key,type(key)))
        print('value:%s,type:%s'%(value,type(value)))
        print('key:%s,value:%s'%(key,value))
f1 = Foo('egon')
f1.age = 18

结果截图:

从上面这段代码和结果截图我们可以看出,我们在实例化一个对象的时候和给一个新的属性赋值的时候同时触发了__setattr__(self, key, value)的执行,并且能够获得传入的key和value 的相应的数据类型,这时候我们要怎么操作才能算是真正的将新的属性添加到这个对象中呢?这时候我们可以做下面的修改:

class Foo:
    def __init__(self,name):
        self.name = name
    def __setattr__(self, key, value):
        print('key:%s,value:%s'%(key,value))
        self.__dict__[key] = value
f1 = Foo('egon')
print(f1.__dict__)
f1.age = 18
print(f1.__dict__)

结果截图:

我们从上图可以看出,已经将新的属性和值添加到了f1对象中,这时候我们还可以进一步做优化或者说是对数据的类型加以控制:

class Foo:
    def __init__(self,name):
        self.name = name
    def __setattr__(self, key, value):
        if not isinstance(value,str):
            raise TypeError('数据类型必须是字符串类型')
        print('key:%s,value:%s'%(key,value))
        self.__dict__[key] = value
f1 = Foo('egon')
print(f1.__dict__)
f1.age = 18
print(f1.__dict__)

结果如图:

经过修改值后:

 

 __delattr__:删除属性时会触发它的执行

同__setattr__的原理这里就不详细来说明了:

def __delattr__(self, item):
        self.__dict__.pop(item)

__getattr__:只有调用属性且属性不存在的时候才会被触发执行

 举例说明:

class Foo:
    def __init__(self,name):
        self.name = name
    def __getattr__(self, item):
        print('getattr:%s'%item)
f1 = Foo('egon')
print(f1.xxxxx)

结果截图:

 

 定制自己的类型

定制自己的类型本质就是在定义自己的类,下面我们用定义自己特有功能的列表类型举例说明:

#只能添加整型数字的列表
class List(list):
    def append(self, p_object):
        if not isinstance(p_object,int):
            raise TypeError('数据类型必须是整型数字')
        super().append(p_object)
l = List([1,2,3])
l.append(5)
print(l)

结果截图:

如果添加的不是整型数字就会报错:

 

 授权方式来实现自己的数据类型

 不是用继承,来实现函数的功能,这称之为授权方式实现自己的数据类型。

下面举例说明:

我们来实现一个带有系统时间的日志记录功能:

import time
class Open:
    def __init__(self,filepath,mode = 'r',encoding = 'utf-8'):
        self.f = open(filepath,mode=mode,encoding=encoding)
        self.filepath = filepath
        self.mode = mode
        self.encoding = encoding
    def write(self,value):
        t = time.strftime('%Y-%m-%d %X')
        self.f.write('%s  %s'%(t,value))
f = Open('1.txt',mode='w',encoding='utf-8')
f.write('aaaaaaa\n')
f.write('aaaaaaa\n')
f.write('aaaaaaa\n')
f.write('aaaaaaa\n')
f.write('aaaaaaa\n')

结果截图:

上面的代码只适合一种功能,如果我们还想使用open的其他功能,比如read(),close()等等,还记不记得我们之前出现过的__getattr__,现在我们就来使用它完成我们想要的功能,而不需要将open这个内置函数中句柄的函数再一一重写,下面请看:

import time
class Open:
    def __init__(self,filepath,mode = 'r',encoding = 'utf-8'):
        self.f = open(filepath,mode=mode,encoding=encoding)
        self.filepath = filepath
        self.mode = mode
        self.encoding = encoding
    def write(self,value):
        t = time.strftime('%Y-%m-%d %X')
        self.f.write('%s  %s'%(t,value))
    def __getattr__(self, item):
        return getattr(self.f,item)
f = Open('1.txt',mode='r',encoding='utf-8')
print(f.read())
print(f.seek(0))
print(f.readline())

结果截图:

以上就达到了我们想要的目的。

 其他知识点:

按格式获得时间可用这个方法:

定义函数的提示类型和查看方法:

 

 

posted @ 2017-04-24 19:30  不老玩童萧龙  阅读(186)  评论(0编辑  收藏  举报