python基础——面向对象进阶

时间:2021-04-16 22:43:11

 

python基础——面向对象进阶

 

1、isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

 

class Foo:
    pass
obj=Foo()
print(isinstance(obj,Foo))  #判断是不是类的对象

 

输出结果为:

True

 

issubclass(sub, super)检查sub类是否是 super 类的派生类

 

class Bar():
    pass
class Foo(Bar):
    pass

print(issubclass(Foo,Bar)) #判断FOO 是不是Bar的儿子类

 

输出结果为:

True

 

2、反射 

 

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

 

四个可以实现自省的函数

下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

 

hasattr()  判断object中有没有一个name字符串对应的方法或属性 

class People:
    county='China'
    def __init__(self,name):
        self.name=name
p=People('egon')
print(hasattr(p,"name"))

输出结果为: 

True

 

getattr()  获取到 并有一个返回值, 通过字符串来获取属性,来判断有没有属性,来操作关于属性问题

 

getattr(p,'country')
res=getattr(p,'country')  #res=p.country
print(res)

res=getattr(p,'name')
print(res)

 

输出结果为:

China
egon

 

获取一个类里面不存在的值情况

 

class People:
    country='China'
    def __init__(self,name):
        self.name=name
    def walk(self):
        print('%s is walking'%self.name)
p=People('egon')
print(getattr(p,'xxxx')) #报错
getattr(p,'xxxxxx','这个属性确实不存在') #没有报错 可以加个参数
print(getattr(p,'xxxxxx','这个属性确实不存在'))
   #可以给getattr在加一个参数,在你获取不到属性的情况下,
   # 把前面的参数作为返回值返回,保证程序不会抛出异常

 

输出结果为:

AttributeError: 'People' object has no attribute 'xxxx' #报错
这个属性确实不存在

 

可以判断有没有这个属性,有的话 就直接打印出来( hasattr getattr #是最常用的 )

 

if hasattr(p,'walk'):
    func=getattr(p,'walk')
    func()
print('----->')
print('----->')

 

输出结果为:

egon is walking
----->
----->

stettr()
设置 用字符串去代替属性
class People:
    country='China'
    def __init__(self,name):
        self.name=name
    def walk(self):
        print('%s is walking'%self.name)
p=People('egon')

p.sex='male'
print(p.sex)
print(p.__dict__)
输出结果为:
male
{'name': 'egon', 'sex': 'male'}

  

delattr()   删除 

 
class People:
    country='China'
    def __init__(self,name):
        self.name=name
    def walk(self):
        print('%s is walking'%self.name)
p=People('egon')
print(p.__dict__) #删除前
del p.name
print(p.__dict__) #删除后
输出结果为:
{'name': 'egon'}
{}

 

3、反射的用途

 

第一种:把字符串映射成可执行命令,或者映射成属性得到一个值

例子:
import sys
def add():
    print('add')
def change():
    print('change')
def search():
    print('search')
def delete():
    print('delete')
this_module=sys.modules[__name__]
while True:
    cmd=input('>>:').strip()
    if not cmd:continue
    if hasattr(this_module,cmd):
        func=getattr(this_module,cmd)
        func()

 

第二种:实现可插拔机制

总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

 

 有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能

 

egon还没有实现全部功能:

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

不影响lili的代码编写:

#from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
    func_get=getattr(f1,'get')
    func_get()
else:
    print('---->不存在此方法')
    print('处理其他的逻辑')

  

4、通过字符串导入模块  

 

官方不推荐使用:

m=input('请输入你要导入的模块:')
m1=__import__(m)  #用字符串导入了一个模块
print(m1)
print(m1.time())

  

 

推荐使用方法: 

import importlib  #导入一个模块
m=input('请输入你要导入的模块:')
t=importlib.import_module('time') #传一个字符串
print(t.time())

 

5、反射当前模块属性

  什么是模块:

每写一个py文件就是模块,可以自己使用,别人需要用 导入一个import模块
文件本身是对象,文件下定义各种各样名字

在自己当前模块里面要获取自己模块内存地址
在自己模块里不能自己导入自己

 

import sys   #导入一个模块名

x=111  #定义一个变量名
class Foo:
    pass

def s1():
    print('s1')

def s2():
    print('s2')

 

 
文件两者用途
第一种 直接运行文件,把文件当做脚本运行
不运行这个文件,在另外一个文件里面当做模块导入这个文件
print(__name__) #等于__main__区分文件不同用处
  

6、_setattr_,_delattr_,_getattr_

_setattr_ 设置一个属性

class Foo:
    def __init__(self,name):
        self.name=name
    def __setattr__(self, key, value):
        print('----setattr----key:%s,value:%s'%(key,value))
        print(type(key))
        print(type(value))
        self.__dict__[key]=value #将值加入到字典里去

f1=Foo('egon')  # f1.name="egon"   触发setattr运行
f1.age=18       #触发setattr运行
print(f1.__dict__)

输出结果为:

----setattr----key:name,value:egon
<class 'str'>
<class 'str'>
----setattr----key:age,value:18
<class 'str'>
<class 'int'>
{'name': 'egon', 'age': 18}

 

加上类型限制(不是字符串类型)

class Foo:
    def __init__(self,name):
        self.name=name

    def __setattr__(self, key, value):
        if not isinstance(value,str):
            raise Exception('must be str')
        self.__dict__[key]=value

f1=Foo('egon')
f1.age=18
print(f1.__dict__)

输出结果为:

    raise Exception('must be str') #报错
Exception: must be str

 

最终结果 

class Foo:
    def __init__(self,name):
        self.name=name

    def __setattr__(self, key, value):
        if not isinstance(value,str):
            raise Exception('must be str')
        self.__dict__[key]=value

f1=Foo('egon')
f1.age='18'
print(f1.__dict__)

输出结果为:

{'name': 'egon', 'age': '18'}

  

_getattr_  获取一个属性 (属性不存在的情况下才会触发)

 

class Foo:
    def __init__(self,name):
        self.name=name
    # 属性不存在的情况下才会触发
    def __getattr__(self, item):
        print('getattr-->%s %s'%(item,type(item)))

f=Foo('egon')
print(f.name) #存在的情况下没有触发getattr

print(f.xxxx) #属性不存在才会触发getattr

 

输出结果为:

egon
getattr-->xxxx <class 'str'>
None

  

 _delattr_ 删除

class Foo:
    def __init__(self,name):
        self.name=name

    def __setattr__(self, key, value):
        self.__dict__[key]=value   #字典的方式加入

    def __delattr__(self, item):
        print('delattr:%s'%item)
        print(type(item))
        self.__dict__.pop(item)
f1=Foo('egon')
f1.age=18
del f1.age
print(f1.__dict__)

输出结果为:

delattr:age
<class 'str'>
{'name': 'egon'}

  

7、定制自己的数据类型

python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,
新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)

class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid
    def append(self, p_object):
        ' 派生自己的append:加上类型检查'
        if not isinstance(p_object,int):
            raise TypeError('must be int')
        super().append(p_object)

    @property
    def mid(self):
        '新增自己的属性'
        index=len(self)//2
        return self[index]

l=List([1,2,3,4])
print(l)
l.append(5)
print(l)
# l.append('1111111') #报错,必须为int类型

print(l.mid)

#其余的方法都继承list的
l.insert(0,-123)
print(l)
l.clear()
print(l)

  

授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

例子 来实现open函数的功能,不能用继承

import time
class Open:
    def __init__(self,file_path,m='r',encode='utf8'):
        self.file_path=file_path
        self.mode=m
        self.encoding=encode
        self.x=open(file_path,mode=m,encoding=encode)  #文件保存在self.x里面

    def write(self,line):
        print('f自己的write',line)
        t=time.strftime('%Y-%m-%d %x')
        self.x.write('%s %s'%(t,line))
    def __getattr__(self, item):
         print('---->',item,type(item))
         return getattr(self.x,item)

f=Open('b.txt','r+')
print(f.read)
res=f.read()
print(res)

输出结果为:

<_io.TextIOWrapper name='a.txt' mode='w' encoding='cp936'>
----> read <class 'str'>
<built-in method read of _io.TextIOWrapper object at 0x0235BE30>
----> read <class 'str'>
2017-04-25 04/25/17 1111
2017-04-25 04/25/17 1111
2017-04-25 04/25/17 1111
2017-04-25 04/25/17 1111