Python学习笔记-面向对象进阶(二)

时间:2022-06-11 00:32:30

 一、反射

1、什么是反射

  反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

2、Python面向对象中的反射

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

3、四个可以实现自省的函数

(1)hasattr(object,name),判断object中有没有一个name字符串对应的方法或属性,检测是否含有某属性。

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class BlackMedium:
    feture='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_hourse(self):
        print('【%s】正在卖房子,傻逼才买呢' %self.name)

    def rent_hourse(self):
        print('【%s】正在租房子,傻逼才租呢' %self.name)

b1=BlackMedium('黑中介','北京')
print(hasattr(b1,'name'))
print(hasattr(b1,'sell_hourse'))
"""
True
True
"""
hasattr(object,name)

(2)getattr(object, name, default=None),获取属性。

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class BlackMedium:
    feture='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_hourse(self):
        print('【%s】正在卖房子,傻逼才买呢' %self.name)

    def rent_hourse(self):
        print('【%s】正在租房子,傻逼才租呢' %self.name)

b1=BlackMedium('黑中介','北京')
print(getattr(b1,'name'))
print(getattr(b1,'rent_hourse'))
fun=getattr(b1,'rent_hourse')
fun()
# print(getattr(b1,'aaaaa')) # 没有aaaaa这个属性则报错
print(getattr(b1,'aaaaa','没有这个属性')) # 没有aaaaa这个属性则输出“没有这个属性”

"""
黑中介
<bound method BlackMedium.rent_hourse of <__main__.BlackMedium object at 0x101d3fba8>>
【黑中介】正在租房子,傻逼才租呢
没有这个属性
"""
getattr(object, name, default=None)

(3)setattr(x, y, v),设置属性。

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class BlackMedium:
    feture='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_hourse(self):
        print('【%s】正在卖房子,傻逼才买呢' %self.name)

    def rent_hourse(self):
        print('【%s】正在租房子,傻逼才租呢' %self.name)

b1=BlackMedium('黑中介','北京')
setattr(b1,'sb',True) # b1.sb=True
setattr(b1,'sb1',123) # b1.sb=True
print(b1.__dict__)

"""
{'name': '黑中介', 'addr': '北京', 'sb': True, 'sb1': 123}
"""
setattr(x, y, v)

(4)delattr(x, y),删除属性。

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class BlackMedium:
    feture='Ugly'
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def sell_hourse(self):
        print('【%s】正在卖房子,傻逼才买呢' %self.name)

    def rent_hourse(self):
        print('【%s】正在租房子,傻逼才租呢' %self.name)

b1=BlackMedium('黑中介','北京')
setattr(b1,'sb',True) # b1.sb=True
setattr(b1,'sb1',123) # b1.sb=True
delattr(b1,'sb') # del b1.sb
delattr(b1,'sb1') # del b1.sb1
print(b1.__dict__)

"""
{'name': '黑中介', 'addr': '北京'}
"""
delattr(x, y)

4、为什么要使用反射?

(1)“即插即用”:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。

(2)动态导入模块(基于反射当前模块成员)

Python学习笔记-面向对象进阶(二)

二、__setattr__,__delattr__,__getattr__

 1、__getattr__含义是只要找的属性不存在就会触发__getattr__

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):
        print('你要找的属性不存在')

f1=Foo(10)
print(f1.y)
f1.aaaaaa
"""
10
你要找的属性不存在
"""
__getattr__

2、__delattr__含义是执行删除操作的时候出发__delattr__

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __delattr__(self, item):
        self.__dict__.pop(item)
        print('删除操作')

f1=Foo(10)
f1.__dict__['z']=3
print(f1.__dict__)
del f1.y
print(f1.__dict__)
"""
{'y': 10, 'z': 3}
删除操作
{'z': 3}
"""
__delattr__

3、__setattr__含义是添加/修改属性会触发它的执行

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __setattr__(self, key, value):
        print('__setattr__执行')
        # self.key=value # 这样就会无限递归了
        self.__dict__[key]=value # 直接修改底层字典
f1=Foo(10)
print(f1.__dict__)
f1.z=2
print(f1.__dict__)
"""
__setattr__执行
{'y': 10}
__setattr__执行
{'y': 10, 'z': 2}
"""
__setattr__

4、__getattr__补充

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    def __init__(self,name):
        self.name=name

    def __getattr__(self, item):
        print('你找的属性【%s】不存在' %item)

f1=Foo('lionel')
print(f1.name)
print(f1.age)
print(f1.sex)
"""
lionel
你找的属性【age】不存在
None
你找的属性【sex】不存在
None
"""
__getattr__补充item

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

 1、包装

  python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法。

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class List(list): # 继承list所有的属性,也可以派生出自己新的,比如append和pop
    def append(self, p_object):
        '派生自己的append:加上类型检查'
        if type(p_object) is str:
            super().append(p_object)
        else:
            print('只能添加字符串类型')

l1=list('hello world')
print(l1,type(l1))
"""
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] <class 'list'>
"""

l2=List('hello world')
print(l2,type(l2))
"""
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] <class '__main__.List'>
"""

l2.append('aaaaa')
print(l2)
"""
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'aaaaa']
"""

l2.append(123123123)
print(l2)
"""
只能添加字符串类型
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'aaaaa']
"""
包装标准类型

2、授权

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

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        # self.filename=filename
        self.file=open(filename,mode,encoding=encoding)
        self.mode=mode
        self.encoding=encoding

    def write(self,line):
        t=time.strftime('%Y-%m-%d %X')
        self.file.write('%s %s' %(t,line))

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

while True:
    info=input('>>: ')
    f1.write('%s\n' %info)

# a.txt文件内容
"""
2018-04-20 18:06:15 cpu负载过高
2018-04-20 18:06:15 内存剩余不足
2018-04-20 18:06:15 硬盘剩余不足
2018-04-20 18:06:38 mysql集群宕机
2018-04-20 18:06:48 kvm虚拟机故障
2018-04-20 18:07:17 数据库被删除
"""
授权

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

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

class Foo:
    pass

f1=Foo()
print(isinstance(f1,Foo))
"""
True
"""

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

class Bar(Foo):
    pass

print(issubclass(Bar,Foo))
"""
True
"""

 五、__getattribute__(self, item)

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    def __init__(self,x):
        self.x=x

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

f1=Foo(10)
print(f1.x)
f1.xxxxxx
"""
10
执行的是__getattr
"""
__getattr__(self, item)
Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    def __init__(self,x):
        self.x=x

    # 不管找不找得到属性,都会触发__getattribute__
    def __getattribute__(self, item):
        print('执行的是__getattribute__')
        raise AttributeError('抛出异常')

f1=Foo(10)
f1.x
f1.xxxxxx
__getattribute__(self, item)
Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    def __init__(self,x):
        self.x=x

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

    # 不管找不找得到属性,都会触发__getattribute__
    def __getattribute__(self, item):
        print('执行的是__getattribute__')
        raise AttributeError('抛出异常')

f1=Foo(10)
f1.x
f1.xxxxxx
"""
执行的是__getattribute__
执行的是__getattr__
执行的是__getattribute__
执行的是__getattr__
"""
__getattr__和__getattribute__同时存在

总结

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

六、__setitem__,__getitem,__delitem__

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
# 中括号形式调用就是*item方法,点的形式调用就是*attr方法
class Foo:
    def __getitem__(self, item):
        print('getitem',item)
        return self.__dict__[item]

    def __setitem__(self, key, value):
        print('setitem')
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('delitem')
        self.__dict__.pop(key)

f1=Foo()
f1['name'] = 'lionel'
f1['age'] = '18'
print(f1.__dict__)
"""
setitem
setitem
{'name': 'lionel', 'age': '18'}
"""

del f1['name']
print(f1.__dict__)
"""
delitem
{'age': '18'}
"""

print(f1['age'])
"""
getitem age
18
"""
View Code

七、__str__与__repr__改变对象的字符串显示

1、改变对象的字符串显示

  改变对象的字符串显示__str__,__repr__ 。 __str__在print时触发,__repr__在解释器中触发。

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        return '名字是%s 年龄是%s' %(self.name,self.age)

f1=Foo('lionel',18)
print(f1) # str(f1) ---> f1.__str__()
"""
名字是lionel 年龄是18
"""
__str__
Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        return '这是str'

    def __repr__(self):
        return '名字是%s 年龄是%s' %(self.name,self.age)

f1=Foo('lionel',20)
print(f1) # str(f1) ---> f1.__str__() ,如果找不到str就会找repr   repr(f1) ---> f1.__repr__()
"""
这是str
"""

'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
__str__和__repr__

八、自定义格式化方式__format__

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
format_dic={
    'ymd':'{0.year}{0.mon}{0.day}',
    'y-m-d':'{0.year}-{0.mon}-{0.day}',
    'y:m:d':'{0.year}:{0.mon}:{0.day}',
    'default':'{0.year}_{0.mon}_{0.day}'
}

class Date:
    def __init__(self,year,mon,day):
        self.year=year
        self.mon=mon
        self.day=day

    def __format__(self, format_spec):
        print('我执行啦')
        if not format_spec or format_spec not in format_dic:
            format_spec='default'
        fm=format_dic[format_spec]
        return fm.format(self)

d1=Date(2018,4,21)

print(format(d1,'ymd')) # 2018421
print(format(d1,'y-m-d')) # 2018-4-21
print(format(d1,'y:m:d')) # 2018:4:21
print(format(d1)) # 2018_4_21
print(format(d1,'asd')) # 2018_4_21
"""
我执行啦
2018421
我执行啦
2018-4-21
我执行啦
2018:4:21
我执行啦
2018_4_21
我执行啦
2018_4_21
"""
__format__

九、__next__和__iter__实现迭代器协议

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Range:
    def __init__(self,n,stop,step):
        self.n=n
        self.stop=stop
        self.step=step

    def __next__(self):
        if self.n >= self.stop:
            raise StopIteration
        x=self.n
        self.n+=self.step
        return x

    def __iter__(self):
        return self

for i in Range(1,7,3):
    print(i)
"""
/usr/local/bin/python3 /Users/wangzhiwei/python/day7/13_迭代器协议.py
1
4
"""
迭代器协议
Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Fib:
    def __init__(self):
        self._a=1
        self._b=1

    def __iter__(self):
        return self

    def __next__(self):
        if self._a > 100:
            raise StopIteration('终止了')
        self._a,self._b = self._b,self._a + self._b
        return self._a

f1=Fib()
for i in f1:
    print(i)
"""
1
2
3
5
8
13
21
34
55
89
144
"""
迭代器协议实现斐波那契数列

十、描述符(__get__,__set__,__delete__)

1、描述符是什么?

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

2、描述符是干什么的?

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

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Foo:
    def __get__(self, instance, owner):
        print('===>get方法')
    def __set__(self, instance, value):
        print('===>set方法')
    def __delete__(self, instance):
        print('===>delete方法')

class Bar:
    x=Foo()

b1=Bar()
b1.x
"""
===>get方法
"""
# __get__():调用一个属性时,触发
# __set__():为一个属性赋值时,触发
# __delete__():采用del删除属性时,触发
View Code

十一、上下文管理协议(__enter__和__exit__)

1、什么是上下文管理协议

  上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('执行enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('执行exit')

with Open('a.txt') as f:
    print('=====>')
    print(f)
    print(f.name)

print('00000000000')
"""
执行enter
=====>
<__main__.Open object at 0x10353f9b0>
a.txt
执行exit
00000000000
"""

# with obj as f:
#     '代码块'
# 1、with obj --->触发obj.__enter__(),拿到返回值
# 2、as f ---->f=返回值
# 3、with obj as f  等同于  f = obj.__enter__()
# 4、执行代码块
# 一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
# 二:有异常的情况下,从异常出现的位置直接触发__exit__
#     a:如果__exit__的返回值为True,代表吞掉了异常
#     b:如果__exit__的返回值不为True,代表吐出了异常
#     c:__exit__的运行完毕就代表了整个with语句的执行完毕
View Code

2、使用场景

  • 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
  • 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处 

 十二、类的装饰器

Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
def deco(obj):
    print('=======')
    return obj

@deco
def test():
    print('test函数运行')
test()
"""
=======
test函数运行
"""

def deco(obj):
    print('=======')
    obj.x=1
    obj.y=2
    obj.z=3
    return obj

@deco
class Foo():
    pass

f1=Foo()
print(Foo.__dict__)
print(f1.x)
"""
=======
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3}
1
"""
View Code
Python学习笔记-面向对象进阶(二)Python学习笔记-面向对象进阶(二)
def Typed(**kwargs):
    def deco(obj):
        for key,val in kwargs.items():
            setattr(obj,key,val)
        return obj
    print('===>',kwargs)
    return deco

@Typed(x=1,y=2,z=3)
class Foo():
    pass
print(Foo.__dict__)

@Typed(name='lionel')
class Bar:
    pass
b1=Bar()
print(b1.name)
"""
===> {'x': 1, 'y': 2, 'z': 3}
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3}
===> {'name': 'lionel'}
lionel
"""
类的装饰器增强版