Python基础:Python命名规范与命名习惯全掌握
Python基础Python命名规范与命名习惯全掌握一、开篇名字很重要编程中有两句经典名言“程序里最难的两件事缓存失效和命名。”“代码是写给人看的顺便给机器运行。”这两句话都指向同一个核心问题给变量、函数、类起好名字是编程中最重要也最难的事之一。 一个好的命名能让代码像阅读文章一样流畅一个糟糕的命名能让最简单的逻辑变得令人费解。这篇文章我会把Python中的命名规范和实战技巧完整地讲清楚。二、PEP 8命名规范全表PEP 8Python Enhancement Proposal 8是Python官方代码风格指南。下面是其中关于命名的完整规范命名对象规范示例变量名snake_casestudent_name,total_count函数名snake_casecalculate_average(),get_user()类名PascalCaseStudentRecord,FileManager常量名UPPER_SNAKE_CASEMAX_SIZE,DEFAULT_PORT模块名lower_snakedata_utils.py,user_models.py包名lowercasemypackage,utils异常类名PascalCase ErrorValueError,ConnectionError私有属性/方法_leading_underscore_internal_method(),_cache魔术方法double_underscore__init__(),__str__(),__len__()名称混淆__leading_double__private_attr触发名称改写2.1 各种命名风格的名称snake_case全小写单词用下划线连接# 变量、函数PascalCase每个单词首字母大写# 类名camelCase第一个单词小写后面首字母大写# Python不推荐UPPER_SNAKE_CASE全大写单词用下划线连接# 常量kebab-case全小写单词用连字符连接# Python不支持CSS/HTML用三、变量命名实战3.1 使用描述性的名字好的变量名应该能回答这个变量存的是什么# ✅ 好命名——自解释student_count42average_score85.5is_user_logged_inTruedefault_page_size20max_retry_attempts3upload_directory_path/data/uploadserror_message_template错误{code} - {message}# ❌ 差命名——需要猜测或查看上下文n42# n是什么的数量avg85.5# 什么的平均值flagTrue# 什么标志ps20# 什么的大小r3# 重试次数path/data# 什么路径msg错误...# 什么消息3.2 布尔变量的命名布尔变量True/False有几种推荐的命名方式# 用 is_ 前缀is_activeTrueis_adminFalseis_deletedFalse# 用 has_ 前缀has_permissionTruehas_childrenFalsehas_errorsTrue# 用 should_ / can_ / will_ 等情态动词should_updateTruecan_deleteFalsewill_expireTrue# 直接用形容词visibleTrueenabledFalsereadyTrueemptyFalse 布尔变量命名技巧读出来能构成一个自然的Yes/No问题。比如is_active读作是活跃的吗——回答是True或False。如果变量名叫active效果也一样。3.3 数字变量的命名# 计数类student_count42# 学生数量retry_count3# 重试次数# 度量类file_size_bytes1024# 文件大小字节timeout_seconds30# 超时时间秒distance_meters100# 距离米# 索引类current_index0# 当前索引start_position5# 起始位置page_number1# 页码# 比率类success_rate0.95# 成功率discount_percent0.15# 折扣百分比3.4 集合变量的命名# 列表/集合——用复数students[小明,小红,小刚]scores[85,92,78]active_usersset()# 字典——用描述性的名词student_scores{小明:85,小红:92}user_profiles{}# 用户资料config_settings{}# 配置设置# 用后缀提示类型在类型不明确时name_list[小明,小红]score_dict{math:95,english:88}user_setset()item_tuple(1,2,3)⚠️ 注意在变量名中加类型后缀如_list、_dict是有争议的。有些人认为这是代码坏味道——如果你的变量名需要加上类型后缀才能说清楚它是什么说明你需要更好的名字或者考虑使用类型注解。我个人建议优先使用不带后缀的描述性名称只有在确实容易混淆时才加后缀。四、函数命名实战4.1 函数名用动词开头函数做事情所以名字应该以动词开头# ✅ 好的函数名——动词开头一看就知道做什么defcalculate_average(numbers):passdefget_user_by_id(user_id):passdefsave_data_to_file(data,filepath):passdefsend_email_notification(recipient,message):passdefvalidate_user_input(input_string):passdefparse_json_response(response_text):passdefconvert_celsius_to_fahrenheit(celsius):pass# ❌ 不好的函数名defaverage(numbers):# 名词不知道是计算还是获取passdefuser(user_id):# 太模糊passdefdata_file(data,path):# 不知道要做什么pass4.2 常用的函数动词前缀前缀含义示例get_获取/读取数据get_user(),get_config()set_设置数据set_name(),set_timeout()create_创建新对象create_order(),create_file()delete_删除对象delete_user(),delete_record()update_更新数据update_profile(),update_status()calculate_计算结果calculate_total(),calculate_tax()validate_验证数据validate_email(),validate_input()parse_解析数据parse_json(),parse_url()convert_转换格式convert_to_json(),convert_units()check_检查状态check_connection(),check_permission()find_查找数据find_user(),find_duplicates()sort_排序sort_by_date(),sort_descending()filter_筛选数据filter_by_status(),filter_active()handle_处理事件handle_error(),handle_click()process_处理数据process_payment(),process_file()build_构造/构建build_url(),build_response()4.3 返回布尔值的函数# ✅ 用 is_ / has_ / can_ 前缀defis_valid_email(email):passdefhas_required_fields(data):passdefcan_user_access(user,resource):pass# 读起来像自然的Yes/No问题ifis_valid_email(user_input):register_user(user_input)五、类命名实战5.1 类名用名词类代表东西/概念用PascalCase命名# ✅ 好类名——名词PascalCaseclassStudent:学生passclassEmailSender:邮件发送器passclassDatabaseConnection:数据库连接passclassOrderManager:订单管理器passclassUserProfile:用户资料pass# ❌ 不好的类名classDoStudent:# 不要动词开头classemail_sender:# 不要蛇形命名classuser_profile:# 不要蛇形命名classA:# 太短了classProcessDataClass:# 不要后缀Class5.2 异常类命名# 异常类以Error结尾classValidationError(Exception):验证错误passclassDatabaseConnectionError(Exception):数据库连接错误passclassAuthenticationFailedError(Exception):认证失败错误passclassInvalidInputError(ValueError):无效输入错误pass六、常量和配置命名# 常量——全大写下划线PI3.141592653589793MAX_CONNECTIONS100DEFAULT_TIMEOUT_SECONDS30DATABASE_URLmysql://localhost:3306/mydbALLOWED_EXTENSIONS{.jpg,.png,.gif}# 配置类——用类来组织相关常量classConfig:应用程序配置DEBUGFalseSECRET_KEYyour-secret-key-hereDATABASE_URIsqlite:///app.dbMAX_CONTENT_LENGTH16*1024*1024# 16MBSESSION_COOKIE_NAMEsession_id# 使用枚举来定义一组相关常量fromenumimportEnumclassOrderStatus(Enum):PENDINGpendingCONFIRMEDconfirmedSHIPPEDshippedDELIVEREDdeliveredCANCELLEDcancelled七、下划线的五种含义下划线在Python命名中有特殊的含义。理解这些约定对于阅读和编写Python代码很重要。7.1 单前导下划线_name表示受保护的/内部使用的属性或方法。这是一个约定不是强制规则。classUser:def__init__(self,name):self.namename self._password_hashself._hash_password(default)def_hash_password(self,password):内部方法——不希望在类外部被调用importhashlibreturnhashlib.sha256(password.encode()).hexdigest()defcheck_password(self,password):公开方法——对外接口returnself._hash_password(password)self._password_hash从类外部仍然可以访问_password_hash和_hash_password()但按照约定你不应该这么做。userUser(小明)# 技术上可以访问但约定上不应该print(user._password_hash)# 能工作但不推荐7.2 单后置下划线name_当变量名和Python关键字冲突时在后面加一个下划线# class是关键字用作变量名时加后缀class_Computer Science 101type_选修id_12345list_[1,2,3]7.3 双前导下划线__name触发Python的名称改写name mangling机制用于避免子类中的命名冲突classParent:def__init__(self):self.public公开属性self._protected受保护属性self.__private私有属性名称改写classChild(Parent):def__init__(self):super().__init__()# 可以访问print(self.public)# 公开属性print(self._protected)# 受保护属性# print(self.__private) # AttributeError! 找不到# 实际上Python把它改名了可以通过改名后的名字访问# 但不要这么做print(self._Parent__private)# 输出私有属性名称改写cChild()Python把__private改写为_Parent__private前面加了类名这样父类和子类的同名双下划线属性就不会冲突。但这种用法在Python社区中并不普遍大多数情况下单下划线_protected就足够了。7.4 双前后下划线name这是Python内置的魔术方法magic methods。永远不要自己创造这样的名字只使用Python定义好的classMyClass:def__init__(self):# 构造方法passdef__str__(self):# 字符串表示returnMyClass实例def__len__(self):# len()调用return0def__add__(self,other):# 运算符returnself7.5 单独的下划线_有三种用法# 用法一表示不会用到的变量for_inrange(5):# 不需要用到循环变量print(循环中)# 用法二交互式环境中表示上一次表达式的结果123_*26# 用法三占位符name,_,city(小明,25,北京)# 年龄25我们不需要print(name,city)八、避免的命名坏习惯8.1 拼音命名# ❌ 不要用拼音xuesheng_mingzi小明shuliang100chaxun_yonghuTrue# ✅ 用英文student_name小明count100query_userTrue如果你觉得英文表达不出来宁可先用中文拼音加注释然后查词典找到正确的英文表达。不过说实话很多国内项目中文拼音也照样用主要看团队习惯。8.2 缩写不当# ❌ 过度缩写难以理解stu_nm小明cal_avg_scr85.5upd_usr_stsTrue# ✅ 完整拼写student_name小明calculate_average_score85.5update_user_statusTrue# 某些广为人知的缩写可以接受# OK——这些缩写大家都能理解num100# numbermsghello# messagecfgload_config()# configdbconnect_db()# databaseidx0# indextmp/tmp# temporary8.3 命名中包含类型信息过时的做法# ❌ 匈牙利命名法——Python不推荐strName小明# str前缀表示字符串iAge25# i前缀表示整数lstStudents[]# lst前缀表示列表bFlagTrue# b前缀表示布尔值# ✅ Python的推荐做法name小明age25students[]flagTrue# 如果确实需要标注类型用类型注解name:str小明age:int25students:list[str][]flag:boolTrue8.4 容易混淆的字符# 避免容易混淆的命名# ❌ 容易搞混的字母O和数字0字母l和数字1O0注意看区别# 字母O数字0l1注意看区别# 小写L数字1九、命名检查工具9.1 pylintpipinstallpylint# 检查一个文件的命名规范pylint my_script.pypylint会检查变量名是否符合命名规范并给出建议。9.2 flake8 pep8-namingpipinstallflake8 pep8-naming# 检查命名规范flake8 my_script.py9.3 IDE内置检查PyCharm和VS Code Pylance都会在你编写代码时实时检查命名规范不符合PEP 8的命名会有下划线提示。十、一个完整的命名示例下面用一个真实的例子展示好的命名如何让代码会说话fromdataclassesimportdataclassfromtypingimportOptional# 类型别名StudentIdstrScorefloatdataclassclassStudent:学生信息student_id:StudentId name:strage:intscores:list[Score]classGradeAnalyzer:成绩分析器——分析学生成绩并生成报告PASS_SCORE60.0EXCELLENT_SCORE90.0def__init__(self,students:list[Student]):self.studentsstudentsdefcalculate_average_score(self)-float:计算所有学生的平均成绩total_score0.0total_count0forstudentinself.students:ifstudent.scores:total_scoresum(student.scores)total_countlen(student.scores)iftotal_count0:return0.0returntotal_score/total_countdeffind_top_students(self,top_count:int3)-list[Student]:找出成绩最好的N个学生returnsorted(self.students,keylambdas:sum(s.scores)/len(s.scores)ifs.scoreselse0,reverseTrue)[:top_count]defget_pass_rate(self)-float:计算及格率passing_students[sforsinself.studentsifs.scoresand(sum(s.scores)/len(s.scores))self.PASS_SCORE]ifnotself.students:return0.0returnlen(passing_students)/len(self.students)*100defgenerate_grade_report(self)-str:生成成绩分析报告report_lines[]report_lines.append(*50)report_lines.append(学生成绩分析报告)report_lines.append(*50)report_lines.append(f学生总数{len(self.students)}人)report_lines.append(f平均成绩{self.calculate_average_score():.2f}分)report_lines.append(f及格率{self.get_pass_rate():.1f}%)report_lines.append(-*50)report_lines.append(Top 3 学生)forrank,studentinenumerate(self.find_top_students(3),1):avgsum(student.scores)/len(student.scores)ifstudent.scoreselse0report_lines.append(f{rank}.{student.name}-{avg:.2f}分)report_lines.append(*50)return\n.join(report_lines)# 使用示例if__name____main__:students[Student(001,小明,20,[85.0,92.0,78.0]),Student(002,小红,21,[95.0,88.0,91.0]),Student(003,小刚,19,[55.0,60.0,58.0]),Student(004,小丽,22,[72.0,68.0,75.0]),Student(005,小华,20,[98.0,96.0,94.0]),]analyzerGradeAnalyzer(students)reportanalyzer.generate_grade_report()print(report)✅ 看看这段代码——即使你没有逐行仔细分析逻辑也能大致知道它在做什么。这就是好的命名带来的力量代码本身就是最好的文档。十一、本篇小结 好的命名是程序员最重要的软技能之一。核心收获遵循PEP 8规范变量/函数snake_case类PascalCase常量UPPER_CASE名字要有描述性student_count远好于n函数用动词开头get_、set_、calculate_、validate_等布尔变量用is_/has_前缀让判断条件读起来像自然语言理解下划线约定_internal、__name_mangling、__magic__、_避免拼音、缩写、匈牙利命名法 每次写代码时花几秒钟想一个好名字。这个投入会在未来无论是你自己还是别人阅读代码时获得成百上千倍的回报。