文章目录
  1. 1. python中的模块导入
    1. 1.1. 包导入
    2. 1.2. 模块导入搜索路径
  2. 2. try-except-finally中语句执行顺序
  3. 3. 字典推导的限制

前事不忘,后事之师。 ——《战国策·赵策一》

python中的模块导入

包导入

这个比较简单,一个例子带过吧,但刚开始确实扑街了…

1
2
3
4
5
6
7
8
9
10
11
12
13
#文件结构:
packages
├── __init__.py
└── my_module.py
# 这种是包导入
import packages
# 后续是从__init__.py中定义的属性中获取的
packages.some_module.some_method
# 这种是把包当做搜索路径上的一环
from packages import some_module
some_module.some_method

模块导入搜索路径

例如下面的使用场景:

1
2
3
4
5
6
7
8
9
10
11
# django中有一个common app,并且已经添加到已安装app列表中
# common app文件结构
common
├── __init__.py
├── common.py
├── others.py
└── somethings.py
# somethings.py中存在导入语句
from common.others import some_func # 会报错

在上面的代码示例中,由于common的名称相同,以及搜索路径默认是先从当前执行代码所在目录开始搜索,所以 common 文件 会在common 包之前被搜索到,造成搜索路径上的覆盖。而一旦搜索到common时,就会去common文件中查找some_func, 但是无法找到,这样就会报错。

try-except-finally中语句执行顺序

在python的异常处理中,无论try语句中是否有异常抛出,finally语句总会被执行。由于这个特性,finally语句经常被用来做一些清理工作,例如关闭文件,数据库等等。

如果当try-except-finally中出现异常,并且未被妥善处理时,python会先把发生的异常暂存,当finally中的动作执行完毕之后,把保存的异常返回给上级。try-except-finally中的语句执行顺序,决定了它会有一些潜在的陷阱。

看以下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def finallyTest():
print 'start------'
while True:
try:
print "running------"
raise IndexError("r") #抛出异IndexError异常
except NameError as e:
print 'NameError happended {0}'.format(str(e))
break
finally:
print 'finally executed'
break #finally语句中有break语句
# 执行测试函数
FinallyTest()
#输出结果为:
start------
running------
finally executed

在上面的例子中,在try语句中,raise出了一个IndexError异常,而且except语句也没有捕获这个错误。按照平常的理解,这个错误会向上级(也就是调用这个函数的程序)传递,在本例中会传递到解释器,并引发一个IndexError错误。但是整个函数执行完之后并没有异常出现。

原因就在于在当try块中发生异常的时候,如果在except语句中找不到对应的异常处理,异常将会被「临时保存起来」。当finally执行完毕的时候,临时保存的异常将会「再次被抛出」,但如果finally语句中产生了新的异常或者执行了return或者break语句,那么临时保存的异常将会被丢失,从而导致「异常屏蔽」。

另外一个稍微复杂一些例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import logging
logger = logging.Logger(__name__)
def returnTest(a):
try:
if a == False:
raise ValueError("data can not be False")
else:
return a
except ValueError as e:
logger.exception("exception!, {detail}").fomat(detail)
return "return exception in except statement"
finally:
return "return in finally statement"
print returnTest(False)
print returnTest(True)

按照平常理解,第一个print语句会触发错误,然后进入except语句打印异常,而同时finally部分中的语句必然会执行,所以这里会返回一个’return in finally statement’值。第二个则会返回True。实际上则不然,返回值竟然都是’return in finally statement’。

原因是和上面的例子中是一样的,无论是try复合语句中,还是except复合语句中的return/break语句,这种会产生程序跳转的语句会先被保存起来。然后再去执行finally中的语句,而一旦finally中出现了跳转语句,就会直接跳转了,这样早成的结果就是上面保存状态的消失。

字典推导的限制

在做一个项目的时候,遇到一个需要多次请求数据库的操作,详细情况就是,有一些公司,这些公司本身有名字,公司代号这样的组织,如果每次都从数据库中取公司信息,会有性能问题。于是就采用用字典缓存下来,于是想使用字典推导。发现推导时只能生成单元素,之前我没有留意过这一点,在此记录一下。

1
2
3
4
5
6
7
8
9
10
# 这样是可以的
>>>{model.key: model.value for Modle in models }
# 结果就是个普通的字典
>>>{key1: value1, key2: value2, ...}
# 但是想要下面这样使用是不支持的
>>>{model.key1: model.value1, moedel.key2: model.value2 for model in models}
# 折中的办法是将上面的value值换成一个字典,或者是使用列表推导再封装一层列表
>>>{model.key1: {models.key1: models.value1, model.key2: model.value2, ...} for model in models}

补充一下实际的例子,其中branch相关的是分公司信息,subbranch相关的是中支公司信息,目的是构造一个,分公司名称为key值,分公司的主键,编号,以及该分公司辖下的中支公司为value值的字典,其中的中支公司也是一个字典:以中支公司名称为key值,中支公司的其他信息为value值。这样就构成了一个公司数据的二阶字典缓存列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
companys_by_name = {
branch.name: {
"pk": branch.pk,
"number": branch.number,
"subbranch": {
subbranch.name: {"pk":subbranch.pk, "number":subbranch.number} for subbranch in SubbranchCompany.objects.filter(branch_company=branch)
} for branch in BranchCompany.objects.all()
}
# 详细含义如下,其中中支公司是分工司辖下的公司
公司信息字典 = {
分公司名字:{
"主键": 当前迭代分公司主键,
"公司号": 当前迭代公司公司号,
"中支公司":{
中支公司名字:{
"主键": 当前迭代中支公司主键,
"中支公司号": 当前迭代中支公司号,
} for 中支公司 in 当期迭代分公司下中支公司集合
}
} for 分公司 in 分公司集合
}
文章目录
  1. 1. python中的模块导入
    1. 1.1. 包导入
    2. 1.2. 模块导入搜索路径
  2. 2. try-except-finally中语句执行顺序
  3. 3. 字典推导的限制