当前位置: 首页 > 后端技术 > Python

翻译:《实用的Python编程》03_02_More_functions

时间:2023-03-26 00:22:48 Python

上一节(3.1脚本)|下一节(3.3错误检查)3.2深入函数虽然前面介绍了函数,但是关于函数如何在更深层次上工作的细节却很少提供。本节旨在填补这些空白并讨论函数调用约定、作用域规则等问题。调用函数考虑以下函数:defread_prices(filename,debug):...可以使用位置参数调用该函数:prices=read_prices('prices.csv',True)或者,可以使用关键字参数调用该函数:prices=read_prices(filename='prices.csv',debug=True)defaultparameters有时你希望参数是可选的,如果是这样,在函数定义中分配一个默认值。defread_prices(filename,debug=False):...如果分配了默认值,参数在函数调用中是可选的。d=read_prices('prices.csv')e=read_prices('prices.dat',True)注意:有默认值的参数必须出现在参数列表的末尾(所有非可选参数都放在最前面)Preferred作为可选参数的关键字参数比较以下两种不同的调用方式:,ignore_errors=True)关键字参数在大多数情况下提高了代码的简洁性——尤其是对于用作标志或与可选功能相关的参数。设计最佳实践始终是为函数参数提供简短但有意义的名称。使用函数的人可能希望使用关键字调用风格。d=read_prices('prices.csv',debug=True)Python开发工具将在帮助函数或帮助文档中显示这些名称。返回值return语句返回一个值:defsquare(x):returnx*x如果没有给出返回值或缺少return语句,则返回None:defbar(x):statementsreturn=bar(4)#a=None#ORdeffoo(x):statements#No`return`b=foo(4)#b=None多个返回值函数只能返回一个值。但是,函数可以通过将返回值放在一个元组中来返回多个值:defdivide(a,b):q=a//b#商r=a%b#余数returnq,r#返回一个元组示例:x,y=divide(37,5)#x=7,y=2x=divide(37,5)#x=(7,2)变量作用域程序为变量赋值:x=value#全局变量定义foo():y=value#局部变量变量赋值发生在函数内外。在函数外部定义的变量是“全局的”。函数内部定义的变量是“局部的”。局部变量在函数内部分配的变量是私有的。defread_portfolio(文件名):portfolio=[]forlineinopen(filename):fields=line.split(',')s=(fields[0],int(fields[1]),float(fields[2]))portfolio.append(s)returnportfolio在此示例中,文件名、投资组合、行、字段和s是局部变量。这些变量在函数调用后将不会持久存在或无法访问。>>>stocks=read_portfolio('portfolio.csv')>>>fieldsTraceback(最近一次调用last):文件“”,第1行,在?NameError:名称“fields”未定义>>>局部变量也不能和其他地方的变量冲突。全局变量函数可以自由访问同一个文件中定义的全局变量的值。name='Dave'defgreeting():print('Hello',name)#使用`name`全局变量然而,函数不能修改全局变量:name='Dave'defspam():name='Guido'spam()print(name)#prints'Dave'记住:函数中的所有赋值都是局部的修改全局变量如果你必须修改一个全局变量,像这样声明它:name='Dave'defspam():globalnamename='Guido'#更改上面的全局名称全局声明必须出现在使用之前,并且对应的变量必须与函数在同一个文件中。看看上面的函数,这是一种糟糕的形式。事实上,如果可以的话,尽量避免使用global。如果您需要一个函数来修改函数外部的某些状态,最好改用类(稍后会详细介绍)。参数传递调用函数时,参数变量通过引用传递。值不被复制(参见第2.7节)。如果传递了可变数据类型(例如列表、字典),则可以就地修改它们。deffoo(items):items.append(42)#修改输入对象a=[1,2,3]foo(a)print(a)#[1,2,3,42]关键点:函数不接受输入参数的副本。重新分配与修改确保您了解修改值和重新分配变量名的细微差别。deffoo(items):items.append(42)#修改输入对象a=[1,2,3]foo(a)print(a)#[1,2,3,42]#VSdefbar(items):items=[4,5,6]#将局部`items`变量更改为指向不同的对象b=[1,2,3]bar(b)print(b)#[1,2,3]reminder:variableassignment内存永远不会被重写。该名称只是绑定到新值。上面的练习这组练习实现的可能是课程中最强大和最难的部分。有很多步骤,很多以前练习的概念都放在一起了。虽然最终解决方案只有大约25行代码,但请花点时间确保您理解每个部分。report.py的核心部分主要是读取CSV文件。例如,read_portfolio()函数读取包含投资组合数据的文件,read_prices()函数读取包含价格数据的文件。这两个功能都有很多低级的“好”东西和类似的功能。例如,它们都打开一个文件并使用csv模块处理它,将各种字段转换为新类型。如果你真的需要解析很多文件,你可能需要清理其中的一些文件,使它们更通用。这是我们的目标。通过打开Work/fileparse.py文件开始本练习,我们将在该文件中编写代码。练习3.3:读取CSV文件首先,我们只关注将CSV文件读取到字典列表中的问题。在fileparse.py中,定义一个如下所示的函数:#fileparse.pyimportcsvdefparse_csv(filename):'''将CSV文件解析为记录列表'''withopen(filename)asf:rows=csv。reader(f)#读取文件头headers=next(rows)records=[]forrowinrows:ifnotrow:#跳过没有数据的行continuerecord=dict(zip(headers,row))records.append(record)returnrecords该函数将CSV文件读入字典列表,但隐藏打开的文件,使用csv模块处理,忽略空行等细节。试试看:提示:python3-ifileparse.py.>>>portfolio=parse_csv('Data/portfolio.csv')>>>portfolio[{'price':'32.20','name':'AA','shares':'100'},{'price':'91.10','name':'IBM','shares':'50'},{'price':'83.44','name':'CAT','shares':'150'},{'price':'51.23','name':'MSFT','shares':'200'},{'price':'40.37','name':'GE','shares':'95'},{'price':'65.10','name':'MSFT','shares':'50'},{'price':'70.44','name':'IBM','shares':'100'}]>>>这没问题,只是您无法对数据进行任何有用的计算。因为所有的内容都是用字符串来表示的。我们很快就会解决这个问题,所以让我们继续在它之上构建。练习3.4:构建列选择器在大多数情况下,您只对CSV文件中选定的列感兴趣,而不是所有数据。修改parse_csv()函数让用户指定任意列,如下所示:>>>#读取所有数据>>>portfolio=parse_csv('Data/portfolio.csv')>>>portfolio[{'price':'32.20','name':'AA','shares':'100'},{'price':'91.10','name':'IBM','shares':'50'},{'price':'83.44','name':'CAT','shares':'150'},{'price':'51.23','name':'MSFT','shares':'200'},{'price':'40.37','name':'GE','shares':'95'},{'price':'65.10','name':'MSFT','shares':'50'},{'price':'70.44','name':'IBM','shares':'100'}]>>>#只读取部分数据>>>shares_held=parse_csv('Data/portfolio.csv',select=['name','shares'])>>>shares_held[{'name':'AA','shares':'100'},{'name':'IBM','shares':'50'},{'name':'CAT','shares':'150'},{'name':'MSFT','shares':'200'},{'name':'GE','shares':'95'},{'name':'MSFT','shares':'50'},{'name':'IBM','shares':'100'}]>>>一个练习2.23中给出了列选择器的示例。然而,这里有一个方法:#fileparse.pyimportcsvdefparse_csv(filename,select=None):'''ParseaCSVfileintoalistofrecords'''withopen(filename)asf:rows=csv.reader(f)#读取文件头headers=next(rows)#如果给定了列选择器,则查找指定列的索引。#同时缩小用于结果字典的标题集ifselect:indices=[headers.index(colname)forcolnameinselect]headers=selectelse:indices=[]records=[]forrowinrows:ifnotrow:#跳过没有数据的行continue#如果选择了特定的列,则过滤行ifindices:row=[row[index]forindexinindices]#创建一个字典record=dict(zip(headers,row))records.append(record)returnrecords这部分有一些棘手的问题,最重要的一个一个可能是列选择到行索引的映射例如,假设输入文件具有以下标题:>>>headers=['name','date','time','shares','price']>>>现在,假设选定的列如下:>>>select=['name','shares']>>>为了执行正确的选择,选定的列名必须映射到文件中的列索引.这就是这一步所做的:>>>indices=[headers.index(colname)forcolnameinselect]>>>indices[0,3]>>>换句话说,names("name")是0列,股数(“股份”)是第3列。从文件中读取数据行时,使用索引过滤:>>>row=['AA','6/11/2007','9:50am','100','32.20']>>>row=[row[index]forindexinindices]>>>row['AA','100']>>>练习3.5:执行类型转换修改parse_csv()函数,以便它可以选择性地应用类型转换以返回数据。例如:>>>portfolio=parse_csv('Data/portfolio.csv',types=[str,int,float])>>>portfolio[{'price':32.2,'name':'AA','shares':100},{'price':91.1,'name':'IBM','shares':50},{'price':83.44,'name':'CAT','shares':150},{'price':51.23,'name':'MSFT','shares':200},{'price':40.37,'name':'GE','shares':95},{'price':65.1,'name':'MSFT','shares':50},{'price':70.44,'name':'IBM','shares':100}]>>>shares_held=parse_csv('Data/portfolio.csv',select=['name','shares'],types=[str,int])>>>shares_held[{'name':'AA','shares':100},{'name':'IBM','shares':50},{'name':'CAT','shares':150},{'name':'MSFT','shares':200},{'name':'GE','shares':95},{'name':'MSFT','shares':50},{'name':'IBM','shares':100}]>>>这已在练习2.24探索中完成.需要在解决方案中插入以下代码片段:...iftypes:row=[func(val)forfunc,valinzip(types,row)]...练习3.6:处理一些包含无标题数据的CSV文件不包含任何标题信息。例如,prices.csv文件如下所示:“AA”、9.22“AXP”、24.85“BA”、44.85“BAC”、11.27...修改parse_csv()文件以通过创建元组列表来处理此问题文档。例如:>>>prices=parse_csv('Data/prices.csv',types=[str,float],has_headers=False)>>>prices[('AA',9.22),('AXP',24.85),('BA',44.85),('BAC',11.27),('C',3.72),('CAT',35.46),('CVX',66.67),('DD',28.47),('DIS',24.22),('GE',13.48),('GM',0.75),('HD',23.16),('HPQ',34.35),('IBM',106.28),('INTC',15.72),('JNJ',55.16),('JPM',36.9),('KFT',26.11),('KO',49.16),('MCD',58.99),('MMM',57.1),('MRK',27.58),('MSFT',20.89),('PFE',15.19),('PG',51.94),('T',24.79),('UTX',52.61),('VZ',29.26),('WMT',49.74),('XOM',69.35)]>>>要实现这个变化,需要修改代码,让第一行数据不被解释为标题行。此外,您需要确保不创建字典,因为不再有可用于列名的键。练习3.7:选择不同的列分隔符尽管CSV文件很常见,您也可能会遇到使用其他列分隔符(例如制表符或空格)的文件。例如,以下Data/portfolio.dat文件:namesharesprice"AA"10032.20"IBM"5091.10"CAT"15083.44"MSFT"20051.23"GE"9540.37"MSFT"5065.10"IBM"10070.44csv.reader()函数允许指定不同的分隔符,如下所示:rows=csv.reader(f,delimiter='')修改parse_csv()函数也允许更改分隔符。例如:>>>portfolio=parse_csv('Data/portfolio.dat',types=[str,int,float],delimiter='')>>>portfolio[{'price':'32.20','name':'AA','shares':'100'},{'price':'91.10','name':'IBM','shares':'50'},{'price':'83.44','name'':'CAT','shares':'150'},{'price':'51.23','name':'MSFT','shares':'200'},{'price':'40.37','name':'GE','shares':'95'},{'price':'65.10','name':'MSFT','shares':'50'},{'price':'70.44','name':'IBM','shares':'100'}]>>>说明到目前为止,如果你完成了,那么你已经创建了一个非常有用的库函数。您可以使用它来解析任意CSV文件、选择感兴趣的列并执行类型转换,而无需过多担心文件或csv模块的内部工作。目录|上一节(3.1脚本)|下一节(3.3错误检查)注意:完整翻译见https://github.com/codists/practical-python-zh

猜你喜欢