廖雪峰python教程整理

python基础

  • 数据类型与变量

    • 用r' '表示内部字符不转义,eg:print r'\\t\'

    • 用''' '''的格式表示多行内容,eg:print '''line1换行line2换行line3'''

    • 用大写的变量名表示常量

  • 字符编码

    • 源码包含中文时,前面加两句:#!/usr/bin/env python # -*- coding: utf-8 -*-

    • 格式化输出: 'Hi, %s, you have $%d.' % ('Michael', 1000000)

    • 如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串

  • list和tuple

    • list,可变,用 []

      • append追加元素到末尾:classmates.append('Adam')

      • insert插入元素到指定位置:classmates.insert(1, 'Jack')

      • 要删除list末尾的元素,用pop(),要删除指定位置的元素,用pop(i)

    • tuple,不可变,用()

      • 只有1个元素的tuple定义时必须加一个逗号,,来消除歧义,eg t = (1,)

      • tuple的每个元素,指向永远不变,但是你要把元素设为可变的list也没办法

  • 循环的范围

    • Python提供一个range()函数,可以生成一个整数序列,比如range(5)生成的序列是从0开始小于5的整数,即0,1,2,3,4

  • dict和set

    • dict

      • dict使用键-值(key-value)存储,用大括号,d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}

      • 把数据放入dict,还可以通过key放入,eg:d['Adam'] = 67

      • 避免key不存在的错误

        • 通过in判断key是否存在:'Thomas' in d

        • 通过dict提供的get方法,如果key不存在,可以返回None,或者自己指定的value:d.get('Thomas', -1)

      • 要删除一个key,用pop(key)

      • dict的key必须是不可变对象

    • set ([])

      • set是一组不重复key的集合,不存储value,重复元素会被自动过滤

      • 通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果

      • 通过remove(key)方法可以删除元素

      • set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作

      • set和dict一样,key必须是不可变对象,可以是tuple

函数

  • 如果想定义一个什么事也不做的空函数,可以用pass语句,可以占个位

  • 数据类型检查可以用内置函数isinstance

  • 函数可以同时返回多个值,但其实就是一个tuple

  • 函数的参数

    • 默认参数

      • 必选参数在前,默认参数在后

      • 当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数

      • 默认参数必须指向不变对象

    • 可变参数,一个星号

      • 传入的参数个数是可变的,在参数前面加了一个*号,eg:def calc(*numbers):

      • 如果已经有一个list或者tuple,要调用一个可变参数,在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去,eg:calc(*nums)

    • 关键字参数,两个星号

      • 允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict,eg:def person(name, age, **kw),该函数除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数

      • 也可以传入任意个数的关键字参数

    • 参数组合

      • 参数定义的顺序必须是:必选参数、默认参数、可变参数和关键字参数

      • 使用*args和**kw是Python的习惯写法

  • 递归函数

    • 递归调用栈溢出的解决办法:尾递归优化,和循环效果一样

高级特性

  • 切片(Slice)操作符取指定索引范围,对list和tuple和字符串都可以进行切片

  • 迭代(Iteration):通过循环遍历list或tuple或其他可迭代对象(字符串等)上

    • 默认情况下,dict迭代的是key,而且迭代出的结果顺序很可能不一样

      • 如果要迭代value,可以用for value in d.itervalues()

      • 如果要同时迭代key和value,可以用for k, v in d.iteritems()

    • 判断是否可迭代:通过collections模块的Iterable类型判断

    • 实现list的下标循环:enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身

  • 列表生成式(List Comprehensions):python内置的可以用来创建list的生成式

    • 写列表生成式时,把要生成的元素放到前面,后面跟for循环,就可以把list创建出来

    • for循环后面还可以加上if判断

    • 还可以使用两层循环,可以生成全排列

  • 生成器(Generator):一边循环一边计算的机制,它保存的是算法

    • 创建方法:

      • 法一:只要把一个列表生成式的[]改成()

      • 法二:创建generator函数

    • 打印其中元素的方法:

      • 法一:用next(),基本不用。每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误

      • 法二:for循环迭代

      • 法三:for没法解决的,用函数实现

        • 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

        • generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行

函数式编程(Functional Programming)

  • 一种抽象程度很高的编程范式,特点:允许把函数本身作为参数传入另一个函数,还允许返回一个函数

  • 高阶函数(Higher-order function)

    • 变量可以指向函数

    • 函数名其实就是指向函数的变量

    • 高阶函数:接收另一个函数作为参数的函数

    • map/reduce

      • map():接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回

      • reduce():reduce把一个函数作用在一个序列[x1, x2, x3...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

    • filter()函数用于过滤序列,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素

    • sorted()函数可以对list进行排序,它还可以接收一个比较函数来实现自定义的排序

  • 返回函数:把函数作为结果值返回

    • 返回的函数并没有立刻执行,而是直到调用了f()才执行

    • 闭包(Closure):返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用

    • 返回函数不要引用任何循环变量,或者后续会发生变化的变量

    • 要是非要引用循环变量,应该再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变

  • 匿名函数:没名字的函数

    • 用关键字lambda表示,冒号前面的x表示函数参数

    • 限制:只能有一个表达式,不用写return,返回值就是该表达式的结果

    • 好处:不用担心函数名冲突

    • 可以把匿名函数赋值给一个变量,再利用变量来调用该函数

    • 也可以把匿名函数作为返回值返回

  • 装饰器(Decorator):在代码运行期间动态增加功能的方式,借助@语法,把decorator置于要扩充函数的定义处

    • 函数对象有一个__name__属性,可以拿到函数的名字

    • 在定义wrapper()的前面加上@functools.wraps(func)

  • 偏函数(Partial function):

    • functools.partial() 把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单

    • 创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数

    • 当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单

模块:

一个.py文件就称之为一个模块(Module)

  • 好处:

    • 大大提高了代码的可维护性

    • 编写代码不必从零开始

    • 还可以避免函数名和变量名冲突

  • 注意:尽量不要与内置函数名字冲突

  • 避免模块名字冲突的方法:包(Package)

    • 每一个包目录下面都必须有一个__init__.py的文件,否则,Python就把这个目录当成普通目录,而不是一个包

    • 可以有多级目录,组成多级层次的包结构

  • 使用模块:

    • import

    • 使用别名import ... as ...

    • 作用域

      • 正常的函数和变量名是公开的(不带_)可以被直接引用

      • 类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途

      • 类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用

      • 外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public

  • 安装第三方模块:第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索

    • 模块搜索路径:默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中

      • 法一:直接修改sys.path,添加要搜索的目录,sys.path.append(' '),这种方法是在运行时修改,运行结束后失效

      • 法二:设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中

  • 使用__future__模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性

面对对象编程(Object Oriented Programming),OOP

  • OP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数

    • 对象包含属性(Property)

    • 对象的方法(Method):方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据

    • 三大特点:数据封装、继承和多态

    • 思想:面向对象的设计思想是抽象出Class,根据Class创建Instance

    • 类和实例:

      • 类(Class):自定义的对象数据类型,类名通常是大写开头的单词

      • (object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类

      • 通过定义一个特殊的__init__方法,在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去

        • 注意到__init__方法的第一个参数永远是self,表示创建的实例本身

        • 有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传

      • 在类中定义的函数,第一个参数永远是实例变量self

      • Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同

    • 访问限制:

      • 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__

      • 以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量

      • 以一个下划线开头的实例变量名,意思是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”

    • 继承和多态:

      • 继承的好处:

        • 子类获得了父类的全部功能,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重

        • 多态:

          • 多态的好处:调用方只管调用,不管细节,即开闭原则:对扩展开放,对修改封闭

    • 获取对象信息:

      • type()

        • 可以获得对象类型,返回的是type类型

        • Python把每种type类型都定义好了常量,放在types模块

        • 所有类型本身的类型就是TypeType

      • isinstance()

        • isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上

        • 还可以判断一个变量是否是某些类型中的一种

      • dir()

        • 列出一个对象的所有属性和方法

        • 配合getattr()、setattr()以及hasattr(),可以直接操作一个对象的状态

          • getattr():获取对象的属性,如果试图获取不存在的属性,会抛出AttributeError的错误,可以传入一个default参数,如果属性不存在,就返回默认值

          • setattr():给对象设置一个属性

          • hasattr():判断是否有某个属性

面向对象高级编程

  • 使用__slots__限制该class能添加的属性

    • __slots__定义的属性仅对当前类起作用,对继承的子类是不起作用的

    • 除非在子类中也定义__slots__,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__

  • 使用@property,保证对属性的必要检查

    • 该装饰器就是负责把一个方法变成属性来调用

    • 把一个getter方法变成属性,只需要加上@property就可以

    • @property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值

    • 只读属性:只定义getter方法,不定义setter方法就是一个只读属性

  • 多重继承

    • 通过多重继承,一个子类就可以同时获得多个父类的所有功能

    • Mixin:用多重继承的一种常见设计

      • 目的:给一个类增加多个功能

      • 好处:不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类

  • 定制类:让自己定义的类表现得和Python自带的list、tuple、dict没什么区别

    • __len__()方法用于让class作用于len()函数

    • __str__()返回用户看到的字符串,可以打印出好看的字符串

    • __repr__()返回程序开发者看到的字符串,为调试而服务的

    • __iter__(),一个类要是想被用于for循环中,就要实现这个方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环

    • __getitem__(),实现这个方法可以让该类像list一样按照下标取出元素,把对象视作list或dict来对集合赋值,它传入的参数可能是一个int,也可能是一个切片对象slice

    • __delitem__()方法,用于删除某个元素

    • __getattr__(),只有在没有找到属性的情况下,才调用该方法,默认返回None,如果要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误

    • __call__(),用于直接对实例进行调用

      • 函数和对象的界限是很模糊的

      • 通过callable()函数,就可以判断一个对象是否是“可调用”对象,比如函数和带有__call()__的类实例

  • 使用元类

    • type():

      • 既可以返回一个对象的类型,也可以创建新的类型,创建要传入的参数:

        • \1. class的名称;

        • \2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;

        • \3. class的方法名称与函数绑定

        • 大多数情况还是用class定义类,type函数允许动态创建类

    • metaclass(元类):

      • 先定义metaclass,就可以创建类,最后创建实例

      • metaclass允许你创建类或者修改类

      • 要通过元类的__new__()的指示来创建新类,新类具有元类的方法

      • 需要通过metaclass修改类定义的场合:ORM(Object Relational Mapping,对象-关系映射),就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表

      • 千万不要把实例属性和类属性使用相同的名字

错误、调试和测试

  • 错误处理:

    • try机制:

      • 当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块

      • 可以有多个except来捕获不同类型的错误

      • 如果没有错误发生,可以在except语句块后面加一个else,当没有错误发生时,会自动执行else语句

      • Python的错误其实也是class,所有的错误类型都继承自BaseException,一旦在except中先使用这个类型,别的错误都捕捉不到了,比如ValueError是StandardError的子类

      • 好处:跨越多层调用,于是可以只在合适的层捕获错误即可,不用每个都写

    • 调用堆栈:如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出;可以通过打印出的错误信息来追踪错误源头

    • 抛出错误:

      • 自己定义错误class,用raise抛出

      • 只有在必要的时候才定义自己的错误类型,尽量使用python内置的错误类型

    • 错误类型转换:

      • 在except中raise一个error,可以把一个类型的错误转化成另一种类型,只要是合理的转换逻辑即可;

      • 但是,决不应该把一个IOError转换成毫不相干的ValueError

      • raise语句如果不带参数,就会把当前错误原样抛出

  • 调试:调试程序的手段

    • \1. 用print打印可能有问题的变量

      • 坏处:将来还要删掉

    • \2. 断言:

      • 用print来辅助查看的地方,都可以用assert代替

      • assert A, ‘B’,表达式A应该是True,否则会抛出AssertionError并打印B

      • 启动Python解释器时可以用-O参数来关闭assert

    • \3. logging:不会抛出错误,而且可以输出到文件

      • 好处:可以指定记录信息的级别,比如debug、info、warning、error等,于是可以统一控制输出哪个级别的信息

    • \4. pdb:启动调试器pdb,单步运行程序

      • 以参数-m pdb启动后,输入命令l来查看代码

      • 输入命令n可以单步执行代码

      • 任何时候都可以输入命令p 变量名来查看变量

      • q结束调试

      • 坏处:代码一长就麻烦了

    • \5. pdb.set_trace():需要import pdb,然后,在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点

      • 程序会自动在pdb.set_trace()暂停并进入pdb调试环境,可以用命令p查看变量,或者用命令c继续运行

    • \6. 使用支持调试功能的IDE比如Pycharm

  • 单元测试:

    • 单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作

    • 需要引入Python自带的unittest模块,对每一类测试都需要编写一个test_xxx()方法

    • 运行方法:两种

  • 文档测试:doctest非常有用,不但可以用来测试,还可以直接作为示例代码。通过某些文档生成工具,就可以自动把包含doctest的注释提取出来。用户看文档的时候,同时也看到了doctest

IO编程

  • 文件读写

    • 读文件:默认都是读取文本文件,并且是ASCII编码的文本文件

      • 用Python内置的open()函数,传入文件名和标示符,标示符'r'表示读

      • 如果文件不存在,open()函数就会抛出一个IOError的错误

      • 如果文件打开成功,接下来,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示

      • 最后一步是调用close()方法关闭文件

      • 为了保证无论是否出错都能正确地关闭文件,可以使用try ... finally来实现,但是很繁琐,所以一般都用with语句

      • 保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容

      • 调用readline()可以每次读取一行内容

      • 调用readlines()一次读取所有内容并按行返回list

    • file-like Object

    • 二进制文件:比如图片、视频等等,用'rb'模式打开文件即可

    • 字符编码:

      • 要读取非ASCII编码的文本文件,就必须以二进制模式打开,再解码,麻烦

      • Python还提供了一个codecs模块帮我们在读文件时自动转换编码,直接读出unicode

    • 写文件:

      • 调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件

      • 一定要用f.close()来关闭文件

      • 用with语句来得保险

  • 操作文件和目录:import os模块

    • 环境变量:

      • 全部保存在os.environ这个dict中

      • 要获取某个环境变量的值,可以调用os.getenv()函数

    • 操作文件和目录:操作文件和目录的函数一部分放在os模块中,一部分放在os.path模块中

      • 查看当前目录的绝对路径:os.path.abspath('.')

      • 在某个目录下创建一个新目录

        • 首先把新目录的完整路径表示出来:os.path.join('/Users/michael', 'testdir')

        • 然后创建一个目录:os.mkdir('/Users/michael/testdir')

        • 删除目录:rmdir

      • 合并拆分路径:并不要求目录和文件要真实存在,它们只对字符串进行操作

        • os.path.join():可以正确处理不同操作系统的路径分隔符

        • os.path.split():可以把一个路径拆分为两部分,后一部分总是最后级别的目录或文件名

        • os.path.splitext()可以直接让你得到文件扩展名

      • 文件操作:

        • 对文件重命名:os.rename()

        • 删除文件:os.remove()

        • 复制文件:用shutil模块提供的copyfile()的函数

        • 过滤文件:

          • 列出当前目录下的所有目录:[x for x in os.listdir('.') if os.path.isdir(x)]

          • 要列出所有的.py文件:[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']

猜你喜欢

转载自blog.csdn.net/dmbjzhh/article/details/80688507