Python教程:第2章(Python进阶)2.1-2.3

目录请见详细目录

2.1.报错、错误与试错

2.1.1.错误和常见的错误类型

在Python领域写程序,无论作为新手上路,还是高手一枚,总是不可避免的遇到这样那样的各种错误,我们熟知的错误就包括:

错误名称导致原因
SyntaxError条件或循环等末尾没有冒号
----------------条件判断中的==写成了=
----------------将关键字作为变量进行了声明
DivisionbyzeroError除法中的除数为0
IndentationError缺失或多余的缩进块
ValueError类型正确但值不符合要求
TypeError对str和int类型进行拼接
-----------将bool类型加减乘除
-----------修改str或tup的部分内容
-----------调用方法时传入的参数不符合传入要求(或多/少传入了参数)
-----------str没加引号
NameError使用了未定义的变量(对象)直接计算或在声明global变量之前无定义
IndexError索引超过了list/str/tuple的最大索引
KeyError调用了dict的未知名称
AttributeError调用了对象的未知属性或方法

2.1.2.try和except嵌套

在程序运行过程中,如果遇到了错误,程序会自动结束并报出错误.我们也可以使用raise来手动抛出错误,例如:

raise ValueError("一个ValueError")

可以自定义错误内容,但类型还是属于ValueError.如何自定义错误?请看教程1.2.4..那么如何让程序在出现错误后仍然运行呢?此时我们就要用到try...except语句了.在try下缩进的代码运行时,如果出现了错误,则会停止该缩进块而去运行except下内容,语法格式如下:

try:
	print("1")
	raise IndexError
	print("2")
except IndexError:
	print("3")
except Exception as e:
	print(e) 

在打印出1以后遇到了IndexError,因此跳出try缩进块.先检索第一个except,发现就是IndexError,于是打印3,离开try...except块.此过程中不会报错,只会依次打印1和3.
如果我们将raise出的错误更改一下,像这样:

try:
	print("1")
	raise ValueError
	print("2")
except IndexError:
	print("3")
except Exception as e:
	print(e)

就会发现检索到的并不是IndexError,因此进入第二个except检索.这里有一个公共错误类Exception,任何错误都继承于该类,因此检索成功.此时将错误信息保存在对象e中,打印出错误.**注意!**在打印该错误时,虽然内容就是报错内容,程序仍未停止,可以继续运行.其实还可以这样:

try:
	print("1")
	raise IndexError
	print("2")
except IndexError:
	print("3")
except NameError:
	print("4")
else:
	print(5)	

此时,前两个except都没有检索到错误,因此进入else块.else块当且仅当try下发生错误且所有except语句均没有检索到错误时才会运行.

2.2.惰性计算和立即计算

此章较为重要 务必认真学习 在日后的人工智能等方面中,惰性计算发挥了巨大的作用!

2.2.1.迭代器与可迭代对象

2.2.1.1.什么是迭代器和可迭代对象

在学循环的时候,我们就知道如果要对1-30的正整数遍历,可以这样写:

for i in range(1,31):
	pass

所以这个range到底是个啥?[由于教程基于Python 3.7+,这里不再提及Python 2的一些细节和特性]如果我们直接使用:print(range(1,31)),会发现打印结果就是range(1,31),类型为<class "range">.而所谓range,其实就是我们的迭代器.对于循环语句,我们知道还可以这样写:

for i in [1,2,3]:
	pass

这意味着让i分别带入1,2,3进行计算.这里的列表[1,2,3]就是可迭代对象.

2.2.1.2.常见的迭代器和可迭代对象

在Python中,迭代器能且只能为range函数返回的结果,或者是一个类型为<class "range">的对象.而可迭代对象则可以是任何group型的数据,或者一个字符串.例如,list tuple array str都可以作为可迭代对象使用.

2.2.1.3.迭代器和可迭代对象的转换

先来看看如何将range转换为可迭代对象.很简单,直接将其存为list(或tup)即可:

a=range(1,31)
a=list(a)
#a=[1,2,3,...30]
b=[range(1,31)]
#b=[range(1,31)]
c=[for i in range(1,31)]
#c=[1,2,3,...30]

看出区别了吧?如果使用list类强制转型range类,就会直接释放其中的所有数字;而用列表储存不同,其仍然以range形式存在;而最后一种则是第一种的另一种等价写法,只不过其可以和下一节的lambda函数一起使用来减少代码量.
那么如何将可迭代对象转换为迭代器呢?其实只需要将首尾和公差计算后即可转换!

2.2.2.从内存角度看惰性计算

什么是惰性计算?在上一节中,我们提到了这样一种列表创建方法:

mylist=[for i in range(1,31)]

如果我希望实现对于每个1-30的数都平方,然后减一呢?
原来的写法应当是这样的:

result=[]
for i in range(1,31):
	j=i
	j=j**2-1
	result.append(j)
print(result)	

如果从迭代器的角度看,可以这样写:
先定义方法:

def caculate(m):
	return m**2-1

然后进行惰性计算:

result=[caculate(i) for i in range(1,31)]

和上面的代码结果是一样的.还可以进行多次操作,如:

n=range(1,31)
result1=[i**2 for i in n]
result2=[result1[i]-1 for i in n]

惰性计算,我们可以这样认为:如果直接创建列表[1,2,3,…30],那么要占用31个内存单位元,分别储存1-30的数字和list这个类型.而range则不同,range(1,31)只需要3个内存单位元,分别储存起始值1,末位置31和range这个类型.这样不仅大大节省了内存空间,而且减少了代码量,岂不妙哉?
最后,我们来官方地讲一下什么是惰性计算.所谓惰性计算就是不在迭代器给出第一时间分析出其中所有元素,而是以少量内存形式存在,一直保存迭代器形式直到所在代码段(如方法/循环)结束才把迭代器中的每一个元素拆开.这种方式虽说没有省下什么代码运行时间,但是大大缩小了(特别是数据量大的时候)内存消耗和代码量.事实上,定义的类下的除初始化外的所有方法,都是惰性计算.它们仅在被调用时被编译.而外部定义的方法则都是立即计算.

2.2.3.迭代器的惰性计算

迭代器作为python对象,同样也可以进行惰性计算.先看一段代码:

n=range(1,31)
a=[range(1,i+1) for i in n]
b=[list(a[i]) for i in n]

先看第一句.变量n是一个迭代器.
第二句: 对于1-30,分别创建一个迭代器range(1,i).这意味着a这个列表中包含着30个惰性计算了的迭代器.
第三句:对于每一个迭代器都转换为列表,因此b应该为:
b=[[1],[1,2],[1,2,3],[1,2,3,4],[1,2,3,4,5],...,[1,2,3,...30]]

2.2.4.惰性计算的应用

目前而言,还没什么真正的应用,但当机器学习开始的时候,就会有大规模应用.这里仅展示一段代码(我的mml-qae库的源代码的一部分),有兴趣的同学可以尝试理解:

import pandas as pd 
import numpy as np
from sklearn.model_selection import train_test_split as tts
from sklearn.neighbors import KNeighborsClassifier as knc 
from sklearn.metrics import accuracy_score as acc
import mml_qae.MoreErrors as me
def max_of_list(l:list):
    """给定列表返回其中最大值"""
    maxium=l[0]
    for i in l:
        try:
            i=float(i)
        except:
            raise me.DataError2   
    for j in range(0,len(l)-1):
        a=l[j];b=l[j+1]
        if b>a:
            maxium=b
    return int(maxium)        

def predict(path:str,to_name:str,need_predict:pd.DataFrame):
    try:data=pd.read_csv(path,header=0)#读入csv数据
    except:raise me.CSVError
    try:  
        x_data=data.drop([to_name],axis=1)#从data中摘取特征组
        y_data=np.ravel(data[[to_name]])#从data中摘取目标组
    except:raise me.DataError3    
    x_trainset , x_testset , y_trainset , y_testset = tts(x_data,y_data,random_state=1)
    #建立训练集(训练特征组 和 训练目标组)和测试集(测试特征组 和 训练目标组)
    n=range(1,23)
    KNNs=[knc(n_neighbors=i) for i in n]
    scores=[KNNs[i].fit(x_trainset,y_trainset).score(x_testset,y_testset) for i in range(len(KNNs))]
    best=max_of_list(scores)
    bs=scores.index(best)+1
    new=knc(algorithm="kd_tree",n_neighbors=bs)
    new.fit(x_trainset,y_trainset)
    ny=new.predict(need_predict)
    return ny

def get_score(path:str,to_name:str):
    data1=pd.read_csv(path,header=0)
    x_data=data1.drop([to_name],axis=1)#从data中摘取特征组
    y_data=np.ravel(data1[[to_name]])#从data中摘取目标组
    x_trainset , x_testset , y_trainset , y_testset = tts(x_data,y_data,random_state=1) 
    n=range(1,23)
    KNNs=[knc(n_neighbors=i) for i in n]
    scores=[KNNs[i].fit(x_trainset,y_trainset).score(x_testset,y_testset) for i in range(len(KNNs))]
    best=max_of_list(scores)
    return best 

2.3.lambda函数详解

2.3.1.什么是lambda

lambda,又称匿名函数.在Python中,如果定义了一个方法m,那么type(m)就会是<class "function">,而print(m)会得到<function m at 0x00000228E0DAC268>.这说明Python已经为你申请了变量m的内存地址0x00000228E0DAC268.而lambda只声明函数内容,不声明名称,如:

lambda x:x+1

此时,我们没法多次调用,而只能在定义后立即使用它来获取返回结果:

print((lambda x:x+1)(1))
#->2

那么想多次调用呢?直接给一个内存地址就好了呗:

func=lambda x:x+1
print(func(1))

这样语法就和普通的def没什么区别的.其中的x为传入实参,x+1为传出实参.lambda语句不需要return,因为它只能有一句代码,而这句代码就是返回值.如果不将其赋值给某一个变量,那么调用就要用tuple包括住所需要传入的实参.也可以传入多个参数,如:

(lambda x,y:abs(x-y))(3,2)
#->1

注意传入参数部分没有括号.整个lambda(包括返回值)都需要用小括号括住.

2.3.2.用lambda简化你的程序

先来看一段乱七八糟的代码:

def a(x,y,z):
	m=x
	if m<y:m=y
	if m<z:m=z
	return m

r=[]	
for i1 in range(1,31):
	for i2 in range(11,41):
		for i3 in range(21,51):
			r.append(a(i1,i2,i3))
print(r)			

尝试简化[这里不给思路 看看能否看懂?看不懂可留言]

r=(lambda i1,i2,i3:i2max(max(i1,i2),i3))\
((range(10*j+1,10*j+31),\
range(10*j+1,10*j+31),\
range(10*j+1,10*j+31) for j in range(1,4)))
print(r)

虽说看起来复杂了点,思路也烦了些,但总之是省代码了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值