python 类的进阶

时间:2023-03-08 23:45:50
python 类的进阶

类的进阶

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

 class Foo:
def __init__(self,name):
self.name = name obj = Foo("egon") print(isinstance(obj,Foo)) l = list([1,2,3])
print(isinstance(l,list)) -----------------结果----------- True
True isinstance(obj,cls)检查是否obj是否是类 cls 的对象

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

 class Foo:
pass class Bar(Foo):
pass print(issubclass(Bar,Foo)) ------------------------结果----------
True issubclass(sub, super)检查sub类是否是 super 类的派生类

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

二.反射

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

 class People:
country = "china"
def __init__(self,name):
self.name = name p = People("egon")
print(p.__dict__)
#-------------------------
print(hasattr(p,"name"))
print("name" in p.__dict__)
print(hasattr(People,"country")) ----------------------------------
{'name': 'egon'}
True
True
True

hasattr(object,name)

 class People:
country = "china"
def __init__(self,name):
self.name = name def walk(self):
print("%s is working" % self.name) p = People("egon") print(getattr(p,"country")) #print(p.country)
# china
print(getattr(p,"walk")) #print(p.walk)
#<bound method People.walk of <__main__.People object at 0x101979278>>

getattr(object, name, default=None)

 class People:
country = "china"
def __init__(self,name):
self.name = name def walk(self):
print("%s is working" % self.name) p = People("egon") setattr(p,"age",15)
print(p.__dict__)
{'age': 15, 'name': 'egon'}

setattr(x, y, v)

 class People:
country = "china"
def __init__(self,name):
self.name = name def walk(self):
print("%s is working" % self.name) p = People("egon") print(p.__dict__)
{'name': 'egon'}
delattr(p,"name")
print(p.__dict__)
{}

delattr(x, y)

 import sys
def add():
print("add") def delete():
print("delete") def update():
print("update") def search():
print("search") this_module = sys.modules[__name__]
while True:
cmd = input("please input: ")
if not cmd:continue
if hasattr(this_module,cmd):
func = getattr(this_module,cmd)
func()

sys.modules[__name__]

2.反射的好处 

    好处一:实现可插拔机制

    有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,

lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。

    总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?

即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能 

 #Ftpclent.py 文件

 class FtpClient:
'ftp客户端,但是还么有实现具体的功能'
def __init__(self,addr):
print('正在连接服务器 %s ' %addr)
self.addr=addr def get(self):
print("get") #Ftpserver.py 文件 from day32 import FtpClient obj = FtpClient.FtpClient('192.168.1.1') if hasattr(obj,"get"):
func = getattr(obj,"get")
func() print("other func")

FTP 代码

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

1
2
3
4
5
6
7
8
#导入字符串
__import__("time")
#使用模块
import importlib
= importlib.import_module("time")
print(t.time())
    1493026292.217129

三.__setattr__,__delattr__,__getattr__

 #初始化和赋值时触发__setattr__运行

 #发现__dict__没有name 和 age
class Foo:
def __init__(self,name):
self.name = name def __setattr__(self, key, value):
print("__setattr__ key:%s value:%s"%(key,value)) obj = Foo("egon")
__setattr__ key:name value:egon
obj.age = 18
__setattr__ key:age value:18
print(obj.__dict__) #发现__dict__没有name 和 age
{} #初始化和赋值都在__dict__中找到 class Foo:
def __init__(self,name):
self.name = name def __setattr__(self, key, value):
print("__setattr__ key:%s value:%s"%(key,value))
self.__dict__[key] = value obj = Foo("egon")
__setattr__ key:name value:egon
obj.age = 15
__setattr__ key:age value:15
print(obj.__dict__)
{'name': 'egon', 'age': 15} #初始化赋值时判断如果不是数字不可以赋值 class Foo:
def __init__(self,name):
self.name = name def __setattr__(self, key, value):
if not isinstance(value,int):
raise TypeError("type error")
self.__dict__[key] = value obj = Foo(15)
obj.age = 15
print(obj.__dict__)
{'age': 15, 'name': 15}

__setattr__

 __delattr__ 

__delattr__

 #属性不存在时候运行

 class Foo:
def __init__(self,name):
self.name = name def __getattr__(self, item):
print("__getattr__ %s" % item) obj = Foo("egon")
print(obj.name)
egon
print(obj.xxx)
__getattr__ xxx
None

__getattr__

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

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

 #自定义自己的数据类型,追加和插入,如果不是数字,报错

 class List(list):
def append(self, p_object):
if not isinstance(p_object,int):
raise TypeError("Type error")
super().append(p_object) def insert(self, index, p_object):
if not isinstance(p_object,int):
raise TypeError("Type error")
super().insert(index,p_object) l = List([1,2,3])
print(l)
[1, 2, 3]
l.append("") #不能追加字符串 l.insert(0,-1)
print(l)
[-1, 1, 2, 3]

自定义数据类型

 import time

 class Open:
def __init__(self,filepath,mode="r",encode="utf-8"):
self.x = open(filepath,mode=mode,encoding=encode)
self.filepath = filepath
self.mode = mode
self.encoding = encode def write(self,value):
log_time = time.strftime('%Y-%m-%d %X')
self.x.write("%s %s" % (log_time,value)) def __getattr__(self, item):
return getattr(self.x,item) f = Open("b.txt","a+")
f.write("11111111\n")
f.seek(0)
print(f.read())

授权

 #基于继承来定制自己的数据类型
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])
print(l.mid) 基于授权来定制自己的数据类型: class Open:
def __init__(self,filepath,mode,encode='utf-8'):
self.f=open(filepath,mode=mode,encoding=encode)
self.filepath=filepath
self.mode=mode
self.encoding=encode def write(self,line):
print('write')
self.f.write(line) def __getattr__(self, item):
return getattr(self.f,item) f=Open('a.txt','w')
f.write('123123123123123\n')
print(f.seek)
f.close()

授权 继承 数据类型

五.反射作业

1
2
3
4
1.基于授权定制自己的列表类型,要求定制的自己的__init__方法,
2.定制自己的append:只能向列表加入字符串类型的值
3.定制显示列表中间那个值的属性(提示:property
4.其余方法都使用list默认的(提示:__getattr__加反射)
 class List:
def __init__(self,x):
self.seq=list(x) def append(self,value):
if not isinstance(value,str):
raise TypeError('must be str')
self.seq.append(value)
@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):
print("")
return str(self.seq) l=List([1,2,3]) l.append('') print(l.mid) l.insert(0,123)

六.__setitem__,__getitem,__delitem__

 #把对象操作属性模拟成字典的格式

 class Foo:
def __init__(self,name):
self.name = name def __getitem__(self, item):
return self.__dict__[item]
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, item):
self.__dict__.pop(item) f = Foo("egon")
print(f.name)
egon
f.name = "egonlin"
print(f.__dict__)
{'name': 'egonlin'} f["age"] = 18
print(f.__dict__)
{'name': 'egon', 'age': 18} del f["name"]
print(f.__dict__)
{}

把对象操作属性模拟成字典的格式

七.__slots__

'''
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。 '''
class Foo:
__slots__='x' f1=Foo()
f1.x=1
f1.y=2#报错
print(f1.__slots__) #f1不再有__dict__ class Bar:
__slots__=['x','y'] n=Bar()
n.x,n.y=1,2
n.z=3#报错 __slots__使用

  

八.__next__和__iter__实现迭代器协议

 class Foo:
def __init__(self, end,start=0):
self.start = start
self.end = end
def __iter__(self):
return self def __next__(self):
if self.start >= self.end:
raise StopIteration
n = self.start
self.start+=1
return n f = Foo(10)
for i in f:
print(i)

rangge(10)

 class Range:
def __init__(self,start,stop):
self.start = start
self.stop = stop def __iter__(self):
return self def __next__(self):
if self.start >= self.stop:
raise StopIteration("StopIteration Error")
n = self.start
self.start += 1
return n obj = Range(0,15) for i in obj:
print(i)

Range(1,15)

 class Range:
def __init__(self, *args):
self.args = args
if len(self.args) == 2:
self.start = self.args[0]
self.end = self.args[1]
else:
self.start = 0
self.end = self.args[0]
def __iter__(self):
return self def __next__(self):
n = self.start
if n < self.end:
n = self.start
self.start += 1
return n
raise StopIteration for i in Range(1,10):
print(i) for i in Range(10):
print(i)

range(10) range(1,15)

 class Fib:
def __init__(self):
self._a=0
self._b=1 def __iter__(self):
return self def __next__(self):
self._a,self._b=self._b,self._a + self._b
return self._a f1=Fib() print(f1.__next__())
print(next(f1))
print(next(f1)) for i in f1:
if i > 100:
break
print('%s ' %i,end='')

斐波那契数列

九.__del__

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

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

 class Foo:

     def __del__(self):
print('执行我啦') f1=Foo()
del f1
print('------->')
--------------------结果------------- 执行我啦
------->

销毁--->执行完

 class Foo:

     def __del__(self):
print('执行我啦') f1=Foo()
print('------->')
--------------------结果------------- ------->
执行我啦

执行完--->销毁

十.__enter__和__exit__

 with open('a.txt') as f:
  '代码块' 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法 class Open:
def __init__(self,name):
self.name=name def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊') with Open('a.txt') as f:
print('=====>执行代码块') -----------------结果---------------- 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊

说明

 class Open:
def __init__(self,name):
self.name=name def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
print(exc_type)
print(exc_val)
print(exc_tb) with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('***着火啦,救火啊***')
print(''*100) #------------------------------->不会执行

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

 class Open:
def __init__(self,name):
self.name=name def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
print(exc_type)
print(exc_val)
print(exc_tb)
return True with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('***着火啦,救火啊***')
print(''*100) #------------------------------->会执行

如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

 import time

 class Open:
def __init__(self,filepath,mode="r",encode="utf-8"):
self.x = open(filepath,mode=mode,encoding=encode)
self.filepath = filepath
self.mode = mode
self.encoding = encode def __enter__(self):
return self def __exit__(self, exc_type, exc_val, exc_tb):
self.x.close()
return True def write(self,value):
log_time = time.strftime('%Y-%m-%d %X')
self.x.write("%s %s" % (log_time,value)) def __getattr__(self, item):
return getattr(self.x,item) with Open("b.txt","a+") as f : #obj = Open().__enter__() ---> obj = f
f.write("11111111\n")

Open类 with打开

十一.__call__

对象后面加括号,触发执行。

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:
def __init__(self):
pass def __call__(self, *args, **kwargs):
print('__call__') obj = Foo() # 执行 __init__
obj() # 执行 __call__
__call__

  

十二.__str__

 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("agon",18)
print(f1) # --------------结果----------
名字是agon 年龄是18

__str__ 打印时候触发