当前位置: 首页 > 科技观察

修改变量名,简单有效提高代码质量!

时间:2023-03-15 16:46:30 科技观察

请快速说出以下代码的作用:foriinrange(n):forjinrange(m):forkinrange(l):temp_value=X[i][j][k]*12.5new_array[i][j][k]=temp_value+15很难,对吧?除非您知道作者的想法,否则尝试修改或调试此代码将很困难。即使是作者本人,在编写这段代码几天后,也忘记了它的用途,因为变量名和“幻数”无助于记住代码的作用。在使用数据科学代码时,类似上述(或更糟)的示例很常见:具有变量名称的代码,如x、y、xs、x1、x2、tp、tn、clf、reg、xi、yi、ii和许多未命名的常量值.坦率地说,数据科学家(包括我自己)不太擅长命名变量。许多从为一次性分析编写面向研究的数据科学代码到编写生产级代码的人不得不放弃从数据科学书籍、课程和实验室中获得的实践,以改进他们的编程风格。实用的机器学习代码在许多方面与数据科学家的编程方法不同,但本文将从两个具有重大影响的常见问题开始:无用/令人困惑/模棱两可的变量名未命名的“魔法”科学研究(或Kaggle项目)和生产机器学习系统。是的,您可以在只运行一次代码的JupyterNotebook中解决这些问题,但是当任务关键型机器学习管道需要每天运行数百次而不会出错时,编写可读易懂的代码势在必行。幸运的是,数据科学家可以采用软件工程中的良好实践,本文也对此进行了介绍。注意:本文重点介绍Python,因为它是迄今为止工业数据科学中使用最广泛的语言。在Python中:变量名/函数名小写,并用下划线分隔命名常量的名称全部大写类名使用驼峰命名规则命名变量命名变量时要牢记三个基本原则:变量名必须描述变量的内容做信息表示。变量名应该用清楚的措辞来反映变量代表什么。阅读代码的次数多于编写代码的次数。因此,优先考虑代码的可读性而不是编写速度。使用标准命名约定允许做出一个全局决策而不是多个局部决策。实际情况如何?以下是对变量名称的一些改进:x和y。多读几遍就知道它们是特性和目标,但其他阅读代码的开发人员可能不清楚。相反,使用描述这些变量含义的名称,例如house_features和house_prices。价值。价值代表什么?它可以是velocity_mph、customers_served、efficiency、revenue_total。像value这样的名称不能反映变量的用途并且容易混淆。温度。即使您只是将变量用作临时值存储,也要给它起一个有意义的名称。这可能是用于转换单位的值,因此在这种情况下请指定:#Don'tdothistemp=get_house_price_in_usd(house_sqft,house_room_count)final_value=temp*usd_to_aud_conversion_rate#Dothisinsteadhouse_price_in_usd=get_house_price_in_usd(house_sqft,house_room_count)house_price_in_aud=house_price_d_in_aud如usd,aud,mph,kwh,sqft,请务必提前与其他团队成员就常用缩写达成一致,并书面记录。然后在代码审查中,确保执行这些书面标准。tp,tn,fp,fn:避免使用特定的机器学习缩写。这些值分别代表true_positives、true_negatives、false_positives和false_negatives,所以很清楚他们的意思。除了难以理解之外,较短的变量名也会造成打字错误。想打tn的时候,写tp比较容易,所以描述完整。上面的例子说明了需要优先考虑代码的可读性而不是代码的编写速度。与优质代码相比,阅读、理解、测试、修改和调试劣质代码需要更长的时间。一般来说,通过使用更短的变量名来更快地编写代码实际上会增加程序的开发时间!如果你不相信我,把你6个月前写的代码拿出来试着修改一下。如果您发现自己的代码难以理解,则表明您应该遵循更好的命名约定。xs和ys。这些值通常用于绘图,在这种情况下,值代表x_coordinates和y_coordinates。但是,这些名称也用于许多其他任务,因此可以通过使用描述变量用途的特定名称来避免混淆,例如时间和距离或温度和energy_in_kwh。BadVariableNames的原因命名变量的大部分问题来自:试图缩短变量名将公式直接转录成代码关于第一点,虽然像Fortran这样的语言确实限制了变量名的长度(6个字符以内),但是有现代编程语言没有限制,所以不要强迫自己使用缩写。也不要使用长变量名,但如果必须选择,请争取可读性。关于第二点,在写方程式或使用模型时——这是学校忘记强调的一点——记住字母或输入代表实际值!这是犯这两种错误以及如何改正错误的示例。假设有一个从模型中导出的多项式可以找到房屋的价格。开发者可能希望直接在代码中写数学公式:temp=m1*x1+m2*(x2**2)final=temp+b这段代码看起来像是机器为机器写的。虽然计算机最终会运行此代码,但人类会更频繁地阅读它,因此请编写人类可以理解的代码!要做到这一点,不必考虑公式本身——如何去做——而是要考虑建模的真实对象——是什么。下面是完整的等式(这很好地检验了读者是否理解模型):house_price=price_per_room*rooms+\price_per_floor_squared*(floors**2)house_pricehouse_price=house_price+expected_mean_house_price没有理解模型或代码。代码是为了解决实际问题而写的,所以你需要了解模型获取的目标。描述性变量名有助于在比公式更高的抽象层次上工作,并帮助开发人员专注于问题本身。其他注意事项命名变量时的一个重点是一致性计数。使用一致的变量名可以减少命名时间并增加解决问题的时间,尤其是在添加复合变量名时。1.Aggregationinvariablenames读者已经了解使用描述性名称的基本思想,将xs改为距离,e改为效率,v改为速度。那么应该使用什么变量名来计算平均速度呢?是average_velocity、velocity_mean还是velocity_average?以下步骤可以解决这个问题:首先,确定常用缩写:avg表示平均值,max表示最大值,std表示标准差等。确保所有团队成员都同意并记录下来。将缩写放在变量名的末尾。将最相关的信息(变量描述的实体)放在开头。按照这些规则,聚合变量可以命名为velocity_avg、distance_avg、velocity_min和distance_max。第2条可根据具体情况酌情选择。当变量表示项目的数量时,会出现一个棘手的问题。如果要使用building_num,它是指建筑物的总数,还是指特定建筑物的索引值?为避免歧义,使用building_count表示建筑物总数,使用building_index表示具体建筑物。这也适用于其他问题,例如item_count和item_index。item_count也可以替换为item_total。这种方法解决了歧义,并保持了在名称末尾添加复合名称的一致性。2.循环索引不幸的是,典型的循环变量变成了i、j、k。这可能是数据科学中大多数错误和混乱的原因。将无法描述的变量名与嵌套循环结合使用(我见过使用ii、jj甚至iii的嵌套循环),您会得到难以阅读、容易出错的代码。这可能有点争议,但我从不使用i或任何其他单个字母作为循环变量,而是选择描述迭代的内容,例如forbuilding_indexinrange(building_count):....或forrow_indexinrange(row_count):forcolumn_indexinrange(column_count):....这对于嵌套循环特别有用,在这种情况下无需记住i代表行还是列,或者获取与j和k混淆。更多的脑力应该花在如何创建最好的模型上,而不是数组索引的特定顺序上。(在Python中,如果不使用循环变量,应该使用下划线“_”作为占位符,这样就不会混淆是否使用了索引。)3.其他命名方式避免在变量名中使用数字避免拼写错误的单词避免歧义字符避免具有相似含义的变量名避免缩写避免听起来相似的变量名坚持易读性优先于方便性的原则。编程主要是与其他程序员交流,因此请适当考虑团队成员。不使用幻数幻数是指未命名的常量。它经常用于单位转换,当改变时间间隔或添加下标时:final_value=unconverted_value*1.61final_quantity=quantity/60valuevalue_with_offset=value+150(那些变量名太可怕了!)幻数会导致很多错误和混乱,因为:只有作者自己知道幻数的含义。要改变幻数的值,需要找到它出现的所有地方,然后手动输入新值定义一个转换函数来替换幻数。此函数将未转换的值和转换率作为参数。defconvert_usd_to_aud(price_in_usd,aud_to_usd_conversion_rate):price_in_aus=price_in_usd*usd_to_aud_conversion_rate如果你想在一个项目的很多函数中使用相同的转换率,你可以在某处定义一个命名常量。USD_TO_AUD_CONVERSION_RATE=1.61price_in_aud=price_in_usd*USD_TO_AUD_CONVERSION_RATE(在开始写这个项目之前,需要和其他组员约定usd代表美元,aud代表澳元。记住标准!)下面是另一个例子:#Conversionfunctionapproachdefget_revolution_count(minutes_elapsed,revolutions_per_minute):revolution_count=minutes_elapsed*revolutions_per_minute#NamedconstantapproachREVOLUTIONS_PER_MINUTE=60revolution_count=minutes_elapsed*REVOLUTIONS_PER_MINUT使用在某处定义的命名常量使得改写数值更加容易和一致。Ifthe转换率发生变化时,无需在每次发生时搜索整个代码库来更改其值,因为它只在一个地方定义。这也告知代码的读者常量的含义。如果参数名能够反映参数的内容,函数参数也是一个可行的方案。幻数缺陷的一个例子来自我在大学从事的一个研究项目。该项目需要访问每15分钟更新一次的能源数据。没有人认为这个数字可能会改变,因此该团队编写了一堆使用神奇数字15(或96,用于每日观察次数)的函数。这些函数工作正常,直到它开始以5分钟和1分钟的间隔获取数据。整个团队花了数周时间修改函数,以便他们接受时间间隔作为参数。即便如此,几个月来我还是遇到了很多使用幻数引起的错误。真实数据变化频繁,比如汇率每分钟都在变化。强制使用具体值进行编程意味着可能不得不花费大量时间重写代码和修复错误。在编程中没有“魔法”的位置,甚至在数据科学中也是如此。标准和约定的重要性使用标准的好处是它们可以帮助开发人员简单地做出全局决策而不是许多局部决策。与其在每次命名变量时都选择在何处声明,不如在项目开始时就做出决定,并在整个项目中始终如一地使用这些变量。这个想法是花更少的时间在命名、格式和样式等非核心数据科学问题上,而花更多的时间解决重要问题(例如使用机器学习来研究环境变化)。习惯于单独工作的开发人员可能会发现很难体会到采用标准的好处。然而,即使是单独工作,您也可以练习定义自己的规则并始终如一地使用它们。开发人员将能够做出更少的琐碎决定,这也为以后的团队开发工作做好了准备。任何需要多人参与的项目都需要标准。读者可能会质疑本文中的一些命名选择,这无关紧要。采用一套一致的标准比命名时专门使用空格或变量名的最大长度更重要。关键是停止在偶尔出现的难题上花费大量时间,而专注于解决不可避免的难题。结束语记住刚刚学过的内容,现在回到文章开头的代码:foriinrange(n):forjinrange(m):forkinrange(l):temp_value=X[i][j][k]*12.5new_array[i][j][k]=temp_value+150并用描述性变量名和符号常量对其进行修改。PIXEL_NORMALIZATION_FACTOR=12.5PIXEL_OFFSET_FACTOR=150forrow_indexinrange(row_count):forcolumn_indexinrange(column_count):forcolor_channel_indexinrange(color_channel_count):normalized_pixel_value=(original_pixel_array[row_index][column_index][color_channel_index]*PIXEL_NORMALIZATION_FACTOR)transformed_pixel_array[row_index][column_index][color_channel_index]=(normalized_pixel_value+PIXEL_OFFSET_FACTOR)现在可以看出,这段代码是在对数组中的像素值进行归一化处理,并添加一个偏移量来创建一个新的数组(忽略这种实现的低效率!)。当此代码交付给同事时,他们将能够阅读和修改它。此外,当开发人员返回此代码,尝试测试和修复错误时,他们将确切地知道他们在做什么。这个话题很无聊?也许有点无聊,但如果你花时间阅读软件工程的参考书目,你会发现优秀程序员和普通程序员的区别在于重复使用这些无聊的技能,比如好的变量名、短的工作周期、测试每一行代码、重构等。这就是将您的代码从实验室级别提升到工业生产级别所需要的。一旦你这样做了,你就会发现使用模型来改变现实生活中的决定是非常有趣的。本文讨论了一些改进变量名称的方法。要记住的所有要点:变量名应该描述它代表什么优先考虑可读性而不是可写性跨项目使用一致的标准,以尽量减少琐碎决策的识别困难。特别注意:使用描述性变量名使用函数参数或命名常量,而不是“幻数”不要使用特定的机器学习缩写使用变量名来描述计算或模型的含义将组合变量名放在最后使用item_count而不是num使用描述性循环索引代替i,j,k在整个项目中采用一致的命名和格式规则