Python语言的最后一些特性

这些课堂讲稿将涵盖Python语言的三个附加特性。这些在本学期的课程中没有出现,但我仍然认为这些特征很重要,足以在本课程中涵盖。

发生器功能

假设有一些数据存储在一个列表中。下面是两个常见的循环示例,您可以使用它们在列表上进行交互。

查询数据列表中的item: #查询数据列表中的item (0,N)

第一个循环的目的很明确:简单地遍历列表中的赢博体育数据项。

第二个示例使用一个范围表达式来帮助我们迭代索引值的范围。您可以从range表达式的结构猜出range()是一个函数。您可能还会猜测range()的作用是构造一个索引值列表[0,1,2,…,N-1],然后循环遍历它。

这不是range()所做的。特别是,由于设置列表需要占用空间,因此这种方法不是最优的。相反,我们可以将range()函数实现为一种称为生成器函数的特殊类型的函数。下面是range()生成器函数的代码:

Def range(start,end): n = start;当n < end: yield n n += 1

生成器函数用yield语句代替return语句。生成器函数被设计为运行到遇到yield语句为止。然后,函数停止并返回yield语句中的值。在对函数的下一次调用时,函数会在yield语句之后的下一条语句中拾取并再次运行,直到遇到yield语句或到达函数体的末尾。

Python for循环被设计成与生成器函数结合使用来进行迭代。循环将反复调用生成器函数,直到生成器停止产生值。这种安排的优点是空间效率高。由于不需要构造索引值的列表来进行迭代,因此可以节省该列表的空间。

在许多赢博体育程序中,生成器函数是有意义的。一个常见的赢博体育程序出现在从文件读取数据的上下文中。在过去的示例中,我们通过将赢博体育数据读入列表然后处理该列表来处理来自文本文件的数据。再一次,使用生成器函数,我们可以避免制作这个列表。下面是一些代码来展示它是如何工作的:

defcleanline (line): ““”将原始行列表转换为适当的数据格式。”“” return (int(line[0]), float(line[1])) def readData(fileName): "“”通用数据读取函数。这将读取单独的行,按照需要格式化它们的内容,并每次返回一行的数据值。”“” with open(fileName) as f: for line in f.readlines(): yield cleanLine(line.split())

这个生成器函数的设计目的是每次为您提供一行数据。使用这个生成器函数,你可以编写这样的代码:

每年,弹出readData("data.txt"):

更高级的赢博体育程序是用于捕获序列中某些复杂模式的术语的生成器函数。例如,在辛普森法则的例子中,我们在几节课之前看到我们需要使用一些复杂的逻辑来获取和中项序列中的1,2,4,2,4,1个系数的模式。下面是一个程序,用于做辛普森规则计算,它使用一个生成器来处理在总和中产生系数所需的逻辑:

这是我们将在这个例子中使用的函数def (x):返回math.sqrt(4.0 - x*x) #生成器,生成辛普森规则中需要的系数和x值#。def simpsonTerms(a,b,N): yield (1,a) h = (b-a)/N for i in range(1,N): yield (4,a+((i-1)*(b-a))/N+h/2) yield (2,a+(i*(b-a))/N) yield (4,b-h/2) yield (1,b) #使用Simpson's rule #计算f(x)从a到b的积分#的近似值,步长为h = (b-a)/N def SimpsonArea(a,b,N): sum = 0.0 for coeff,x in simpsonTerms(a,b,N): sum = sum + coeff*f(x)返回sum*(b-a)/(6*N) print(“N错误”);for j in range(3,20,2): estimate = SimpsonArea(0.0,2.0,2**j) error = math.fabs(π-估计)打印(“{:> 7 d} {: g}”.format (2 * * j、错误))

断言

Python语言中我不喜欢的几件事之一是缺少类型信息。当我们向函数传递参数时,这个问题最为明显。为了使函数正常工作,调用者需要将函数作者在编写函数时想要的数据类型传递给它。例如,如果函数f(x,y)期望x是实数,y是包含三个元素的元组,我们可以在代码中编写f(1.5,2.5)。当代码运行时,您的程序很可能会在f(x,y)函数的代码中生成一个错误。只有这样,您才会意识到您向函数传递了错误的参数。

函数作者可以做很多事情来防止这类问题。他们可以做的一件事是提供一个文档字符串或其他文档,记录他们对参数的期望。作者可以做的另一件事是在函数体的开始放置防御性的if语句,以检查确保传入的参数具有正确的类型和其他详细信息。例如,我们想象的f(x,y)函数的作者可以这样做:

def (x,y):如果不是isinstance(x,float): print(“Error! ”x应该是浮点数。“)elif not isinstance(y,tuple): print(”错误!“) elif len(y) != 3: print(”Error! "y应该有三个元素。”)else: #继续执行剩下的代码

Python的isinstance()函数提供了一种检查某些数据项类型的方法。这里的if语句使用该测试来确认传入的数据具有正确的类型。

这不是一个理想的解决方案,因为尽管这会提醒您将错误的参数传递给f,但它不会阻止您的程序运行超过错误调用f的点。这不是一个好主意,因为如果f不能做您希望它做的事情,那么在调用f失败后不久,您自己的代码很可能会中断。

在这种情况下,正确的方法是一旦检测到问题,就强制程序打印错误消息并停止运行。在Python中实现这一点的一种方法是使用Python断言结构:

Def f(x,y): assert isinstance(x,float),"x in f(x,y)应该是float." assert isinstance(y,tuple), “y in f(x,y)应该是元组” assert len(y) == 3, “y in f(x,y)应该有三个元素。”#继续执行剩下的代码

assert语句包含一个测试和一个字符串消息。如果测试失败,assert将停止程序并打印一条错误消息,其中包含assert失败时程序所在位置的信息。

对于那些见过其他使用异常的编程语言的人来说,您可能有兴趣知道assert所做的是在测试失败时抛出异常。

try结构

Python使用一种称为异常机制的机制来处理程序中的致命错误。在整个课程中,我们使用异常提供的信息来调试我们的程序。这种编程方式的一个问题是,当出现问题时,我们的程序完全停止,我们必须修复问题。

为了让程序员在如何响应这类错误方面有更多的选择,Python提供了try结构。try结构的一般形式是

try: #可能产生错误的代码,除了:#只在发生错误时运行的代码

这里有一个例子说明这是如何有用的。在人口和GDP值的绘图分配中,你必须与缺失的数据作斗争。这里是一个可能的解决方案,该分配,利用尝试结构,从偶尔丢失的数据引起的错误优雅地恢复:

进口matplotlib。将数据加载到一个列表中。Filename = ‘data/population_data. ’pop_data = json. json' with open(filename) as f: pop_data = json. Load (f) #加载数据帧df = pd.read_csv('data/GDP.csv') df = df。set_index('Country Code') def findPopulation(Code, year):对于pop_data中的条目:如果条目['Country Code'] == Code并且条目[' year '] == str(year):返回float(条目['Value'])返回0 g = [] p =[]对于pop_data中的条目:如果条目[' year '] == '1995': Code =条目['Country Code'] try: p95 = float(条目['Value']) p05 = findPopulation(Code,2005) gdp95 = df。Loc [code,'1995'] gdp05 = df。loc[code,'2005'] gpc95 = gdp95/p95 gpc05 = gdp05/p05 gdp_growth = (gpc05-gpc95)/gpc95 pop_growth = (p05-p95)/p95 g.p append(gdp_growth) p.p append(pop_growth) except: print('Missing data for '+code)Plot (p,g, 'bo')

如果您查看程序底部for循环中的代码,您将看到一个try结构。如果发生以下两种情况之一,try之后的代码可能会失败并出现异常。如果缺少特定国家的人口数据,findPopulation()函数被设计为返回0。当我们计算人均GDP时,这将很快导致除以0的例外。这里出错的第二件事是数据帧可能缺少特定国家代码的条目。当发生这种情况时,loc[]表达式将生成无效索引异常。

如果在try之后的代码中出现任何错误,程序将停止运行try之后的代码,并跳转到except之后的代码。这将导致程序打印一条错误消息(也不会为该特定国家/地区的g或p添加任何内容),但随后继续处理其余国家/地区。这是一种从丢失数据引起的问题中恢复的优雅而适当的方法。