Python3高级编程第2版-语法类级别以下

学习总结:Python高级编程第2版-语法最佳实践-类级别以下

  • 字符串与字节
  • 集合类型
  • 迭代器
  • 生成器
  • 装饰器
  • 上下文管理器 - with语句
  • for … else …
  • 函数注解

字符串与字节

  • str: 字符串
  • bytes: 字节数组
  • bytearray: 可变字节数组
1
2
3
4
5
6
7
8
9
>>> print(bytes([100,121,101]))
b'dye'

>>> list(b'dye')
[100, 121, 101]

>>> m = b"你好"
File "<stdin>", line 1
SyntaxError: bytes can only contain ASCII literal characters.

Unicode字符串中包含无法用字节表示的”抽象”文本. 因此如果Unicode字符串没有被转换成二进制数据的话,是无法保存在磁盘中或通过网络发送的

将字符串对象编码为字节序列的方式:

  • str.encode(encoding, errors)
  • bytes(source, encoding, errors)

将二进制数据转化为字符串的方式:

  • bytes.decode(encoding, errors)
  • str(source, encoding, error)
1
2
3
4
5
6
7
8
9
>>> "你好".encode()
b'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> t = "你好".encode()
>>> t
b'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> t.decode()
'你好'
>>> print(type(t))
<class 'bytes'>

数据结构进阶

列表

下面的用法几乎适用于任何可迭代对象

列表推导式

1
2
>>> [i for i in range(10) if i % 2 == 0]
[0, 2, 4, 6, 8]

enumerate(枚举)

1
2
3
4
5
6
>>> for i, ele in enumerate([1,2,3]):
... print(i, ele)
...
0 1
1 2
2 3

zip 压包解包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> for item in zip([1,2,3], [4,5]):
... print(item)
...
(1, 4)
(2, 5)

>>> for item in zip(*zip([1,2,3], [4,5])):
... print(item)
...
(1, 2)
(4, 5)

>>> a,b,*c = 1,2,3,4
>>> a,b,c
(1, 2, [3, 4])

字典

字典推导

1
2
>>> {i:i*2 for i in (1,2,3,4) if i % 2 == 0}
{2: 4, 4: 8}

keys() values() items()

三者返回的是视图对象

集合

集合推导

1
2
>>> {i for i in (1,2,3,2)}
{1, 2, 3}

set()和frozenset()

frozenset() 返回一个冻结的集合,冻结后集合不能再添加或删除任何元素。

collections库

  • Counter:字典的子类,提供了可哈希对象的计数功能
  • defaultdict:字典的子类,提供了一个工厂函数,为字典查询提供了默认值
  • OrderedDict:字典的子类,保留了他们被添加的顺序
  • namedtuple:创建命名元组子类的工厂函数
  • deque:类似列表容器,实现了在两端快速添加(append)和弹出(pop)
  • ChainMap:类似字典的容器类,将多个映射集合到一个视图里面

迭代器

迭代器只不过是实现迭代器协议的容器对象。它基于以下两个方法:

  • __next__:返回容器下一个元素
  • __iter__:返回迭代器本身

使用iter函数

迭代器可以利用内置iter函数和一个序列来构建,如下例:

1
2
3
4
5
6
7
8
9
10
11
>>> obj = iter([1,2,3])
>>> next(obj)
1
>>> next(obj)
2
>>> next(obj)
3
>>> next(obj)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

当遍历结束后,可以得到StopIteration异常,捕获到这种异常可停止循环。

1
2
3
4
5
6
7
>>> obj = iter([1,2,3])
>>> for i in obj:
... print(i)
...
1
2
3

自定义迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyIter(object):
def __init__(self, step):
self.step = step

def __next__(self):
"""return the next element"""
if self.step <= 0:
raise StopIteration
self.step -= 1
return self.step+1

def __iter__(self):
"""return the iterator self"""
return self
1
2
3
4
5
6
7
>>> for ele in MyIter(4):
... print(ele)
...
4
3
2
1

生成器

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

斐波那契数列简单生成器

1
2
3
4
5
def fibonacci():
a, b = 0, 1
while True:
yield b
a, b = b, a + b
1
2
3
4
5
6
7
8
9
10
11
>>> fib = fibonacci()
>>> next(fib)
1
>>> next(fib)
1
>>> next(fib)
2
>>> next(fib)
3
>>> type(fib)
<class 'generator'>

该函数返回一个generator对象,该对象是一个特殊的迭代器,它知道如何保存上下文。

上述斐波那契数列迭代器对象可以被无限次调用,就像是数列一样。

send函数

send函数的作用和next类似,但会将send函数内部传入的值变为yield的返回值。

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
>>> def MyGenerator():
... value = yield "Hello"
... yield value
...
>>> gen = MyGenerator()
>>> next(gen)
'Hello'
>>> next(gen)
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

>>> gen = MyGenerator()
>>> next(gen)
'Hello'
>>> gen.send("Hi")
'Hi'

>>> gen = MyGenerator()
>>> gen.send("Hi")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator

>>> gen = MyGenerator()
>>> gen.send(None)
'Hello'
>>> gen.send("Hi")
'Hi'

send函数可以根据客户端代码来改变自身行为,为了完成这一行为,还添加了两个函数:

  • throw:允许客户端发送要抛出的各类异常
  • close:引发特定异常,GeneratorExit

类成员函数作为生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> class A:
... def fibonacci(self):
... a, b = 0, 1
... while True:
... yield b
... a, b = b, a + b
...
>>> aa = A()
>>> for i, obj in enumerate(aa.fibonacci()):
... print(i, obj)
... if i > 5:
... break
...
0 1
1 1
2 2
3 3
4 5
5 8
6 13

装饰器

装饰器是一个可调用对象,即函数或者实现了__call__方法的类。

@staticmethod和@classmethod

  • @staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
  • @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
  • @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 简化前写法
class A:
def static_method():
print("staticmethod")
static_method = staticmethod(static_method)

def class_method(cls):
cls.static_method()
print("classmethod")
class_method = classmethod(class_method)

# 简化后写法
class A:
@staticmethod
def static_method():
print("staticmethod")

@classmethod
def class_method(cls):
cls.static_method()
print("classmethod")

无参装饰器

作为一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 使用装饰器之前
def say_sth(words="Hello"):
print(words)

def decorator(func):
def wrapper(*args, **kwargs):
print("%s 调用" % func.__name__)
return func(*args, **kwargs)
return wrapper

say_sth = decorator(say_sth)
say_sth("Hi~")

# 使用装饰器之后
def decorator(func):
def wrapper(*args, **kwargs):
print("%s 调用" % func.__name__)
return func(*args, **kwargs)
return wrapper

@decorator
def say_sth(words="Hello"):
print(words)
say_sth("Hi~")

作为一个类

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
# 使用装饰器之前
class DecoratorClass:
def __init__(self, function):
self.function = function

def __call__(self, *args, **kwargs):
print("%s 调用" % self.function.__name__)
return self.function(*args, **kwargs)

def say_sth(words="Hello"):
print(words)

say_sth = DecoratorClass(say_sth)
say_sth("Hi~")

# 使用装饰器之后
class DecoratorClass:
def __init__(self, function):
self.function = function

def __call__(self, *args, **kwargs):
print("%s 调用" % self.function.__name__)
return self.function(*args, **kwargs)

@DecoratorClass
def say_sth(words="Hello"):
print(words)

say_sth("Hi~")

带参装饰器

作为一个函数

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
# 使用装饰器之前
def say_sth(words="Hello"):
print(words)

def decorator(say=True):
def inner_wrapper(func):
def wrapper(*args, **kwargs):
if say:
print("%s 调用" % func.__name__)
return func(*args, **kwargs)
return wrapper
return inner_wrapper

say_sth = decorator(say=False)(say_sth)
say_sth("Hi~")

# 使用装饰器之后
def decorator(say=True):
def inner_wrapper(func):
def wrapper(*args, **kwargs):
if say:
print("%s 调用" % func.__name__)
return func(*args, **kwargs)
return wrapper
return inner_wrapper

@decorator(say=False)
def say_sth(words="Hello"):
print(words)

say_sth("Hi~")

作为一个类

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
34
35
# 使用装饰器之前
class DecoratorClass:
def __init__(self, say=True):
self.say = say

def __call__(self, func):
def wrapper(*args, **kwargs):
if self.say:
print("%s 调用" % func.__name__)
return func(*args, **kwargs)
return wrapper

def say_sth(words="Hello"):
print(words)

say_sth = DecoratorClass(say=False)(say_sth)
say_sth("Hi~")

# 使用装饰器之后
class DecoratorClass:
def __init__(self, say=True):
self.say = say

def __call__(self, func):
def wrapper(*args, **kwargs):
if self.say:
print("%s 调用" % func.__name__)
return func(*args, **kwargs)
return wrapper

@DecoratorClass(say=False)
def say_sth(words="Hello"):
print(words)

say_sth("Hi~")

保存函数元数据

上述示例均未保存函数的元数据(主要是文档字符串和原始函数名)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> class DecoratorClass:
... def __init__(self, say=True):
... self.say = say
...
... def __call__(self, func):
... def wrapper(*args, **kwargs):
... """wrapper doc"""
... if self.say:
... print("%s 调用" % func.__name__)
... return func(*args, **kwargs)
... return wrapper
...
>>> @DecoratorClass(say=True)
... def say_sth(words="Hello"):
... """say_sth doc"""
... print(words)
...
>>> say_sth.__name__
'wrapper'
>>> say_sth.__doc__
'wrapper doc'

为了解决这个问题,我们需要使用functools中的wraps()装饰器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> from functools import wraps
>>> class DecoratorClass:
... def __init__(self, say=True):
... self.say = say
...
... def __call__(self, func):
... @wraps(func)
... def wrapper(*args, **kwargs):
... """wrapper doc"""
... if self.say:
... print("%s 调用" % func.__name__)
... return func(*args, **kwargs)
... return wrapper
...
>>> @DecoratorClass(say=True)
... def say_sth(words="Hello"):
... """say_sth doc"""
... print(words)
...
>>> say_sth.__name__
'say_sth'
>>> say_sth.__doc__
'say_sth doc'

上下文管理器 - with 语句

  • 关闭一个文件
  • 释放一个锁
  • 创建一个临时的代码布丁
  • 在特殊环境运行中受保护的代码

作为一个类

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

  • __enter__(self): 任何返回值都会绑定到指定的as子句
  • __exit__(self, exc_type, exc_value, traceback): 退出时需要执行的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class FileOpen:
def __init__(self,filename, mode, encoding='utf-8'):
self.filename = filename
self.mode = mode
self.encoding=encoding

def __enter__(self):
self.fp = open(self.filename, self.mode, encoding=self.encoding)
return self.fp

def __exit__(self, exc_type, exc_value, traceback):
self.fp.close()

with FileOpen("test.txt", 'r', encoding='utf-8') as f:
print(f.readline())

作为一个函数

使用contextlib模块,yield分割语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import contextlib

@contextlib.contextmanager
def FileOpen(file_name, mode, encoding='utf-8'):
file_handler = open(file_name, mode, encoding='utf-8')
try:
yield file_handler
except Exception as exc:
print('the exception was thrown')
finally:
file_handler.close()

with FileOpen("test1.txt", 'r', encoding='utf-8') as f:
print(f.readline())

for … else … 语句

else子句在for循环语句中正常循环结束才会执行,如果由于break语句退出循环,则else子句不会被执行。

1
2
3
4
5
6
>>> for i in range(10):
... pass
... else:
... print("no break")
...
no break

函数注解

可以指定参数类型和返回值类型,但是实际调用时,并不会强制要求参数与注解类型一致。

1
2
3
4
5
6
7
>>> def test(a: str, b: int = 2) -> str:
... return a + str(b)
...
>>> test.__annotations__
{'a': <class 'str'>, 'b': <class 'int'>, 'return': <class 'str'>}
>>> test("11", "33")
'1133'