flask web 开发读书笔记 1 语法补遗

关于

本文是阅读《Flask Web Development: Developing Web Application with Python》这本书的读书笔记。

@property

我理解@property的主要作用是将调用getter和setter的操作简化成对属性赋值的操作。

举个例子

1
2
3
4
5
6
7
8
9
10
11
class Student(object):
def get_score(self):
return self._score
def set_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

当我们使用时

1
2
3
4
5
6
7
8
>>> s = Student()
>>> s.set_score(60) # ok!
>>> s.get_score()
60
>>> s.set_score(9999)
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!

利用@property的方式我们可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

当我们在使用时

1
2
3
4
5
6
7
8
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!

除了这种基本的用法(简化getter和setter的定义)以外,我们还可以构造一些
a. 只能读不能写的属性
b. 只能写不能读的属性

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2014 - self._birth
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, value):
self.password_hash = generate_password_hash(value)

上面的birth是可读写属性,而age就是一个只读属性,因为age可以根据birth和当前时间计算出来,password是只可写不可读的属性。

@classmethod 与 @staticmethod

@classmethod
我们要写一个只在类中运行而不在实例中运行的方法. 如果我们想让方法不在实例中运行

比如,对类的实例数量进行统计

1
2
3
4
5
6
7
8
9
10
11
class Kls(object):
no_inst = 0
def __init__(self):
Kls.no_inst = Kls.no_inst + 1
@classmethod
def get_no_of_instance(cls_obj):
return cls_obj.no_inst
ik1 = Kls()
ik2 = Kls()
print ik1.get_no_of_instance()
print Kls.get_no_of_instance()

@staticmethod
经常有一些跟类有关系的功能但在运行时又不需要实例和类参与的情况下需要用到静态方法。
或者说,不需要self参数的类/实例方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
IND = 'ON'
class Kls(object):
def __init__(self, data):
self.data = data
@staticmethod
def checkind():
return (IND == 'ON')
def do_reset(self):
if self.checkind():
print('Reset done for:', self.data)
def set_db(self):
if self.checkind():
self.db = 'New db connection'
print('DB connection made for: ', self.data)
ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()

装饰器级联/多重装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def first(func):
print '%s() was post to first()'%func.func_name
def _first(*args,**kw):
result = func(*args,**kw)
print '11111'
return result
return _first
def second(func):
print '%s() was post to second()'%func.func_name
def _second(*args,**kw):
print '22222'
return func(*args,**kw)
return _second
@first
@second
def test():
return 'hello world'

相当于,先装饰second的内容,再装饰first的内容。

1
2
3
temp1 = second(test)
temp2 = first(temp1)
temp2()

补充测试脚本

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
def first(func):
print '%s() was post to first()'%func.func_name
def _first(*args,**kw):
result = func(*args,**kw)
print '11111'
return result
return _first
def second(func):
print '%s() was post to second()'%func.func_name
def _second(*args,**kw):
print '22222'
return func(*args,**kw)
return _second
def test():
print 'hello world'
return 0
if __name__ == '__main__':
temp1 = second(test)
temp2 = first(temp1)
temp2()
temp1 = first(test)
temp2 = second(temp1)
temp2()

@wraps

Python装饰器(decorator)在实现的时候,有一些细节需要被注意。例如,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变)。这样有时候会对程序造成一些不便,例如笔者想对unittest框架中的一些函数添加自定义的decorator,添加后由于函数名和函数的doc发生了改变,对测试结果有一些影响。
所以,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding=utf-8 -*-
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Docstring"""
print('Called example function')
print(example.__name__, example.__doc__)
example()

可以尝试对比一下有无@wraps的区别