一 __doc__
表示类的描述信息
#!/usr/bin/env python # -*- coding: utf-8 -*- class C: """ 这是个大美女 """ def func(self): pass print(C.__doc__) #输出:这是个大美女
类的描述信息是无法被继承的
class Foo: '我是描述信息' pass class Bar(Foo): pass print(Bar.__doc__) #该属性无法继承给子类 输出信息: None
二.isinstance和issubclass
# isinstance(obj,Foo)检查是否obj是否是类 Foo 的对象 class Foo(object): pass obj = Foo() print(isinstance(obj,Foo)) # issubclass(Bar, Foo)检查Bar类是否是 Foo 类的派生类 class Foo(): pass class Bar(Foo): pass print(issubclass(Bar, Foo)) # 输出结果: # True # True
三 __module__和__class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
说明:test和module_class 在同一级目录下
test.py
class C: def __init__(self): self.name = 'zzl'
module_class.py
from test import C obj = C() print(obj.__module__) print(obj.__class__) # 输出: # test # <class 'test.C'>
四:__init__
class Foo: def __init__(self, name): self.name = name self.age = 18 obj = Foo('zzl') # 自动执行类中的 __init__ 方法 print(obj.name,obj.age) #输出结果: zzl 18
五 __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): print('执行我啦') f1=Foo() # del f1 print('------->') 输出信息: -------> 执行我啦 class Foo: def __del__(self): print('执行我啦') f1=Foo() del f1 print('------->') # 输出信息: 执行我啦 -------> # 总结:当内存释放的时候,执行__del__
六. __call__
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__ 输出结果: __call__
七.二次加工标准类型(包装)
python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
重写list的append方法:
class List(list): def append(self, p_object): if not isinstance(p_object,int): raise TypeError('must be int') super().append(p_object) l=List([1,2,3]) print(l) l.append(5) print(l)
授权:重写write的方法,并把当前时间写进去:
import time class Open: def __init__(self,file_path,mode='r'): self.obj=open(file_path,mode) def write(self,msg): msg='%s %s'%(time.strftime('%Y-%m-%d %X'),msg) f.obj.write(msg) def __getattr__(self, item): return getattr(self.obj,item) f=Open('a.txt','a') f.write('111 ') f.close() #f.obj.close() a.txt中写入的文件: 2017-05-17 23:14:58 111 2017-05-17 23:16:42 111 2017-05-17 23:18:13 111
八 __getattribute__
__getattr__:
# __getattribute__:不管是否存在我都会执行 class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print('from in __getattribute__ ') f1=Foo(10) f1.x f1.ssss 输出结果:
from in __getattribute__
from in __getattribute__
__getattr__:
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('from in __getattr__ ') f1=Foo(10) print(f1.x) f1.ssss #不存在的属性访问,触发__getattr__
当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('from in __getattr__') # return self.__dict__[item] def __getattribute__(self, item): print('from in __getattribute__') raise AttributeError() f1=Foo(10) f1.x f1.ssss 输出结果: from in __getattribute__ from in __getattr__ from in __getattribute__ from in __getattr__
九 __setitem__,__getitem,__delitem__
#!/usr/bin/env python #-*-coding:utf-8-*- #注意看item赋值的时候都是用的[] 中括号 class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(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): print('del obj.key时,执行我') self.__dict__.pop(item) f1=Foo('zzl') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='cyy' print(f1.__dict__) 输出结果: del obj.key时,执行我 del obj[key]时,执行我 {'name': 'cyy'}
十__str__,__repr__,__format__
#__str__:str函数或者print函数--->obj.__str__(),也就是打印对象时,默认输出该方法的返回值 #repr或者交互解释器--->obj.__repr__() # 如果__str__没有被定义,那么就会使用__repr__来代替输出 # 注意:这俩方法的返回值必须是字符串,否则抛出异常 class Foo: # def __str__(self): # return 'zzl' def __repr__(self): return 'cyy' obj = Foo() print(obj) #输出cyy
class Foo: def __str__(self): return 'zzl' def __repr__(self): return 'cyy' obj = Foo() print(obj) #输出zzl
date_dic={ 'ymd':'{0.year}:{0.month}:{0.day}', 'dmy':'{0.day}/{0.month}/{0.year}', 'mdy':'{0.month}-{0.day}-{0.year}', } class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day def __format__(self, format_spec): if not format_spec or format_spec not in date_dic: format_spec='ymd' fmt=date_dic[format_spec] return fmt.format(self) d1=Date(2016,12,29) print(format(d1)) print('{:dmy}'.format(d1))
输出结果:
2016:12:29
29/12/2016
十一__slots__
#!/usr/bin/env python #-*-coding:utf-8-*- class Foo: __slots__=['x','y'] def __init__(self,x,y,z): self.x=x self.y=y self.z=z f=Foo(1,2,3) 报错: AttributeError: 'Foo' object has no attribute 'z'
解释&总结:
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的) 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。
十二:__next__和__iter__实现迭代器协议
# 实现range的迭代器 class Foo: def __init__(self,start,stop): self.num=start self.stop=stop def __iter__(self): return self def __next__(self): if self.num == self.stop: raise StopIteration n=self.num self.num+=1 return n f=Foo(1,5) # print(next(f)) # print(next(f)) # print(next(f)) # print(next(f)) # print(next(f)) # print(next(f)) for i in f: print(i) 输出结果: 1 2 3 4
注:如果用next,当num等于5的时候会抛出异常
十三:反射
什么是反射?
通过一个字符串名字反射成一个数据属性,python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
hasattr getattr setatar delattr
class Chinese: country='China' def __init__(self,name,age): self.name=name self.age=age p1=Chinese('zzl',18) print(hasattr(p1,'x')) print(getattr(p1,'name')) print(getattr(p1,'x','not find')) print(getattr(p1,'age','not find')) setattr(p1,'x',100) #---》p1.x=100 print(p1.__dict__) delattr(p1,'x') print(p1.__dict__) #输出结果: False zzl not find 18 {'name': 'zzl', 'age': 18, 'x': 100} {'name': 'zzl', 'age': 18}
导入其他模块,利用反射查找该模块是否存在某个方法
#!/usr/bin/env python # -*- coding:utf-8 -*- def test(): print('from the test')
#!/usr/bin/env python # -*- coding:utf-8 -*- """ 程序目录: module_test.py index.py 当前文件: index.py """ import module_test as obj #obj.test() print(hasattr(obj,'test')) getattr(obj,'test')() 输出结果: True from the test
反射的好处:
一.实现可插拔机制
可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
ftpserver端
#!/usr/bin/env python #-*-coding:utf-8-*- class Ftpserver: def __init__(self,ip): self.ip=ip # # def conn(self): # print('正在连接 %s...'%self.ip)
ftpclient端
#!/usr/bin/env python #-*-coding:utf-8-*- import ftpserver if hasattr(ftpserver,'Ftpserver'): cls_fs=getattr(ftpserver,'Ftpserver') print(cls_fs) obj=cls_fs('2.2.2.2') if hasattr(obj,'conn'): func=getattr(obj,'conn') func() print('其他代码') 如果server端不写conn的函数,输出结果是: <class 'ftpserver.Ftpserver'> 其他代码 已经写了conn函数,输出结果是: <class 'ftpserver.Ftpserver'> 正在连接 2.2.2.2... 其他代码 这样就实现了可插拔机制
十四 __setattr__,__delattr__,__getattr__
#!/usr/bin/env python #-*-coding:utf-8-*- 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(180) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=102 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.aaaa 输出结果: ----> from setattr {'y': 180} ----> from setattr {'y': 180, 'z': 102} ----> from delattr {'y': 180, 'z': 102} ----> from getattr:你找的属性不存在
十五 上下文管理
open函数既能够当做一个简单的函数使用,又能够作为上下文管理器。
with open('a.txt', 'w') as f: f.write('hello') f.write('world')
自定义上下文管理器:
要实现上下文管理器,必须实现两个方法 – 一个负责进入语句块的准备操作,另一个负责离开语句块的善后操作。同时,我们需要两个参数:文件名和打开方式。
Python类包含两个特殊的方法,分别名为:__enter__以及__exit__(双下划线作为前缀及后缀)。
__enter__ 方法将在进入代码块前被调用。
__exit__ 方法则在离开代码块之后被调用(即使在代码块中遇到了异常)。
class deamon: def __enter__(self): print('Entering the block') #出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 def __exit__(self, *unused): print('Exiting the block') #with中代码块执行完毕时执行 with deamon(): print('In the block') # 输出: # Entering the block # In the block # Exiting the block
- 没有传递任何参数。
- 在此没有使用“as”关键词。
- 下面我们将讨论__exit__方法的参数设置
class PyOpen: def __init__(self, filename, mode): #通过__init__接受两个参数 self.filename = filename self.mode = mode def __enter__(self): self.openedFile = open(self.filename, self.mode) #打来文件并返回 return self.openedFile def __exit__(self, *unused): #当离开语句块的时候关闭文件 self.openedFile.close() with PyOpen('b.txt', 'w') as f: #模仿文件使用我们自己的上下文管理 f.write('Hello World')
如何去处理异常?
如果语句块内部发生了异常,__exit__方法将被调用,而异常将会被重新抛出(re-raised)。当处理文件写入操作时,大部分时间你肯定不希望隐藏这些异常,所以这是可以的。而对于不希望重新抛出的异常,我们可以让__exit__方法简单的返回True来忽略语句块中发生的所有异常(大部分情况下这都不是明智之举)。
class PyOpen: def __init__(self, filename, mode): #通过__init__接受两个参数 self.filename = filename self.mode = mode def __enter__(self): self.openedFile = open(self.filename, self.mode) #打来文件并返回 return self.openedFile def __exit__(self, exc_type, exc_val, exc_tb): print(exc_type) print(exc_val) print(exc_tb) return True with PyOpen('b.txt', 'w') as f: #模仿文件使用我们自己的上下文管理 f.write('Hello World') raise AttributeError('出现异常啦') print('还可以玩') 输出结果: <class 'AttributeError'> 出现异常啦 <traceback object at 0x0000000000B7B988> 还可以玩
好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
十六 metaclass
元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。好吧,元类就是用来创建这些类(对象)的,元类就是类的类,可以这样理解:
MyClass = MetaClass()
MyObject = MyClass()
__metaclass__属性:
class Mytype(type): def __init__(self,what,bases=None,dict=None): print(what,bases,dict) def __call__(self, *args, **kwargs): print('--->') obj=object.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Room(metaclass=Mytype): def __init__(self,name): self.name=name r1=Room('zzl') print(r1.__dict__) 输出: Room () {'__module__': '__main__', '__init__': <function Room.__init__ at 0x0000000000A8E2F0>, '__qualname__': 'Room'} ---> {'name': 'zzl'}