一、开发基础(3)

佐手、 提交于 2020-03-25 09:28:44

模块

  • 模块也是一个对象

    • 模块的出现是为了代码的重用
    • 通过模块,将大型应用分解为小模块,小模块分别实现不同的功能
    • 使用模块最重要的就是要避免循环导入
    • 模块名/目录名遵循命名语法规则
  • import如何工作

    • 第一次导入时,找到模块文件,编译成位码,执行代码创建其顶层定义的对象
    • 已经导入的模块保存在sys.modules字典中
  • 模块搜索路径

    • 环境变量
    • sys.path列表,调用append方法,这种修改只在脚本运行时保持,退出就消失
  • 支持的导入格式

    • py
    • pyc
    • 包导入
    • 编译拓展模块等等
  • from和import

    • 都是赋值语句,import将整个模块作为对象赋值给一个模块名,from将一个或多个变量名赋值给同名对象
    • from赋值过来的变量名与其来源的文件没有联系,因此在reload时from导入的变量名可能还是之前的版本
  • reload

    • reload是内置函数,不是语句
    • 传给reload的是已存在的模块对象,不是变量
    • reload在Python3中位于模块中,使用前必须先导入自己 from importlib import reload
    • import影响所有import导入的模块和以后使用from的模块,不影响之前的from
    • 包的目录中必须有__init__.py文件
    • 包导入在模块导入的基础上拓展了本地作用域的概念,对模块进行了更高一级的分组,就如同模块是对程序的分解,包则是对模块的分解
    • 相对导入 from . import module Python3中不带.的导入总是先在sys.path的绝对路径中查找
    • 使用绝对导入,sys.path需要包含包的根目录,不如相对导入方便,并且相对导入只使用from语句
  • 模块隐藏数据

    • 使用_X单下划线命名的变量不会被导入,这种方法针对from module improt *语句
    • 在模块顶层创建__all__列表,只放可以被导入的变量名,这种方法也是针对from module improt *语句
  • 循环导入

    • 常见的临时解决办法一是在函数内import,二是在模块结尾导入,但都不是最好的办法
    • 最好的办法是对两个模块循环导入使用的功能进行抽象,放入一个单独模块文件中,让这两个模块导入该单独的模块文件即可完成解耦

  • 类的概念

    • 其实就是一个高级的字典,除了提供属性访问,还提供了方法用于处理数据
    • 面向对象编程最重要的概念就是继承和多态,而类很好的提供了这些功能
  • 类的属性

    • 类属性,所有实例共享
    • 实例属性,既可以在__init__方法中初始化,也可以在实例化后进行赋值,这种属性是每个实例独有的,只能通过该实例对象访问
  • 类的方法

    • 类方法,第一个参数是类(人为默认是cls),可以通过类名.方法调用,也可以通过实例.方法调用,但是无法在外部调用,相当于
Test.classmethod(Test, *args, **kwargs)
test.classmethod(test.__class__, *args, **kwargs)
- 静态方法,不接收类或实例作为参数,但是类和实例都能调用,与类方法没什么太大区别,唯一区别就是静态方法内一般不会调用到类或实例对象
- 实例方法,第一个参数是实例(默认是self),只能通过实例调用,会自动传入实例本身
- 魔法方法/运算符重载方法,Python类内置的一些方法,由于继承机制,我们可以通过修改这些魔法方法中的内容来为我们的类添加一些特殊功能
    - 例如直接print实例,返回的是一个`<__main__.object>`这种对象,通过重载`__str__`或`__repr__`,再次print这个实例时会显示一些我们定义的内容
    - 常见的魔法方法:(以下方法省略了头尾的双下划线,在使用中记得补上)
        - new:创建实例对象
        - init:为实例对象的属性进行初始化操作
        - del:对象收回,也称析构方法,用了该方法的类创建的实例不会被gc自动回收,因此一般不会重写这个方法
        - add:运算符+,左侧加法,即实例对象在左侧,如ins + 5
        - sub:运算符-,左侧减法,即实例对象在左侧,如ins - 5
        - or:运算符|
        - repr,str:打印,转换,str只适用于print和str函数,`__repr__`可用于所有环境中,程序优先使用`__str__`
        - call:函数调用
        - getattr:点号运算
        - setattr:属性赋值语句
        - delattr:属性删除
        - getattribute:属性获取
        - getitem:索引运算
        - setitem:索引赋值语句
        - delitem:索引和分片删除
        - len:长度
        - bool:布尔测试
        - lt,gt,le,ge,eq,ne:<, >, <=, >=, ==, !=
        - radd:右侧加法
        - iadd:增强加法+=
        - iter,next:迭代环境,前面写过,`__iter__`对象返回自身即self,next返回下一个值,并且应该提供StopIteration异常
        - contains:成员关系测试,优先`__contains__`,其次`__iter__`,最后`__getitem__`
        - index:整数值
        - enter,exit:环境管理器,用于with方法的调用
        - get,set:描述符

魔法方法早期学习时只要学__repr____init__即可,以后使用多了就会慢慢理解其它方法。

  • 类的继承

    • 实例调用__class__获取它的类,类通过__bases__获取父类列表
    • Python中的类的继承是用树的形式来表达的,因此就出现了两种继承顺序
      • Python2中按深度优先的顺序进行继承搜索,忽视右侧
      • Python3中按广度优先的顺序进行继承搜索,可以遍历到所有继承的类
      • 这种继承在某些书中会称为钻石继承,或MRO树等,但其实就是以上两种方法
    • 只要父类树中某个类提供了方法,则其下的所有子类都将获得这个方法,这种继承使得功能不需重复实现,只要继承就可以了
    • 继承方法的覆盖,只要在子类中重写了该方法,则子类的实例永远无法调用到父类的方法,除非直接通过父类调用其类方法,解决办法是在该方法中再调用父类的方法
  • 类的组合

    • 在实际业务中,可能会出现容器类,就是这个类的某个属性调用了其它类的实例,在这个类中能通过该实例调用其它类的方法
    • 这种将其它类实例作为属性参与到运行中,就是组合类,它实现了不同类接口的整合
  • 类的包装

    • 与组合差不多,只是它接收的是类本身而不是类的实例
  • 类的多态

    • 有个说法就是鸭子类型,其实本身Python在多态方面就支持的比较好,如+在面对数字和字符串时表示的含义完全不同,但也能直接使用,而不用顾及类型是否需要提前声明
  • 类的私有属性

    • 在命名规则中有提到过数据隐藏
    • 类中单下划线开头的变量是由程序员指定的私有变量,但仍然可以被外部访问
    • 双下划线开头的变量在使用时会被自动拓展变量名,避免了重名,如__x变成了_class__x,会自动加单下划线+类名
    • 类和实例调用__dict__获取属性字典
    • 在类中顶层定义__slots__列表,限制可以使用的属性,如果父类没有定义slots,则子类总可以访问到父类的__dict__属性
  • 元类

  • 类的特性和Mixin多重继承

    • Mixin多重继承其实就是使某一个类继承自不同的类树,从而获得不同的属性,最简单的例子就是子女继承了父母的基因,而父母是从不同基因树继承过来的。
    • 类特性回头再写
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!