Python笔记 -- 整理自廖雪峰教程
编码问题
-
Python3中的字符串在
内存中是以Unicode编码的ord()获取字符串的编码整数表示chr()把编码转化为对应的字符bytes类型的数据用前缀b表示 b'ABC'- 可以用
encode()指定编码类型 'ABC'.encode('ascii') - 可以用
decode()对字节流数据解码 b'ABC'.decode('ascii') - 选择errors='ignore'可以无视decode时错误的字节
-
同C语言, 使用%可以格式化字符串 'hello %s' % 'world'
- 输出%需要转译 %%
format()也有类似的作用- 还有一种以f-string格式
- f'pi eq {r:.4f}' {}内变量会被替换输出
列表
-
list
- 越界访问会抛出IndexError错误
- 追加到末尾
list.append() - 指定插入位置
list.insert(idx, data) - 删除末尾元素
list.pop() / list.pop(idx) - 替换元素
list[idx] = data
-
tuple 元组
- tuple一旦初始化后便不能修改, 使用()初始化
- ('a', 'b', ['X']) 中, 列表中元素是可变的, tuple的不变是指其指向元素不变
-
input()得到的结果的str类型的
非顺序结构
-
dict
- 初始化格式 { key:value }
- 判断key是否存在
key in dict: 返回True/Falsedict.get(key): 不存在返回None
- 删除
dict.pop(key) - dict中的key必须是不可变对象
- 底层原理为hash表
-
set
- 初始化 s = set()
- 插入元素
set.add() - 删除元素
set.remove() - 求交集 set1 & set2
- 求并集 set1 | set2
- 底层原理也是hash表
-
对不可变对象的操作
a = 'abc'
a.replace('a', 'A') # 'Abc'
a # 'abc'
- 对于不变对象来说, 调用对象自身的任意方法, 也不会改变该对象自身的内容, 而是创建一个新的对象并返回
函数
-
函数名其实就指向一个函数对象的引用, 把函数名赋予一个变量, 等价于给该函数起了一个别名
-
pass占位符 -
参数类型检查
isinstance(x, (int, float)) # 检查x是否是int或者float类型
-
函数的多返回值实际是一种假象, 实际上是返回了一个tuple
-
支持默认参数, 可变参数, 关键字参数; 还调用支持不按顺序提供参数, 但是要指明参数名
def add_end(L=[]):
L.append('End')
return L
add_end() # ['End']
add_end() # ['End', 'End']
因为默认参数也是一个变量, 每次调用时, 如果改变了默认参数, 则会影响下一次调用
- 可变参数格式, 实际传入的是一个tuple
def calc(*args):
return sum([i*i for i in args])
calc(1,2,3,4) # 30
- 关键字参数: 传入关键字参数时会在函数内部自动组装为一个dict
def person(name, **kw):
print('name:', name, 'other:', kw)
person('XiaoMing', age = 12)
若需要限制关键字参数的名字, 可使用命名关键字参数(可为默认参数)
def person(name, *, age):
print('name:', name, 'age:', age)
person('XiaoMing') # error
person('XiaoMing', 12) # error
person('XiaoMing', age = 12) # ok
-
混合使用时, 参数的定义顺序必须是: 必选参数, 默认参数, 可变参数, 命名关键字参数和关键参数
-
特别地, 任意函数都可以通过类似
func(*args, **kw)调用
args = (1,2,3,4)
kw = {'a':12, 'b':[1,2]}
f1(*args, **kw)
高级特性
-
切片
- [a:b] 取[a, b)的内容
- 字符串也可以切片
-
无论有无下标, 都可以迭代
d = {'a':1, 'b':2}
for key in d : print(key)
for k, v in d.items() : print(k ," ", v)
注意字典的迭代顺序不一定按照定义的顺序, 默认迭代参数为key
- 通过索引迭代
List = ['a', 'b']
for i, v in enumerate(List):
print(i, " ", v)
- 列表生成式
[x*x for x in range(1,8) if x % 2] # [1, 9, 25, 49]
[n+m for n in 'AB' for m in 'CD'] # ['AC', 'AD', 'BC', 'BD']
for前方必须是一个表达式, 故前方的if必须接else; 而for后方的if不能接else, 因为它表示for循环的过滤条件
- 生成器(generator): 循环过程中不断推算出后续元素而不生成完整列表
(x*x for x in range(10))
其保存的是算法, 一个典型案例是斐波拉契数列
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 这样, fib就是一个generator function了
a, b = b, a + b
n = n + 1
return 'done'
for n in fib(10):
print(n)
生成器可以通过next()函数不断获得下一个返回值, 当已经取得最后一个返回值时, 若再次调用next()函数, 会抛出StopIteration错误
- 凡是可作用于
next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列; - 凡是可作用于for循环的对象都是
Iterable类型; - 集合数据类型如list、dict、str等是
Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
函数式编程
-
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数
-
Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
高阶函数
-
函数也是一个变量, 可以赋值
-
map()和reduce(), 其中reduce在包functools中map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。`
list(map(str,[1,2,3])) # ['1', '2', '3']
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
from functools import reduce
reduce(lambda a,b: a+b, [1,2,3,4]) # 10
- 两者结合
def normalize(names):
return reduce(
lambda n1,n2: n1+n2, map(
lambda name: [name[0].upper()+name[1:]]
if len(name)>0 else [], names))
normalize(['asv','vas','aqwe',''])
filter()与map()类似, 但是其根据传入函数的返回值决定是否保留该元素, 常用于过滤序列
def not_empty(s):
return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', ' '])) # ['A', 'B', 'C']
def primes():
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
def _not_divisible(n):
return lambda x: x % n > 0
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一个数
yield n
it = filter(_not_divisible(n), it) # 构造新序列
for n in primes():
if n < 100:
print(n)
else:
break
-
sorted()排序-
返回排序后列表, 原参数列表不变
-
其也是个高阶函数, 接受一个
key函数实现自定义排序
-
sorted([36, 5, -12, 9, -21], key=abs)
# [5, 9, -12, -21, 36]
-
默认为升序, 设置参数reverse=True, 实现降序排序
-
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回
-
关于闭包(Closure)
-
内部函数可以引用外部函数的参数和局部变量,当外部函数将内部函数返回时,相关参数和变量都保存在返回的函数中
-
每次调用外部函数都会返回一个新的函数
-
返回的函数不会立即执行, 而是等到调用了再执行, 期间保存在其内部参数是可能发生变化的; 因此返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
-
若真的有返回内部参数是循环变量函数的需求, 可在内部函数中再定义一个函数
-
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i))
return fs
- nonlocal: 声明变量为外部变量
匿名函数
- lambda表达式
- 只能有一个表达式, 返回值就是该表达式的结果
装饰器
-
由于函数也是一个对象, 故也可以有属性, 比如
__name__ -
在代码运行期间动态增加功能的方式,称之为
装饰器(decorator)
import functools
def log(func):
@functools.wraps(func) # 防止传入的func签名变为wrapper
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
- decorator本身需要传入参数, 则可定义为
import functools
def log(text):
def decorator(func):
@functools.wraps(func) # 防止传入的func签名变为wrapper
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
偏函数
- 类似C++中的bind
import functools
int2 = functools.partial(int, base=2)
int2('1000000')
面向对象编程
-
一个.py文件称为一个
模块(Module) -
一个文件夹是一个
包(package), 每个包必须有一个__init__.py文件- 该文件可以为空, 其本身就是一个模块, 模块名为对应包名
-
面向对象最重要的概念就是
类(Class)和实例(Instance) -
定义类格式:
class Student(object):
pass
? 表示继承自某一个类, 如果无合适类, 默认继承object类
-
类的初始化方法为
__init__(self, *args) -
在Python中,实例的变量名如果以
__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问 -
在Python中,变量名类似
__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量- 双下划线开头的实例变量不一定不能从外部访问, 不能直接访问
__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量
- 双下划线开头的实例变量不一定不能从外部访问, 不能直接访问
-
type()对象类型判断,isinstance()对象class类型判断 -
dir()获取对象所有属性与方法 -
__str__(self)设置print时显示的字符串,__repr__(self)直接调用时显示的字符串, 前者一般是用户看到的, 后者是开发者看到的 -
__iter__(self)返回一个迭代对象, for循环可以不断通过__next__(self)方法获取循环的下一个值, 直到遇到StopIteration错误 -
__getitem__(self, n)设定下标访问操作, 注意n有可能是一个切片对象slice(即0:3之类的)
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片, 这里未处理负数情况
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
__getattr__(self, attr)找不到某属性时调用该方法
错误处理
try...except...finally处理错误
try:
r = 10 / int('2')
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
-
Python所有的错误都是从BaseException类派生的
-
错误栈: 如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获
-
记录错误但程序继续执行: 内置的
logging模块
import logging
try:
bar('0')
except Exception as e:
logging.exception(e)
- 使用
raise抛出错误
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise # 捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。
# 只有在必要的时候才定义我们自己的错误类型。如果可以选择Python已有的内置的错误类型(比如ValueError,TypeError),尽量使用Python内置的错误类型
## 调试
- 使用`断言(assert)`, 断言失败,assert语句本身会抛出AssertionError
```python
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
-
启动Python解释器时可以用-O参数来关闭assert,
python -O err.py -
使用
logging模块处理错误
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
? 允许指定记录信息的级别,有debug,info,warning,error等几个级别
序列化
- 使用
pickle模块实现序列化
import pickle
d = dict(name='Bob', age=20, score=88)
pickle.dumps(d)
- 写入和读取序列化文件
f = open('dump.txt', 'wb')
d = dict(name='Bob', age=20, score=88)
pickle.dump(d, f)
f.close()
f = open('dump.txt', 'rb')
d = pickle.load(f)
f.close()
d
-
更通用的序列化 JSON, XMK ...
-
Python中
json模块提供的完善的转化
import json
d = dict(name='Bob', age=20, score=88)
json.dumps(d)
json_str = '{"age": 20, "score": 88, "name": "Bob"}'
json.loads(json_str)
- 对Class实现序列化 json的dumps方法还提供了许多可选操作
json.dumps(s, default=lambda obj: obj.__dict__) # 偷懒写法
# 标准的写法是自定义转化函数
json.dumps(s, default=student2dict)
json.loads(json_str, object_hook=dict2student)