面向对象进阶(一)

isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)检查obj是否是类 cls 的对象

class Foo(object):
   pass  
obj = Foo()  
isinstance(obj, Foo)

 

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

class Foo(object):
    pass
class Bar(Foo):
    pass
issubclass(Bar, Foo)

 

反射

1.什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
 

2.py
thon面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
hasattr(object,name)
getattr(object, name, default=None)
setattr(x, y, v)
delattr(x, y)
 

四个方法的使用演示

class BlackMedium:
    feature='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

def sell_house(self):
print(‘%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼’ %self.name)
def rent_house(self):
print(‘%s 黑中介租房子啦,傻逼才租呢’ %self.name)

b1=BlackMedium(‘万成置地’,‘回龙观天露园’)

#检测是否含有某属性
print(hasattr(b1,‘name’))
print(hasattr(b1,‘sell_house’))

#获取属性
n=getattr(b1,‘name’)
print(n)
func=getattr(b1,‘rent_house’)
func()


# getattr(b1,’aaaaaaaa’)
#报错

print(getattr(b1,‘aaaaaaaa’,‘不存在啊’))

#设置属性
setattr(b1,‘sb’,True)
setattr(b1,‘show_name’,lambda self:self.name+‘sb’)
print(b1.__dict__)
print(b1.show_name(b1))

#删除属性
delattr(b1,‘addr’)
delattr(b1,‘show_name’)
delattr(b1,‘show_name111’)#不存在,则报错

print(b1.__dict__)

 

类也是对象

class Foo(object):
    staticField = "old boy"
    def __init__(self):
        self.name = 'wupeiqi'
    def func(self):
        return 'func'
    @staticmethod
    def bar():
        return 'bar'

print getattr(Foo, ‘staticField’)
print getattr(Foo, ‘func’)
print getattr(Foo, ‘bar’)

 

反射当前模块成员

#!/usr/bin/env python

# -*- coding:utf-8 -*-

import sys
def s1():
print ‘s1’
def s2():
print ‘s2’
this_module = sys.modules[__name__]
hasattr(this_module, ‘s1’)
getattr(this_module, ‘s2’)

 

导入其他模块,利用反射查找该模块是否存在某个方法

def test():
    print('from the test')

 

"""
程序目录:
    module_test.py
    index.py
当前文件:
    index.py"""
import module_test as obj
#obj.test()
print(hasattr(obj,'test'))
getattr(obj,'test')()

 

3.为什么用反射之反射的好处
好处一:实现可插拔机制
有俩程序员,一个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('处理其他的逻辑')

 

好处二:动态导入模块(基于反射当前模块成员)

import importlib
importlib.import_module('模块名','包名')

 
 

__setattr__,__delattr__,__getattr__

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

def __getattr__(self, item):
print(‘—->
from getattr:你找的属性不存在’
)
def __setattr__(self, key, value):
print(‘—->
from setattr’
)

# self.key=value
#这就无限递归了,你好好想想


# self.__dict__[key]=value
#应该使用它

def __delattr__(self, item):
print(‘—->
from delattr’
)

# del self.item
#无限递归了

self.__dict__.pop(item)
#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__)
# 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值

f1.z=3
print(f1.__dict__)
#__delattr__删除属性的时候会触发
f1.__dict__[‘a’]=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)
#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx

 

二次加工标准类型(包装)

包装: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)
#其余的方法都继承listl.insert(0,-123)
print(l)
l.clear()
print(l)

二次加工标准类型(基于继承实现)

 

class List(list):
    def __init__(self,item,tag=False):
        super().__init__(item)
        self.tag=tag
    def append(self, p_object):
        if not isinstance(p_object,str):
            raise TypeError
        super().append(p_object)
    def clear(self):
        if not self.tag:
            raise PermissionError
        super().clear()
l=List([1,2,3],False)
print(l)
print(l.tag)
l.append('saf')
print(l)

# l.clear()
#异常
l.tag=True
l.clear()
练习(clear加权限限制)

 

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

import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)
    def write(self,line):
        t=time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' %(t,line))

def __getattr__(self, item):
return getattr(self.file,item)

f1=FileHandle(‘b.txt’,‘w+’)
f1.write(‘你好啊’)
f1.seek(0)
print(f1.read())
f1.close()

 

授权示范二

#我们来加上b模式支持
import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        if 'b' in mode:
            self.file=open(filename,mode)
        else:
            self.file=open(filename,mode,encoding=encoding)
        self.filename=filename
        self.mode=mode
        self.encoding=encoding

def write(self,line):
if ‘b’ in self.mode:
if not isinstance(line,bytes):
raise TypeError(‘must be bytes’)
self.file.write(line)

def __getattr__(self, item):
return getattr(self.file,item)

def __str__(self):
if ‘b’ in self.mode:
res=“<_io.BufferedReader name=’%s’>” %self.filename
else:
res=“<_io.TextIOWrapper name=’%s’ mode=’%s’ encoding=’%s’>” %(self.filename,self.mode,self.encoding)
return res
f1=FileHandle(‘b.txt’,‘wb’)

# f1.write(‘你好啊啊啊啊啊’)
#自定制的write,不用在进行encode转成二进制去写了,简单,大气

f1.write(‘你好啊’.encode(‘utf-8’))
print(f1)
f1.close()

 


 

发表评论