描述器
- Python中,一个类实现了__get__ __set__ __delete__ 三个方法中的任意一个就是描述器
- 仅实现__get__ 的称为非数据描述器(non-data descriptor)
- 同时实现__get__ 与__set__ 的称为数据描述器(data descriptor)
- 属性查找顺序 :
- 数据描述器优先于实例字典(实质是将实例属性从实例字典移除)
- 实例字典优先于非数据描述器
- 数据描述器 –> 实例字典 –> 非数据描述器
- 描述器在Python中使用广泛
- classmethod staticmethod 为非数据描述器
- 实例可重新定义与覆盖方法,允许单个实例获取与其他实例不同得方法
- property为数据描述器
- 实例不能覆盖属性的行为
- @StaticMethod
class StaticMethod:
def __init__(self,fn):
self._fn=fn
def __get__(self, instance, owner):
return self._fn
class A:
@StaticMethod
#stmtd=StaticMethod(stmtd)
def stmtd():
print('static method')
A.stmtd()
A().stmtd()
- @ClassMethod
from functools import partial
class ClassMethod:
def __init__(self,fn):
self._fn=fn
def __get__(self, instance, owner):
return partial(self._fn,owner)
class A:
@StaticMethod
#clsmtd =ClassMethod(clsmtd)
def clsmtd(cls):
print(cls.__name__)
A.__dict__
A.clsmtd()
链表
- 链表是一种重要的数据结构
- 链表由若干个结点(node)组成
- 每个结点有数据域与指针域
- 数据域存放数据 指针域存放前后结点
- 为方便执行 一般将头尾结点固定为Head与tail
- 单链表每个结点只有后向指针(指向下一个节点)
- 双链表每个结点都有两个方向的指针
- 单链表尾部结点的后向指针为空
- 双链表尾部结点的后向指针与头部节点的前向指针均为空
- 链表与顺序表时间复杂度对比 :
item | 查找的时间复杂度 | 插入的时间复杂度 |
---|---|---|
链表 | O(n) | O(1) |
顺序表 | O(1) | O(n) |
- 指针为汇编语言与C语言的概念
- 支持面向对象的语言可以用实例属性模拟指针
- Python实现单链表
class Node:
def __init__(self,item,next=None):
self.item=item
self.next=next
def __repr__(self):
return repr(self.item)
class SingleLinkedList:
def __init__(self):
self.head=None
self.tail=None
def append(self):
node=Node(item)
if self.head is None:#empty
self.head=node
else:
self.tail.next = node
self.tail=node#update tail
return self
def iternodes(self):
current = self.head
while current:
yield current
current=current.next
- Python实现双链表
class DNode(Node):
def __init__(self,item,next=None,prev=None):
super().__init__(item,next)
self.prev = prev
class DoubleLinkedList(SingleLinkedList):
def __init__(self):
self.head=None
self.tail=None
def append(self, item):
node = DNode(item)
if self.head is None: # empty
self.head = node
else:
self.tail.next = node
node.prev = self.tail
self.tail = node # update tail
return self
def iternodes(self,reverse=False):
current = self.tail if reverse else self.head
while current:
yield current
current = current.prev if reverse else current.next
def insert(self, index, item):
if index < 0:
raise IndexError
currentnode = None
for i, node in enumerate(self.iternodes()):
if i == index:
currentnode = node
break
else: # index not found
self.append(item)
return
insertnode = DNode(item)
prevnode = currentnode.prev
if prevnode is None: # insert on index 0
self.head = insertnode
else:
prevnode.next = insertnode
insertnode.prev = prevnode
insertnode.next = currentnode
currentnode.prev = insertnode
def pop(self):
if self.tail is None:
raise Exception('Empty')
lastnode = self.tail
prevnode = lastnode.prev
if prevnode is None:
self.head = None
self.tail = None
else:
prevnode.next = None
self.tail = prevnode
return lastnode.item
def remove(self, index):
if self.tail is None:
raise Exception('Empty')
if index < 0:
raise IndexError
currentnode = None
for i, node in enumerate(self.iternodes()):
if i == index:
currentnode = node
break
else:
raise IndexError('Wrong index' + str(index))
prevnode = currentnode.prev
nextnode = currentnode.prev
if prevnode is None and nextnode is None:#only 1 node
self.head = None
self.tail = None
elif prevnode is None:#remove on index 0
self.head = nextnode
nextnode.prev=None
elif nextnode is None:#remove on index -1
self.tail = prevnode
prevnode.next=None
else:
prevnode.next=nextnode
nextnode.prev=prevnode
del currentnode
异常 Exception
- 可能的意外情况,不同于错误
- 例如open函数打开的文件不存在或访问网络时网络中断等情况
- 异常可以被捕获,并被处理 但错误不可以
- 异常的产生:
- Python解释器检测到并引发异常
- raise语句显式抛出异常
- 程序会在抛异常处中断执行,不捕获异常程序会结束运行
- raise后什么也不写,表示抛出最近被激活的异常. 若没有最近被激活的异常,则该语句本身就会产生RuntimeError异常.此方式很少使用
- raise后接BaseException类的子类与实例,后接类则会被无参实例化.实际使用中一般后接Exception类
- 异常捕获语法:
try:
待捕获异常的代码块
except [异常类]:
异常处理的代码块
- try语句捕捉到异常后,异常位置之后的语句不再执行,转而执行except部分的语句
- 常见的异常类及继承层次
- BaseException
- SystemExit
- EXception
- RuntimeError
- RecursionError
- ArithmeticError
- ZeroDivsionError
- LookupError
- IndexError
- KeyError
- SyntaxError
- OSError
- ChildProcessError
- ConnectionError
- FileExistsError
- FileNotFoundError
- RuntimeError
import sys
try:
sys.exit(1)
except SystemExit:
print('SysExit')
print('outer')
- BaseException是所有内建异常类的基类
- Exception是所有内建的 非系统退出的异常类的基类 自定义异常类应继承自它
- 异常捕获的规则:
- except语句可有多个 从上到下比较,匹配则进入对应的except语句块
- 若被某一except语句捕获,则其他except语句不会再次捕获
- 若无任何except语句捕获异常,则向外抛出该异常
- 异常捕获的原则:从小到大,从具体到宽泛
- 被抛出的异常为异常类的实例,可用as子句获取该异常实例
class MyException(Exception):
def __init__(self,code,message):
self.code=code
self.message=message
try:
raise MyException(200,'ok')
except MyException as e:
print('exception info:',e.code,e.message)
except Exception as e:
print('exception info:',e)
- finally子句放于try except子句之后,不管是否发生异常,最后都会执行.
finally子句中一般放置资源清理 释放工作的语句
else子句放于except与final之间 没有发生任何异常则执行
try:
待捕获异常的代码块
except [异常类]:
异常处理的代码块
except 异常类 as <变量名>:
异常处理的代码块 #as子句可获得异常对象
else:
未发生异常则会执行的语句块
finally:
无论try中是否异常,一定会执行的语句
模块 module
- 模块 : Python源代码文件
- 包/Package : 模块组织一起的和包名同名的目录及其相关文件
- 导入模块的语句
- import 模块1[,模块2,…]
- import <模块> as <模块别名>
- import语句
- 找到指定模块,加载并初始化,生成模块对象.找不到则抛ImportError异常
- 导入后在import所在作用域的局部命名空间(可用dir()函数查看)中增加名称,并关联上一部创建的对象
- 若导入顶级模块,其名称会加入本地名词空间中,并绑定到其模块对象
- 若导入非顶级模块,只将顶级模块名加入本地名词空间. 导入的模块必须用完整名称来访问
- 若使用as,as后面的名称直接绑定到导入的模块对象,并将该名称加入到本地名词空间中
-用法示例 :
import functools
import functools as fl
import os.path as osp
import os.path #不推荐
- from语句
- 找到from子句中指定的模块,加载并初始化(不是导入)
- import子句后的名称
- 先查from子句后导入的模块是否具有该名称的属性
- 没有则尝试导入该名称的子模块
- 未找到则抛异常
- 该名称保存于本地名词空间
- 若有as子句,则使用as后面的名称
- import子句后的名称
- 用法示例 :
from pathlib import Path,PosixPath
from pathlib import *
'''
若模块没有\_\_all__,在当前名称空间导入该模块的所有公共成员(非下划线开头的成员)
若模块有\_\_all__,则导入\_\_all__列表中指定的名称
'''
from functools import wraps as wr,partial
from os.path import exists
模块搜索顺序
- 可用sys.path查看
- sys.path可被修改,追加新的目录
- 路径顺序为 :
- 程序主目录 –> PYTHONPATH –> 标准库目录
- 程序主目录 : python代码所在目录
- PYTHONPATH : Python程序的安装目录
- 标准库目录 : Python自带的库模块所在目录,通常为PYTHONPATH/Lib
- 模块的重复导入
- 所有加载的模块保存于sys.modules中
- sys.modules是字典,因此可避免重复导入
- __name__ 用于存储当前模块的名称
- 运行模块时,__name__的值为 : ‘__main__ ‘
- 通过import被其他模块导入时,__name__ 为模块名
- 可用 if __name__ == ‘__main__’:语句判断以上两种情况
if __name__ == '__main__':
print('in __main__)
else:
print('in import mode')
- 模块的属性 :
属性 | 含义 |
---|---|
__file__ | 字符串,源文件路径 |
__cached__ | 字符串,编译后的字节码文件路径 |
__spec__ | 显示模块规范 |
__name__ | 模块名 |
__package__ | 当模块是包,同__name__ 否则可设置为顶级模块的空字符串 |
包/Package
- Python中,若目录下存在__init__.py文件,则该目录为包
- 包目录下的py文件 子目录都是其子模块
- 包能更好地组织模块:可将大量代码拆分为很多子模块,待需要时再加载
- 导入子模块一定会加载父模块 反之则不成立
- 如同类与函数,模块也是封装 模块可封装变量 类 函数
- 模块就是命名空间,其内部顶层标识符都是它的属性,可通过__dict__或dir(module)查看
- 包也是模块,但模块不一定是包
- 包是特殊的模块,是一种组织方式,包含__path__属性
绝对导入&相对导入
- 绝对导入
- 用import与from语句时,模块名称最前面不是以.开头
- 绝对导入从模块搜索路径中寻找模块
- 相对导入
- 只能在包内使用,且只可用于from语句
- .表示当前目录 ..表示父目录
- 不要在顶层模块使用相对导入
- 假设有a.b.c的模块(目录)层级 c是模块 在c的代码中可使用如下导入语句 :
相对导入 | 绝对导入等价写法 |
---|---|
from . import d | import a.b.d |
from .. import e | import a.e |
from .d import x | import a.b.d.x |
from ..e import x | import a.e.x |
- __slots__属性 - 限定实例属性
- 实例属性保存于实例字典 当实例过多,会占用过多内存
- __slots__ 相当于实例属性的”白名单”
- 一旦使用__slots__,就会阻止实例生成字典
- __slots__属性不会被子类继承