python之面相对象进阶

Posted 游小刀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python之面相对象进阶相关的知识,希望对你有一定的参考价值。

一 isinstance(obj,cls)和issubclass(sub,super)

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

1 class Foo(object):
2     pass
3
4 obj = Foo()
5
6 isinstance(obj, Foo)

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

复制代码
1 class Foo(object):
2     pass
3
4 class Bar(Foo):
5     pass
6
7 issubclass(Bar, Foo)
复制代码

二 反射

1 什么是反射

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

 

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

四个可以实现自省的函数

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

判断object中有没有一个name字符串对应的方法或属性
hasattr(object,name)
def getattr(object, name, default=None): # known special case of getattr
    """
    getattr(object, name[, default]) -> value

    Get a named attribute from an object; getattr(x, \'y\') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn\'t
    exist; without it, an exception is raised in that case.
    """
    pass
getattr(object, name, default=None)
def setattr(x, y, v): # real signature unknown; restored from __doc__
    """
    Sets the named attribute on the given object to the specified value.

    setattr(x, \'y\', v) is equivalent to ``x.y = v\'\'
    """
    pass
setattr(x, y, v) 
def delattr(x, y): # real signature unknown; restored from __doc__
    """
    Deletes the named attribute from the given object.

    delattr(x, \'y\') is equivalent to ``del x.y\'\'
    """
    pass
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\')
反射当前模块成员

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

#!/usr/bin/env python
# -*- coding:utf-8 -*-

def test():
    print(\'from the test\')
module_test.py 
复制代码
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3
 4 """
 5 程序目录:
 6     module_test.py
 7     index.py
 8
 9 当前文件:
10     index.py
11 """
12
13 import module_test as obj
14
15 #obj.test()
16
17 print(hasattr(obj,\'test\'))
18
19 getattr(obj,\'test\')()
复制代码

 

3 为什么用反射之反射的好处

好处一:实现可插拔机制

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

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

class FtpClient:
    \'ftp客户端,但是还么有实现具体的功能\'
    def __init__(self,addr):
        print(\'正在连接服务器[%s]\' %addr)
        self.addr=addr
egon还没有实现全部功能 
#from module import FtpClient
f1=FtpClient(\'192.168.1.1\')
if hasattr(f1,\'get\'):
    func_get=getattr(f1,\'get\')
    func_get()
else:
    print(\'---->不存在此方法\')
    print(\'处理其他的逻辑\')
不影响lili的代码编写

 

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

 

 

三 __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)

#其余的方法都继承list的
l.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()
授权示范一
#_*_coding:utf-8_*_
__author__ = \'Linhaifeng\'
#我们来加上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()
授权示范二
#练习一
class List:
    def __init__(self,seq):
        self.seq=seq

    def append(self, p_object):
        \' 派生自己的append加上类型检查,覆盖原有的append\'
        if not isinstance(p_object,int):
            raise TypeError(\'must be int\')
        self.seq.append(p_object)

    @property
    def mid(self):
        \'新增自己的方法\'
        index=len(self.seq)//2
        return self.seq[index]

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

    def __str__(self):
        return str(self.seq)

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

print(l.mid)

#基于授权,获得insert方法
l.insert(0,-123)
print(l)





#练习二
class List:
    def __init__(self,seq,permission=False):
        self.seq=seq
        self.permission=permission
    def clear(self):
        if not self.permission:
            raise PermissionError(\'not allow the operation\')
        self.seq.clear()

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

    def __str__(self):
        return str(self.seq)
l=List([1,2,3])
# l.clear() #此时没有权限,抛出异常


l.permission=True
print(l)
l.clear()
print(l)

#基于授权,获得insert方法
l.insert(0,-123)
print(l)
练习题(授权)

 

五 __getattribute__

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

    def __getattr__(self, item):
        print(\'执行的是我\')
        # return self.__dict__[item]

f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__
回顾__getattr__
class Foo:
    def __init__(self,x):
        self.x=x

    def __getattribute__(self, item):
        print(\'不管是否存在,我都会执行\')

f1=Foo(10)
f1.x
f1.xxxxxx
__getattribute__
#_*_coding:utf-8_*_
__author__ = \'Linhaifeng\'

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

    def __getattr__(self, item):
        print(\'执行的是我\')
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print(\'不管是否存在,我都会执行\')
        raise AttributeError(\'哈哈\')

f1=Foo(10)
f1.x
f1.xxxxxx

#当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
二者同时出现

 

六 描述符(__get__,__set__,__delete__)

1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发

class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass
定义一个描述符

2 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

class Foo:
    def __get__(self, instance, owner):
        print(\'触发get\')
    def __set__(self, instance, value):
        print(\'触发set\')
    def __delete__(self, instance):
        print(\'触发delete\')

#包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
f1=Foo()
f1.name=\'egon\'
f1.name
del f1.name
#疑问:何时,何地,会触发这三个方法的执行
引子:描述符类产生的实例进行属性操作并不会触发三个方法的执行