字符串与字节

python 3 中只有一种能保存文本信息的数据类型——str,它是不可变序列,保存的是Unicode code point。

python 2中用str表示字节字符串。这种类型在python 3中用bytes对象来处理。

bytes以及可变的bytearray与str不同,只能用字节值作为序列值。即0<=x<=255。

将字符串对象编码为字节序列的方法有两种:

a. 利用str.encode(encoding, errors)方法,利用registered codec对字符串进行编码。

b. 利用bytes(source, encoding, errors)构造函数,创造一个新的字节序列。

类似的反向转换:

bytes.decode(encoding, errors)&str(source, encoding, errors)

因为python字符串与字节序列是不可变的,故每次合并字符串(“+”)都需要创建新的字符串实例,效率低下。

替代 “”.join(substrs)

性能提升视实际情况而定,一般而言对于大型列表来说采用join方法性能提升显著。

集合类型

Tuple:

Tuple是不可变的(immutable),因此也是可哈希的(hashable)。

List:

List在CPython中实现为长度可变的数组,并不是“链表”。在需要真正使用链表的地方可以使用collections中的deque,它是栈和队列的一般化。

列表推导(list comprehension)要比用循环更快速。

enumerate()的使用,同时返回index和item。

zip()列表合并,对两个等大小的可迭代对象同时进行均匀遍历。对其返回值再次使用zip会恢复原状。

序列解包(sequence unpacking),等号两侧对应赋值,*解包,嵌套解包(tuple),详见python cookbook。

Dictionary:

字典推导的使用:

1
squares = {number: numbers**2 for number in range(100)}

python 3中,keys(), values(), items()中返回的是视图对象(可以动态改变,与获取时间无关)。

另外,keys()和values()调用后的值是完全对应的,实时动态调整。

如果一个对象在整个生命周期都有不变的散列值,而且这个对象可以与其它对象进行比较,那么这个对象就是可哈希的。python中所有不可变的内置类型都是可以哈希的。可变类型(list, dic, set等)是不可哈希的,因此不能做为字典的键。CPython使用开放定址法(open addressing)来解决散列冲突问题。

如果一个字典曾经有很多元素,后又大大减少,那么遍历这个字典可能要花费相当长时间(guess:标记删除)。因此,某些情况下,最好创建一个新的字典对象。

字典中的key的顺序是无序的,与散列方法和添加顺序无关。解决方法可以采用collections中的OrderedDict(),用法类似。

Set:

元素唯一性,测试元素是否在集合中效率高。其内部实现与dic类似,实现为带有空值的字典,上古时期由dic实现,后废弃。

set(): 一种可变的,无序的,有限的集合,其元素是唯一的,不可变的(可哈希)的对象。

frozenset(): 一种不可变的,可哈希的,无序的结果,其元素是唯一的,不可变的(可哈希)的对象。可用作dic,set的键。

创建方法:

  1. 接受可迭代对象set([1, 2, 3])
  2. 集合推导{elem for elem in range(3)}
  3. 集合字面值{1, 2, 3},空集合无字面值,{}为空dic字面值。

collection model:

nametuple, deque, ChainMap, Counter, OrderedDict, defaultdict, etc…

iterator:

迭代器是一个实现了迭代器协议的容器对象,基于以下两个方法:

_next_

_iter_

迭代器是个底层概念,可以不用,为生成器提供基础。

generator:

基于yield语句,生成器可以暂停函数并返回一个中间结果。该函数会保存上下文,稍后恢复。

常见使用场景数据流生成(机器学习数据生成)

最好编写多个处理序列值的简单可迭代函数,将生成器链接起来使用,而不要编写一个复杂函数同时计算出整个集合的结果。保持代码简单而不是保持数据简单。

send向yield传值。

decorator:

使函数包装与方法包装(一个函数接受函数并返回其增强版本),变得更加容易阅读和理解。

任何可调用对象(任何实现了__call__方法的对象都是可调用的)都可以用做装饰器。

装饰器语法只是语法糖而已,同显式装饰器效果相同。

最初使用场景是在方法定义的开头能够将其定义为类方法或静态方法:

@staticmethod与@classmethod的区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A(object):
def m1(self, n):
print("self", self)

@classmethod
def m2(cls, n):
print("cls", cls)

@staticmethod
def m3(n):
pass

a = A()
a.m1(1)
A.m2(1)
A.m3(1)

m1是实例方法,第一个参数必须是self(约定)。

m2是类方法,第一个参数必须是cls(约定)。

m3是静态方法,参数可有可无,看需要。

执行过程:

  • 第一步:代码从第一行开始执行 class 命令,此时会创建一个类 A 对象(没错,类也是对象,一切皆对象嘛)同时初始化类里面的属性和方法,记住,此刻实例对象还没创建出来。
  • 第二、三步:接着执行 a=A(),系统自动调用类的构造器,构造出实例对象 a
  • 第四步:接着调用 a.m1(1) ,m1 是实例方法,内部会自动把实例对象传递给 self 参数进行绑定,也就是说, self 和 a 指向的都是同一个实例对象。
  • 第五步:调用A.m2(1)时,python内部隐式地把类对象传递给 cls 参数,cls 和 A 都指向类对象。

对于实例方法m1(类对象需要绑定实例对象,否则报参数错误)

A.m1(a, 1) 等价于 a.m1(1)

对于类方法m2,不管是 A.m2 还是 a.m2,都是已经自动绑定了类对象A的方法,对于后者,因为python可以通过实例对象a找到它所属的类是A,找到A之后自动绑定到 cls。

A.m2(1) 等价于 a.m2(1)

对于静态方法m3,跟普通函数没区别,与类和实例都没有绑定关系。

A.m3(1) 等价于 a.m3(1)

  1. 作为一个函数

1
2
3
4
5
6
7
8
9
def mydecorator(function):
def wrapped(*args, **kwargs):
#do someting, before call the function
result = function(*args, **kwargs)
#do something, after call the function
#return the result
return result
#返回wrapped作为装饰函数
return wrapped
  1. 作为一个类

1
2
3
4
5
6
7
8
9
10
11
#非参数化装饰器用作类的通用模式
class DecoratorAsClass:
def __init__(self, function):
self.function = function

def __call__(self, *args, **kwargs):
#do something before call the function
result = self.function(*args, **kwargs)
#do something after call the function
#return result
return result
  1. 参数化装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#参数化装饰器需要用两层包装
#调用时不管有没有参数都要加括号
def repeat(number=3):
"""
多次重复执行装饰函数
返回最后一次原始函数调用的值作为结果
:param number: 重复次数,默认值为3
"""
def actural_decorator(function):
def wrapper(*args, **kwargs):
result = None
for _ in range(number):
result = function(*args, **kwargs)
return result
return wrapper
return actural_decorator
  1. 保存内省的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#函数元数据被覆盖了
def dummy_decorator(function):
def wrapped(*args, **kwargs):
"""包装函数内部文档"""
return function(*args, **kwargs)
return wrapped
@dummy_decorator
def function_with_important_docstring():
"""这是我们想要保存的重要文档字符串"""

>>>function_with_important_docstring.__name__
"wrapped"
>>>function_with_important_decstring.__doc__
"包装函数内部文档"

解决方法:
from functools import wraps

def preserving_decorator(function):
@wraps(function)
def wrapped(*args, **kwargs):
"""包装函数内部文档"""
return function(*args, **kwargs)
return wrapped

@preserving_decorator
def function_with_important_docstring()
"""这是我们想要保存的重要文档字符串"""

>>>function_with_important_docstring.__name__
"function_with_important_docstring"
>>>function_with_important_docstring.__doc__
"""这是我们想要保存的重要文档字符串"""

上下文管理器——with语句

一般语法:

1
2
3
4
5
6
7
8
9
10
11
with context_manager:
#代码块
#使用as保存为局部变量
with context_manager as context:
#代码块
with A() as a, B() as b:
...
等价于嵌套使用:
with A() as a:
with B() as b:
...
  1. 作为一个类

任何实现了上下文管理协议(context manager protocol)的对象都可以用作上下文管理器,该协议包含两个特殊的方法:

_enter_(self)

_exit_(self, exc_type, exc_value, traceback)

with调用过程:

  1. 调用__enter__方法。任何返回值都会绑定到指定的as语句
  2. 执行内部代码块
  3. 调用__exit__方法

__exit__接受代码块中出现错误时填入的3个参数。如果没有错误,参数都设置为None。出现错误时,__exit__不应该重新引发这个错误,因为这事调用者(caller)的责任。但它可以通过返回True来避免引发异常。类似于finally子句的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ContextIllustration:
def __enter__(self):
print("entering context")
def __exit__(self, exc_type exc_value, traceback):
print("leaving context")

if exc_type is None:
print("with no error")
else:
print("with an error (%s)" % exc_value)

>>>with ContextIllustration():
print("inside")

entering context
inside
leaving context
with no error
  1. 作为一个函数——contextlib模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from contextlib import contextmanager

@contextmanager
def context_illustration():
print("entering context")
try:
yield
except Exception as e:
print("leaving context")
print("with an error (%s)" % e)
#需要再次抛出异常
raise
else:
print("leaving context")
print("with no error")

for…else…语句

1
2
3
4
5
6
7
8
9
10
for i in range(1):
break
else:
print("nobreak")
>>> (直接跳出循环没有输出)
for i in range(1):
pass
else:
print("break")
>>>break

函数注解

函数注解时关于用户自定义函数的类型的完全可选的元信息。//leetcode 有相关使用

1
2
3
4
5
>>>def f(ham: str, eggs: str = "eggs") -> str:
pass

>>>print(f.__annotations__)
{"return":<class "str">, "eggs":<class "str">, "ham":<class "str">}

参数注解为冒号后计算注解值的表达式。返回值注解为函数名结尾冒号与参数列表之后的->之间的表达式。