Python学习笔记
课程链接
Python基础
- 整数
- 对于很大的数,例如
10000000000,很难数清楚0的个数。Python允许在数字中间以_分隔,即10_000_000_000和10000000000是一样的
- 对于很大的数,例如
- 浮点数
- 对于很大或很小的浮点数,就必须用科学计数法表示,把10用e替代,就是
1.23e9,或者12.3e8,0.000012可以写成1.2e-5
- 对于很大或很小的浮点数,就必须用科学计数法表示,把10用e替代,就是
- 字符串
- 若字符串内部既包含
'又包含"则可以用转义字符\来标识,如I'm "OK"!可以写成:'I\'m \"OK\"!' - 若字符串里面有很多字符都需要转义,为了简化,Python还允许用
r''表示''内部的字符串默认不转义:>>> print('\\\t\\') \ \ >>> print(r'\\\t\\') \\\t\\
- 若字符串内部既包含
- Python中的两种除法
/除法计算结果是浮点数,即使两个整数整除//除法只取结果的整数部分
- 字符编码
- Python提供了
ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:>>> ord('A') 65 >>> chr(66) 'B' - 以Unicode表示的
str通过encode()方法可以编码为指定的bytes:>>> 'ABC'.encode('ascii') b'ABC' >>> '中文'.encode('utf-8') b'\xe4\xb8\xad\xe6\x96\x87' - 相反,从网络或磁盘上读取了的字节流数据就是
bytes。要把bytes变为str,就需要用decode()方法:>>> b'ABC'.decode('ascii') 'ABC' >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8') '中文' - Python源代码文件头:
#!/usr/bin/env python3 # -*- coding: utf-8 -*-
- Python提供了
- 格式化
- 使用
%:>>> 'Hello, %s' % 'world' 'Hello, world' >>> 'Hi, %s, you have $%d.' % ('Michael', 1000000) 'Hi, Michael, you have $1000000.' - 使用
format()函数 :>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125) 'Hello, 小明, 成绩提升了 17.1%' - 使用
f-string:>>> r = 2.5 >>> s = 3.14 * r ** 2 >>> print(f'The area of a circle with radius {r} is {s:.2f}') The area of a circle with radius 2.5 is 19.62
- 使用
- 有序列表
list与tuple- list可以随时添加,删除和更改元素;tuple一经初始化就不能修改
>>> classmates = ['Michael', 'Bob', 'Tracy'] # list >>> classmates = ('Michael', 'Bob', 'Tracy') # tuple
- list可以随时添加,删除和更改元素;tuple一经初始化就不能修改
range()函数- 生成一个整数序列,再通过list()函数转换为list
>>> list(range(5)) [0, 1, 2, 3, 4]
- 生成一个整数序列,再通过list()函数转换为list
dict字典类型- 使用键-值(key-value)存储
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} >>> d['Michael'] 95
- 使用键-值(key-value)存储
set集合类型set也是一组key的集合,但不存储value,且key不能重复:>>> s = set([1, 1, 2, 2, 3, 3]) >>> s {1, 2, 3}set可进行交集、并集等操作:>>> s1 = set([1, 2, 3]) >>> s2 = set([2, 3, 4]) >>> s1 & s2 {2, 3} >>> s1 | s2 {1, 2, 3, 4}
函数
- 空函数
pass语句什么都不做,可用来作为占位符让程序先跑起来
- 参数检查
- 数据类型检查可以使用内置函数
isinstance()实现:def my_abs(x): if not isinstance(x, (int, float)): raise TypeError('bad operand type') if x >= 0: return x else: return -x
- 数据类型检查可以使用内置函数
- 默认参数
- 设置默认参数时,必选参数在前,默认参数在后
- 有多个默认参数时,调用时既可以按顺序提供默认参数,又可以不按顺序提供部分默认参数。当不安顺序提供部分默认参数时,需要把参数名写上
- 可变参数
- 对于参数个数不确定的函数可将参数变为可变参数:
def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum >>> calc(1, 2) 5 >>> calc() 0 >>> nums = [1, 2, 3] >>> calc(*nums) 14
- 对于参数个数不确定的函数可将参数变为可变参数:
- 关键字参数
- 关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict:
def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw) >>> person('Bob', 35, city='Beijing') name: Bob age: 35 other: {'city': 'Beijing'} >>> person('Adam', 45, gender='M', job='Engineer') name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'} >>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **extra) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
- 关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict:
- 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
高级特性
- 切片
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] >>> L[0:3] ['Michael', 'Sarah', 'Tracy'] >>> L[:3] ['Michael', 'Sarah', 'Tracy'] >>> L[1:3] ['Sarah', 'Tracy'] >>> L[-2:] ['Bob', 'Jack'] >>> L[-2:-1] ['Bob']>>> L = list(range(100)) >>> L[:10:2] [0, 2, 4, 6, 8] >>> L[::5] [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95] - 迭代
dict迭代:>>> d = {'a': 1, 'b': 2, 'c': 3} >>> for key in d: ... >>> for value in d.values(): ... >>> for k, v in d.items(): ...- 对
list实现下标循环:>>> for i, value in enumerate(['A', 'B', 'C']): ...
- 列表生成式
- 生成list
>>> list(range(1, 11)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - 根据特定规则生成list
>>> [x * x for x in range(1, 11) if x % 2 == 0] [4, 16, 36, 64, 100] - 双层循环:
>>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] - 调用函数:
>>> L = ['Hello', 'World', 'IBM', 'Apple'] >>> [s.lower() for s in L] ['hello', 'world', 'ibm', 'apple'] - if...else:
在一个列表生成式中,for前面的if ... else是表达式,而for后面的if是过滤条件,不能带else>>> [x if x % 2 == 0 else -x for x in range(1, 11)] [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
- 生成list
- 生成器
若列表元素可以按照某种算法推算出来,可在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间- 创建
generator>>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630> >>> next(g) 0 >>> next(g) 1 ...
- 创建
- 通过循环遍历生成器
python >>> g = (x * x for x in range(10)) >>> for n in g: ... print(n) ... - 函数式创建
generator
变成generator的函数,在每次调用def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done' >>> f = fib(6) >>> f <generator object fib at 0x104feaaa0>next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行
函数式编程
- 高阶函数
- 变量可以指向函数,即函数本身可以赋值给变量
- 接受另一个函数作为参数的函数称为高阶函数
def add(x, y, f): return f(x) + f(y) map()函数和reduce()函数map()函数接受两个参数,一个是函数,另一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回:>>> def f(x): ... return x * x ... >>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> list(r) [1, 4, 9, 16, 25, 36, 49, 64, 81]reduce()函数把一个函数作用在一个序列上,且这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,效果为:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
filter()函数
filter()函数用于过滤序列,接收一个函数和一个序列,并将传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素,例如:def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15]sorted()函数- 直接对
list进行排序:>>> sorted([36, 5, -12, 9, -21]) [-21, -12, 5, 9, 36] - 通过
key函数实现自定义排序:>>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36] - 若要反向排序,可传入第三个参数
reverse=True
- 直接对
- 匿名函数
- 关键字
lambda表示匿名函数,冒号前面的x表示函数参数 - 只能有一个表达式,返回值即为表达式的结果
- 匿名函数也是一个函数对象,可赋值给一个变量,再利用变量来调用该函数
- 关键字
- 装饰器
- 在代码运行期间动态增加功能的方式称为"装饰器"(Decorator)
- 示例:
# 定义decorator def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper # 通过@,将decorator置于函数定义处 @log # 相当于执行了now = log(now) def now(): print('2015-3-25') >>> now() call now(): 2015-3-25
- 偏函数
- 根据需要将一个函数的某些参数固定(即设置默认值),返回新的函数
- 示例:
>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 # int2()函数base默认值为10
模块
- 使用模块
- Python模块的标准文件模板
#!/usr/bin/env python3 # -*- coding: utf-8 -*- ' a test module ' # 表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释 __author__ = 'Michael Liao' ... - 有关
if __name__=='__main__:
当我们在命令行运行模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该模块时,if判断将失败 - 作用域
- 正常的函数和变量名是公开的(public),可以被直接引用
- 类似
__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途 - 类似
_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用
- Python模块的标准文件模板
面向对象编程
- 类和实例
- 通过
class关键字定义类class Student(object): # object表示该类从object类继承而来,默认为object,支持多继承 pass >>> bart = Student() >>> bart.name = 'Bart Simpson' # 可自由地给一个实例绑定属性 >>> bart.name 'Bart Simpson' - "构造函数"
class Student(object): def __init__(self, name, score): self.name = name self.score = score # 这样再创建实例时就不能传入空的参数
- 通过
- 访问限制
- 为防止外部代码自由修改内部属性,可把属性改为私有变量:
class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score))
- 为防止外部代码自由修改内部属性,可把属性改为私有变量:
- 继承和多态
- 当子类和父类都存在相同的方法时,子类的方法覆盖了父类的方法
- 静态语言 vs 动态语言
- 对于静态语言(例如Java)来说,如果需要传入
Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用其中的run()方法 - 对于Python这样的动态语言来说,则不一定需要传入
Animal类型。我们只需要保证传入的对象有一个run()方法就可以了
- 对于静态语言(例如Java)来说,如果需要传入
- 获取对象信息
type()函数
判断对象类型isinstance()函数
判断一个对象是否是该类型本身或位于该类型的父继承链上dir()函数
获取一个对象的所有属性和方法,返回一个list
- 实例属性和类属性
- 相同的实例属性将屏蔽掉类属性
面向对象高级编程
- 使用
__slots__- 给实例绑定方法:
>>> def set_age(self, age): # 定义一个函数作为实例方法 ... self.age = age ... >>> from types import MethodType >>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法 - 给类绑定方法:
>>> def set_score(self, score): ... self.score = score ... >>> Student.set_score = set_score - 限制实例的属性:
注意:class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称 # 此时只能对创建的实例绑定name, age属性,绑定其他属性将报错__slots__定义的属性仅对当前类实例起作用,对继承的子类不起作用
- 给实例绑定方法:
- 使用
@property- Python内置的
@property装饰器就是负责把一个方法变成属性调用的
把一个getter方法变成属性,只需要加上class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value >>> s = Student() >>> s.score = 60 # OK,实际转化为s.set_score(60) >>> s.score # OK,实际转化为s.get_score() 60@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值
- Python内置的
- 使用枚举类
from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) for name, member in Month.__members__.items(): print(name, '=>', member, ',', member.value) # valule属性默认从1开始计数 - 使用元类
- 利用
type()动态创建类type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:>>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class >>> h = Hello() >>> h.hello() Hello, world.- 要创建一个class对象,type()函数依次传入3个参数:
- class的名称
- 继承的父类集合,如果只有一个父类,注意tuple的单元素写法
- class的方法名称与函数绑定
- 利用
IO编程
- 文件读写
- 读文件
with open('/path/to/file', 'r') as f: print(f.read()) # 常用函数 read() # 一次性读取文件的全部内容 read(size) # 每次最多读取size个字节的内容 readline() # 每次最多读取size个字节的内容 readlines() # 一次读取所有内容并按行返回list - 写文件
with open('/Users/michael/test.txt', 'w') as f: f.write('Hello, world!')
- 读文件
- 操作文件和目录
- 创建一个目录:
os.mkdir('filepath') - 删除一个目录:
os.rmdir('filepath') - 合并两个路径:
os.path.join('path1', 'path2') - 拆分路径(后一部分为最后级别的目录或文件名):
os.path.split()
- 创建一个目录:
正则表达式
- 直接给出字符就是精确匹配,
\d可以匹配一个数字,\w可以匹配一个字母或数字,\s可以匹配一个空格或Tab等空白符 .可以匹配任意字符*表示任意个字符,+表示至少一个字符,?表示0个或1个字符,{n}表示n个字符,{n, m}表示n-m个字符- 特殊字符需要用
\转义 - 精确匹配
[0-9a-zA-Z\_]可以匹配一个数字、字母或者下划线[0-9a-zA-Z\_]+可以匹配至少由一个数字、字母或者下划线组成的字符串[a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)A|B可以匹配A或B^表示行的开头,^\d表示必须以数字开头$表示行的结束,\d$表示必须以数字结束