11 描述器 链表 异常 模块

描述器

  • 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
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后面的名称
  • 用法示例 :
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 dimport a.b.d
from .. import eimport a.e
from .d import ximport a.b.d.x
from ..e import ximport a.e.x
  • __slots__属性 - 限定实例属性
  • 实例属性保存于实例字典 当实例过多,会占用过多内存
  • __slots__ 相当于实例属性的”白名单”
  • 一旦使用__slots__,就会阻止实例生成字典
  • __slots__属性不会被子类继承
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值