面向对象进阶

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

 isintance(obj,class)检查obj是否是类class的对象

class Foo:
    pass

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

还可以这么玩

print(isinstance([],list))
print(isinstance((),tuple))
print(isinstance('',str))
print(isinstance({},dict))

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

class Foo:
    pass

class Bar(Foo):
    pass

print(issubclass(Bar,Foo))

反射

什么是反射

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

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

四个可以实现自省的函数

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

#例子
class
Chinese: country='China' def __init__(self,name,age): self.name=name self.age=age p1=Chinese('jack','18')
#hasattr
print(hasattr(p1,'x'))
#判断p1对象中有没有'x'字符串
hasattr
#getattr
print(getattr(p1,'name'))
#判断p1对象中有没有name字符串

print(getattr(p1,'x','找不到'))
#getattr可以定义三个参数,最后一个参数为找不到时返回信息
getattr
# setattr
print(p1.__dict__)  #没改之前查看一下
setattr(p1,'age','100') #设置值不需要打印
print(p1.__dict__)  #改过之后查看一下
print(getattr(p1,'age'))
setattr
# delattr
print(p1.__dict__)
delattr(p1,'age')   #删除值
print(p1.__dict__)
delattr

可插拔式编程

比如说要写一个ftp功能

#类的定义者
class FtpServer:
    def __init__(self,ip):
        self.ip=ip
    # def conn(self):
    #     print('正在连接%s....' %self.ip)
    #编写到这里,定义者突然间有急事,三个月内无法继续编写
#客户端实现者
from FTP_SERVER import FtpServer

obj=FtpServer('1.1.1.1')
#对于客户端实现者来说,要跟着服务端的脚步,即服务端实现的功能我客户端才能调用,但如果服务端实现者临时有事未来三个月内无法实现编写怎么处理

if hasattr(obj,'conn'):
    func=getattr(obj,'conn')
    func()
#进行判断处理,通过反射,查看当前类中的conn方法是否实现,实现加括号就进行调用,
else:
    print('编写其它代码逻辑')

即使后来类的定义者回来了,后续的功能都写好了,但是对于客户端实现者来说,不用再回去把前面的代码进行修改

#如果类的定义者一行代码都没有写,但我还要继续写下去,怎么处理。
import FTP_SERVER
if hasattr(FTP_SERVER,'FtpServer'):
    cls_fs=getattr(FTP_SERVER,'FtpServer')
    print(cls_fs)
#进行判断,查看这个文件下面是否有这个类,在python中一切接对象么。所以文件也是一个对象
#如果找不到就进行编写其他代码逻辑
print('其他逻辑')

 __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',key,value)
        #只给实例去用,类无法触发
        self.__dict__[key]=value

    def __delattr__(self, item):
        print('----> from delattr')
        self.__dict__.pop(item)
        # print(item)

f=Foo(100)
# print(f.x)
# print(f.y)
#能找到的属性并不会除非__getattr__

# print(f.z)
# f.z
#查找一个找不到的属性就会触发__getattr__

# del f.y
#注释掉__delattr__ 'self.__dict__.pop(item)' 执行del f.y可以触发__delattr__


#设置值
f.z=1
# print(f.z)
#并没有真正设定值。在__setattr__中 添加代码'self.__dict__[key]=value'
#f.z=1的意思是在f的名称空间中加入一个z 值为1。在名称空间中都是那种key:value格式所有我们可以用上面的方法将值加入到f的名称空间中

# 查看值
# print(getattr(f,'z'))
# print(f.__dict__)


# 删除值
print(f.__dict__)  #删除前查看一次
del f.z
print(f.__dict__)  #删除后查看一次,

#上面打印的item结果就是要删除的z,有了key我们就可以删除,代码'self.__dict__.pop(item)'

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

#自定义列表
class List(list):       #继承python内置list,
    pass

# l=List([1,2,3])
# print(l)
#没毛病,直接实例化调用内置list的init方法
# 进行二次加工,添加append功能,并限制
class List(list):
    def append(self, p_object):     #self是列表本身,p_object为要添加的值
        if not isinstance(p_object,int):
            raise TypeError('Must be int type')     #定义规则,只能添加数字类型
        super().append(p_object)    #使用super调用父类,再次查看

l=List([1,2,3,4])
l.append('asdasd')
print(l)

授权

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

使用的方法:__getattr__

#自定义Open方法,实现每一行开头都显示时间
# class Open:
#     def __init__(self,file_path,mode='r'):
#         self.obj=open(file_path,mode)
#
# f=Open('a.txt','w')
# f.obj.write('1111111111111')  #open打开文件写的操作就是f.write,而不是f.obj.write
#上面的方法实现了自定义Open的方法,但是没有时间功能

import time class Open: def __init__(self,file_path,mode='r'): self.obj=open(file_path,mode) def write(self,file): #定义一个write方法,让f.obj.write 变成f.write file='%s %s ' %(time.strftime("%Y-%m-%d %X"),file) #然后在真正的写操作之前进行一个字符串拼接 self.obj.write(file) #f这个对象传到self,file参数接受要输入的内容 f=Open('a.txt','w') f.write('11111111111111111111111111111111 ')

上面的方法已经可以实现这个功能,但是想要f.close()呢?怎么办?

import time
class Open:
    def __init__(self,file_path,mode='r'):
        self.obj=open(file_path,mode)

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

    def __getattr__(self, item):                #定义一个getattr函数
        # print('--------------->',item)
        return getattr(self.obj,item)           #调用真正的close并返回


f=Open('a.txt','w')
f.close()       #我想要关闭文件f。但是close不属于f,而属于self.obj,那我们就拿到他

#首先f.close会在f的类中找close,找不到就会触发__getattr__的运行
#那么close的字符串会传给item这个参数,然后通过getattr去真正的对象里面去找有没有close这个名字
#找到了那么就是一个内存地址并返回,那么f.close()拿到的就是f.obj.close()

__setitem__,__getitem,__delitem__

就是在把对象模拟成字典的形式,就可以用key值取value值

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

    def __getitem__(self, item):                #真正的字典取值
        # print(self.__dict__[item])            #使用print方法执行 print(f['name'])会打印,但是也会返回None,所以用ret直接返回
        return self.__dict__[item]
    def __setitem__(self, key, value):
        self.__dict__[key]=value

    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)

    def __delattr__(self, item):        #而delattr与delitem区别是delattr是通过obj.key,delitem是obj['key']
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

f=Foo('jack')
# print(f.name)
print(f['name'])    #通过getitem找值
f['age']='25'       #通过setitem设置值
f['sex']='man'

# print(f.__dict__)   #再次查看
del f.name          #通过delattr删除
del f['age']        #通过delitem删除
print(f.__dict__)
e,g

__slots__

class Foo:
    __slots__=['x','y']     #限制类中,只能有x,y参数
    def __init__(self,x,y,z):
        self.x=x
        self.y=y
        self.z=z

f=Foo(1,2,3)
#实例化报错,提示Foo没有z这个参数
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。


4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。           更多的是用来作为一个内存优化工具。
__slots__使用
class Foo:
    __slots__=['x','y']
    # def __init__(self,x,y,z):
    #     self.x=x
    #     self.y=y
    #     self.z=z

f=Foo()
f1=Foo()
#正常来说,实例化后实例会有一个自己的名称空间,也就是__dict__查看,那么使用了__slost__就统一共用一个名称空间有slost提供
#查看实例的名称空间
print(f.__dict__)
print(f1.__dict__)

 __next__和__iter__实现迭代器协议

#自定义迭代器
class Foo:
    def __iter__(self):
        pass
    def __next__(self):
        pass

f=Foo()

from collections import Iterator,Iterable
print(isinstance(f,Iterable))
print(isinstance(f,Iterator))
#实现迭代器range功能

class Foo:
    def __init__(self,start,stop):
        self.start=start
        self.stop=stop

    def __iter__(self):
        return self

    def __next__(self):
        if self.start == self.stop:     #进行判断,如果不判断,print(f.__next__()) 会无限下去,判断成功,抛出异常
            raise StopIteration
        res=self.start      #将开始值暂存,并返回
        self.start+=1       #将开始值+1,比如说第一次是1 暂存到res中,返回,之后执行第一次值+1
        return res

f=Foo(1,5)              #实例化
print(f.__next__())     #取值
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())

for i in f:
    print(i)
实现迭代器range功能

__del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

class Foo:
    def __del__(self):      #__del__叫析构方法,在del删除对象的时候会执行
        print('---->')      #通常在里面写一些清理操作

f=Foo()
# del f

__enter__和__exit__

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

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

    def __enter__(self):
        # print(self)
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行')
        print('exe_type',exc_type)      #异常类型
        print('exc_val',exc_val)        #异常值
        print('exc_tb',exc_tb)          #异常追踪
        #抛出异常后会有结果,只要一抛异常,程序就自动退出,后面代码将不会执行
        # return True
        # 如果exit返回值为True异常将不会抛出,只会被exit接收到

with Open('a.txt') as f:
    print('=====>执行代码块',f)
    print(dasdasdasd)
    print('==========================================================>')
    # print(f,f.name)

__call__

class Foo:
    def __call__(self, *args, **kwargs):
        print('-----L>')

f1=Foo()
f1()
#只要在类中定义了__call__那么对象就可以加括号运行,暂时用不到

未完待续...

原文地址:https://www.cnblogs.com/charles1ee/p/6734550.html