九、Python面向对象进阶

(一)内置函数补充


1、isinstance(obj,cls)

检查obj是否是类 cls 的对象,如果cls有父类,也是其父类的实例

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

2、issubclass(sub,super) 

检查sub类是否是 super 类的派生类 

class Foo(object):
     pass

class Bar(Foo):
     pass

print(issubclass(Bar, Foo))  # True
print(issubclass(Bar, object))  # True

(二)反射


1、反射的概念 

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

2、python面向对象中的反射

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

3、实现反射的四个函数 

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

3.1 hasattr(object,name)

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

3.2 getattr(object, name, default=None)

获取object中name字符串对应的方法或属性,如果该方法或属性不存在,指定了default的值则返回此值,未指定default的值则抛出异常

3.3 setattr(x, y, v)

设置x中y字符串对应的方法或属性的value,相当于x.y=v 

3.4 delattr(x, y) 

从x中删除 y字符串对应的方法或属性,相当于del x.y,不存在,则报错

4、反射例子 

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

5、类也是对象 

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')

6、反射当前模块成员

import sys

def s1():
    print 's1'

def s2():
    print 's2'

x=111

# 获取当前模块
this_module = sys.modules[__name__] 

current_module=__import__(__name__)

print(hasattr(current_module,"x"))
print(getattr(current_module,"x"))

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

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

"""
程序目录:
module_test.py
index.py
"""
# 导入模块:
def test():
    print('from the test')
# 当前文件:
index.py
"""
import module_test as obj

#obj.test()

print(hasattr(obj,'test'))  # True
getattr(obj,'test')()  # from the test

8、反射的好处

  • 实现可插拔机制:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,即事先把主要的逻辑写好(只定义接口),后期再去实现接口的功能
    # 还没有实现全部功能的接口
    class FtpClient:
        'ftp客户端,但是还么有实现具体的功能'
        def __init__(self,addr):
            print('正在连接服务器[%s]' %addr)
            self.addr=addr
    
    # 不影响逻辑代码编写
    #from module import FtpClient
    f1=FtpClient('192.168.1.1')
    if hasattr(f1,'get'):
        func_get=getattr(f1,'get')
        func_get()
    else:
        print('---->不存在此方法')
        print('处理其他的逻辑')
  •  动态导入模块(基于反射当前模块成员)

9、__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

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


1、包装

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

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

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)  # [1, 2, 3, 4]
l.append(5)
print(l)  # [1, 2, 3, 4, 5]
# l.append('1111111') #报错,必须为int类型

print(l.mid)  # 3

#其余的方法都继承list的
l.insert(0,-123)
print(l)  # [-123, 1, 2, 3, 4, 5]
l.clear()
print(l)  # []

 1.2 练习(clear加权限限制)

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)  # [1, 2, 3]
print(l.tag)  # False

l.append('saf')
print(l)  # [1, 2, 3, 'saf']

# l.clear()  # 异常

l.tag=True
l.clear()
print(l)  # []

2、授权

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

2.1 授权示范一 

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())  # 2024-01-19 17:20:12 你好啊
f1.close()

 2.2 授权示范二

#我们来加上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)  # <_io.BufferedReader name='b.txt'>
f1.close()

2.3 练习题(授权)

#练习一
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)  # [1, 2, 3]
l.append(4)
print(l)  # [1, 2, 3, 4]
# l.append('3333333') #报错,必须为int类型

print(l.mid)  # 3

#基于授权,获得insert方法
l.insert(0,-123)
print(l)  # [-123, 1, 2, 3, 4]

#练习二
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)  # [1, 2, 3]
l.clear()
print(l)  # []

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

(四)__getattribute__


对象调用属性时触发(不管存不存在都会触发) 

  • __getattr__(self,item):其实是在__getattribute__里抛出AttributeError异常时触发
  • __getattribute__中要返回获取的属性值,否则用点调用属性并不会真正获取到属性值,而得到None

1、回顾__getattr__

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__

2、__getattribute__

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

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

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

# 输出结果:
不管是否存在,我都会执行
None
不管是否存在,我都会执行
None

3、二者同时出现

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删除属性时,触发 

2、定义描述符 

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

3、描述符的作用

用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中) ,描述符类产生的实例进行属性操作并不会触发三个方法的执行

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
#疑问:何时,何地,会触发这三个方法的执行
# 描述符应用之何时?何地?
#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

#描述符Int
class Int:
    def __get__(self, instance, owner):
        print('Int调用')
    def __set__(self, instance, value):
        print('Int设置...')
    def __delete__(self, instance):
        print('Int删除...')

class People:
    name=Str()
    age=Int()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age

#何地?:定义成另外一个类的类属性

#何时?:且看下列演示

p1=People('alex',18)  # Str设置... Int设置...

#描述符Str的使用
p1.name  # Str调用
p1.name='egon'  # Str设置...
del p1.name  # Str删除...

#描述符Int的使用
p1.age  # Int调用
p1.age=18  # Int设置...
del p1.age  # Int删除...

#我们来瞅瞅到底发生了什么
print(p1.__dict__)  # {}
print(People.__dict__)  # {'__module__': '__main__', 'name': <__main__.Str object at 0x00000207FD9B3390>, 'age': <__main__.Int object at 0x00000207FD9DDDD8>, '__init__': <function People.__init__ at 0x00000207FF61D048>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}

#补充
print(type(p1) == People)  # True,type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == People.__dict__)  # True

4、描述符分类

4.1 数据描述符 

至少实现了__get__()和__set__()

class Foo:
    def __set__(self, instance, value):
        print(&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值