Python之旅:三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数、异常处理

三元表达式

1
2
name = 'allen'
print('验证失败') if name!='allen' else print('验证成功')

列表推导式

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
## 优点:方便,改变了编程习惯,可称之为声明式编程
## 示例一:
test_list=[]
for i in range(10):
test_list.append('test_%s' %i)

## 用三元表达式
test_list=['test_%s' %i for i in range(10)]
print(test_list)

## 示例二:
res=['test_{}'.format(item+item1) for item in range(10) if item==3
for item1 in range(66) if item1==10
]
## ['test_13']

## 复杂一般用不着,可读性差
res=[itemN for item1 in iterable1 if item1
for item2 in iterable2 if item2
...
for itemN in iterableN if itemN
]
## 类似于
res=[]
for item1 in iterable1:
if item1:
for item2 in iterable2:
if item2
...
for itemN in iterableN:
if itemN:
res.append(itemN)

生成器表达式

1
2
3
4
5
6
7
8
9
10
11
12
##1、把列表推导式的[]换成()就是生成器表达式

##2、示例:生一筐鸡蛋变成给你一只老母鸡,用的时候就下蛋,这也是生成器的特性
>>> chicken=('鸡蛋%s' %i for i in range(5))
>>> chicken
<generator object <genexpr> at 0x10143f200>
>>> next(chicken)
'鸡蛋0'
>>> list(chicken) #因chicken可迭代,因而可以转成列表
['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4',]

##3、优点:省内存,一次只产生一个值在内存中

递归

递归调用的定义

递归函数 recursion:函数直接或间接的调用自身,是python算法中比较核心的概念。

递归函数必须具备以下3个特点:

1.直接或者间接调用自身

2.具有结束条件,防止递归外溢

3.代码规模逐渐减少

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 f1():
print('from f1')
f1()
f1()

##间接调用本身
def f1():
print('from f1')
f2()

def f2():
print('from f2')
f1()
f1()

## 调用函数会产生局部的名称空间,占用内存,因为上述这种调用会无需调用本身,python解释器的内存管理机制为了防止其无限制占用内存,对函数的递归调用做了最大的层级限制
## 四 可以修改递归最大深度

import sys
sys.getrecursionlimit()
sys.setrecursionlimit(2000)

def f1(n):
print('from f1',n)
f1(n+1)
f1(1)

## 虽然可以设置,但是因为不是尾递归,仍然要保存栈,内存大小一定,不可能无限递归,而且无限制地递归调用本身是毫无意义的,递归应该分为两个明确的阶段,回溯与递推

递推与回溯

递归的递推:递归每一次都是基于上一次进行下一次的执行。

递归的回溯:当遇到终止条件,则从最后往回一级一级的把值返回来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 递归递推
def age(n):
if n == 1:
return 18
return age(n –1) + 2
print(age(5))

##age(5) = age(4) +2 第一次进入
##age(4) = age(3) +2 第二次进入
##age(3) = age(2) +2 第三次进入
##age(2) = age(1) +2 第四次进入
##age(1) = 18 第五次进入,此时达到结束的条件,递归终止

这是一个典型的递归递推算法,它的每一次执行都是基于上一次的结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def fx(n):
print("递归进入第",n,"层")
if n == 3:
return n
fx(n + 1)
print("递归退出第",n,"层")
fx(1)
print("程序结束")

运行结果:
递归进入第 1
递归进入第 2
递归进入第 3
递归退出第 2
递归退出第 1
程序结束

这是一个典型的递归回溯算法,满足终止条件时,一级一级的把值返回来。

总结:

递归必须要有一个明确的结束条件, 否则就变成死循环导致栈溢出,以上两例都是以“if”语句作为结束条件的。每递归一次要解决一些事情,否则就失去了递归的意义。

python中的递归效率低且没有尾递归优化

python中的递归效率低,需要在进入下一次递归时保留当前的状态,在其他语言中可以有解决方法:尾递归优化,即在函数的最后一步(而非最后一行)调用自己。
但是python又没有尾递归,且对递归层级做了限制

递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

二分法

想从一个按照从小到大排列的数字列表中找到指定的数字,遍历的效率太低,用二分法(算法的一种,算法是解决问题的方法)可以极大低缩小问题规模

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
## 实现类似于in的效果
l=[1,2,10,30,33,99,101,200,301,311,402,403,500,900,1000] #从小到大排列的数字列表

def search(n,l):
print(l)
if len(l) == 0:
print('not exists')
return
mid_index=len(l) // 2
# [1,2,10,30,33,99,101,200,301,311,402,403,500,900,1000] // 2
# [1,2,10,30,33,99,101] // 2
if n > l[mid_index]: # n>l[7]==3>200
#in the right
l=l[mid_index+1:] # l=l[8] l=[301,311,402,403,500,900,1000]
search(n,l) # search(3,[301,311,402,403,500,900,1000])
elif n < l[mid_index]: # n<l[7]==3<200
#in the left
l=l[:mid_index] # l=l[:7] l=[1,2,10,30,33,99,101]
search(n,l) # search(3,[1,2,10,30,33,99,101])
else:
print('find it')

search(3,l)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## 实现类似于l.index(30)的效果
l=[1,2,10,30,33,99,101,200,301,402]

def search(num,l,start=0,stop=len(l)-1): # search(301,[1,2,10,30,33,99,101,200,301,402],0,9)
if start <= stop: # 0<=9
mid=start+(stop-start)//2 # mid=0+(9-0)//2
print('start:[%s] stop:[%s] mid:[%s] mid_val:[%s]' %(start,stop,mid,l[mid]))
if num > l[mid]:
start=mid+1
elif num < l[mid]:
stop=mid-1
else:
print('find it',mid)
return
search(num,l,start,stop)
else: #如果stop > start则意味着列表实际上已经全部切完,即切为空
print('not exists')
return

search(301,l)

匿名函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 匿名就是没有名字
def func(x,y,z=1):
return x+y+z

## 匿名
lambda x,y,z=1:x+y+z #与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,除非让其有名字
func=lambda x,y,z=1:x+y+z
func(1,2,3) # 6

##让其有名字就没有意义
## 常规函数换算成匿名函数
def func(x,y,z=1):
print(x+y+z)
func(2,3)

print((lambda x,y,z=1:x+y+z)(2,3))
1
2
3
4
##有名函数与匿名函数的对比
有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能
匿名函数:一次性使用,随时随时定义
应用:maxminsorted,map,reduce,filter

lambda 代码示例

将lambda函数传递给一个另外一个函数call_func()进行处理的过程,在call_func中首先通过datetime包中的datetime.now()打印时间。然后调用被传递的函数。不仅lambda函数可以作为参数传递给其他函数,普通函数也可以当做参数传递

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
## /usr/bin/env python
## coding=utf-8
import datetime

def namedFync(a):
return "I'm named function with param {}".format(a)

def call_func(func,param):
print(datetime.datetime.now())
# print(namedFync('hello'))
# func=lambda x:x*2,param=9; print((lambda x:x*2)(9))
# func=lambda y:y*y,param=-4; print((lambda y:y*y)(-4))
print(func(param))
print("")

if __name__ == '__main__':
call_func(namedFync,'hello')
call_func(lambda x:x*2,9)
call_func(lambda y:y*y,-4)

#################### 结果如下 ####################
2019-12-26 13:57:45.356044
I'm named function with param hello

2019-12-26 13:57:45.356044
18

2019-12-26 13:57:45.356044
16

内置函数

1
2
3
##注意:内置函数id()可以返回一个对象的身份,返回值为整数。这个整数通常对应与该对象在内存中的位置,但这与python的具体实现有关,不应该作为对身份的定义,即不够精准,最精准的还是以内存地址为准。is运算符用于比较两个对象的身份,等号比较两个对象的值,内置函数type()则返回一个对象的类型

##更多内置函数:https://docs.python.org/3/library/functions.html?highlight=built#ascii

img## format

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
##字符串可以提供的参数 's' None
>>> format('some string','s')
'some string'
>>> format('some string')
'some string'

##整形数值可以提供的参数有 'b' 'c' 'd' 'o' 'x' 'X' 'n' None
>>> format(3,'b') #转换成二进制
'11'
>>> format(97,'c') #转换unicode成字符
'a'
>>> format(11,'d') #转换成10进制
'11'
>>> format(11,'o') #转换成8进制
'13'
>>> format(11,'x') #转换成16进制 小写字母表示
'b'
>>> format(11,'X') #转换成16进制 大写字母表示
'B'
>>> format(11,'n') #和d一样
'11'
>>> format(11) #默认和d一样
'11'

##浮点数可以提供的参数有 'e' 'E' 'f' 'F' 'g' 'G' 'n' '%' None
>>> format(314159267,'e') #科学计数法,默认保留6位小数
'3.141593e+08'
>>> format(314159267,'0.2e') #科学计数法,指定保留2位小数
'3.14e+08'
>>> format(314159267,'0.2E') #科学计数法,指定保留2位小数,采用大写E表示
'3.14E+08'
>>> format(314159267,'f') #小数点计数法,默认保留6位小数
'314159267.000000'
>>> format(3.14159267000,'f') #小数点计数法,默认保留6位小数
'3.141593'
>>> format(3.14159267000,'0.8f') #小数点计数法,指定保留8位小数
'3.14159267'
>>> format(3.14159267000,'0.10f') #小数点计数法,指定保留10位小数
'3.1415926700'
>>> format(3.14e+1000000,'F') #小数点计数法,无穷大转换成大小字母
'INF'

##g的格式化比较特殊,假设p为格式中指定的保留小数位数,先尝试采用科学计数法格式化,得到幂指数exp,如果-4<=exp<p,则采用小数计数法,并保留p-1-exp位小数,否则按小数计数法计数,并按p-1保留小数位数
>>> format(0.00003141566,'.1g') #p=1,exp=-5 ==》 -4<=exp<p不成立,按科学计数法计数,保留0位小数点
'3e-05'
>>> format(0.00003141566,'.2g') #p=1,exp=-5 ==》 -4<=exp<p不成立,按科学计数法计数,保留1位小数点
'3.1e-05'
>>> format(0.00003141566,'.3g') #p=1,exp=-5 ==》 -4<=exp<p不成立,按科学计数法计数,保留2位小数点
'3.14e-05'
>>> format(0.00003141566,'.3G') #p=1,exp=-5 ==》 -4<=exp<p不成立,按科学计数法计数,保留0位小数点,E使用大写
'3.14E-05'
>>> format(3.1415926777,'.1g') #p=1,exp=0 ==》 -4<=exp<p成立,按小数计数法计数,保留0位小数点
'3'
>>> format(3.1415926777,'.2g') #p=1,exp=0 ==》 -4<=exp<p成立,按小数计数法计数,保留1位小数点
'3.1'
>>> format(3.1415926777,'.3g') #p=1,exp=0 ==》 -4<=exp<p成立,按小数计数法计数,保留2位小数点
'3.14'
>>> format(0.00003141566,'.1n') #和g相同
'3e-05'
>>> format(0.00003141566,'.3n') #和g相同
'3.14e-05'
>>> format(0.00003141566) #和g相同
'3.141566e-05'

lambda与内置函数结合使用

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
36
37
38
39
40
41
42
43
44
字典的运算:最小值,最大值,排序
salaries={
'allen':3000,
'alex':100000000,
'wupeiqi':10000,
'yuanhao':2000
}

迭代字典,取得是key,因而比较的是key的最大和最小值
>>> max(salaries)
'yuanhao'
>>> min(salaries)
'alex'

可以取values,来比较
>>> max(salaries.values())
>>> min(salaries.values())
但通常我们都是想取出,工资最高的那个人名,即比较的是salaries的值,得到的是键
>>> max(salaries,key=lambda k:salary[k])
'alex'
>>> min(salaries,key=lambda k:salary[k])
'yuanhao'



也可以通过zip的方式实现
salaries_and_names=zip(salaries.values(),salaries.keys())

先比较值,值相同则比较键
>>> max(salaries_and_names)
(100000000, 'alex')


salaries_and_names是迭代器,因而只能访问一次
>>> min(salaries_and_names)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: min() arg is an empty sequence



sorted(iterable,key=None,reverse=False)

!!!lambda与内置函数结合使用!!!

eval与exec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
##1、语法
## eval(str,[,globasl[,locals]])
## exec(str,[,globasl[,locals]])

##2、区别
##示例一:
s='1+2+3'
print(eval(s)) #eval用来执行表达式,并返回表达式执行的结果
print(exec(s)) #exec用来执行语句,不会返回任何值
'''
None
'''

##示例二:
print(eval('1+2+x',{'x':3},{'x':30})) #返回33
print(exec('1+2+x',{'x':3},{'x':30})) #返回None

## print(eval('for i in range(10):print(i)')) #语法错误,eval不能执行表达式
print(exec('for i in range(10):print(i)'))

evalexec

complie(了解即可)

1
2
3
4
5
6
7
8
9
10
compile(str,filename,kind)
## filename:用于追踪str来自于哪个文件,如果不想追踪就可以不定义
## kind可以是:single代表一条语句,exec代表一组语句,eval代表一个表达式
s='for i in range(10):print(i)'
code=compile(s,'','exec')
exec(code)

s='1+2+3'
code=compile(s,'','eval')
eval(code)

异常

异常处理

异常处理是编程语言中的一种机制,用于处理软件或信息系统中出现的异常状况(即超出程序正常执行的某些特殊条件)。

异常处理(又称错误处理)功能提供了处理程序运行时出现的任何意外或异常情况而不会使整个程序机制崩溃的方法。

Python异常处理使用try,catch,else,finally关键字来尝试可能未成功的操作、处理失败及正常情况,以及在事后情理资源。

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
## 监测可能会发生异常的模块
try:
<语句> #运行别的代码

## 第1种捕获一种异常
except Exception1:
<语句> #如果在try部份引发了Exception1异常,则执行这个语句

## 第2种捕获多种异常
except(Exception2,Exception3,Exception4)
##如果在try部份引发了Exception2 or Exception3 or Exception4异常,则执行这个语句
<语句>

## 第3种捕获异常转换为一个变量
except Exception5 as e:
print(e)

## 第4种捕获多种异常转换为一个变量
except (Exception6,Exception7,Exception8) as e:
print(e)

## 第5种捕获任何异常
except:
<语句>

## 当没有异常时的处理(可选快)
else:
<语句>

## 无论是否出现异常,最后都要做的处理(可选快)
finally:
<语句>

## 每种except形式可以被定义多次,当try块中发现异常时,系统从上到下逐个检查except块。当发现满足发生异常定义的except块时,进入该块进行处理,并忽略其他except块

自定义异常

1
2
3
4
5
6
7
8
9
10
try:
print(1+'1')
except (Exception,TypeError) as e:
print(e)
finally:
print(12)

################# 结果:###############
unsupported operand type(s) for +: 'int' and 'str'
12

raise显示引发异常

有关于python里raise显示引发异常的方法:

  • 当程序出错时,python会自动触发异常,也可以通过raise显示引发异常
  • 一旦执行了raise语句,raise之后的语句不在执行
  • 如果加入了try,except,那么except里的语句会被执行
1
2
3
4
5
6
7
8
9
10
11
12
def mye( level ):
if level < 1:
raise Exception("Invalid level!")
# 触发异常后,后面的代码就不会再执行
try:
mye(0) # 触发异常
except Exception as err:
print(1,err)
else:
print(2)
## 执行以上代码,输出结果为:
1 Invalid level!

python标准异常

异常名称描述
BaseException所有异常的基类
SystemExit解释器请求退出
KeyboardInterrupt用户中断执行(通常是输入^C)
Exception常规错误的基类
StopIteration迭代器没有更多的值
GeneratorExit生成器(generator)发生异常来通知退出
StandardError所有的内建标准异常的基类
ArithmeticError所有数值计算错误的基类
FloatingPointError浮点计算错误
OverflowError数值运算超出最大限制
ZeroDivisionError除(或取模)零 (所有数据类型)
AssertionError断言语句失败
AttributeError对象没有这个属性
EOFError没有内建输入,到达EOF 标记
EnvironmentError操作系统错误的基类
IOError输入/输出操作失败
OSError操作系统错误
WindowsError系统调用失败
ImportError导入模块/对象失败
LookupError无效数据查询的基类
IndexError序列中没有此索引(index)
KeyError映射中没有这个键
MemoryError内存溢出错误(对于Python 解释器不是致命的)
NameError未声明/初始化对象 (没有属性)
UnboundLocalError访问未初始化的本地变量
ReferenceError弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError一般的运行时错误
NotImplementedError尚未实现的方法
SyntaxErrorPython 语法错误
IndentationError缩进错误
TabErrorTab 和空格混用
SystemError一般的解释器系统错误
TypeError对类型无效的操作
ValueError传入无效的参数
UnicodeErrorUnicode 相关的错误
UnicodeDecodeErrorUnicode 解码时的错误
UnicodeEncodeErrorUnicode 编码时错误
UnicodeTranslateErrorUnicode 转换时错误
Warning警告的基类
DeprecationWarning关于被弃用的特征的警告
FutureWarning关于构造将来语义会有改变的警告
OverflowWarning旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning关于特性将会被废弃的警告
RuntimeWarning可疑的运行时行为(runtime behavior)的警告
SyntaxWarning可疑的语法的警告
UserWarning用户代码生成的警告