[Python工匠]输出① 变量与注释

news2024/11/26 22:49:30

变量(variable)是用来从内存找到某个东西的标记 

#去掉s两边的空格,再处理
value = process(s.strip())
s = " hello World   "
value1 = len(s)
value2 = len(s.strip())

print(value1)
print(value2)
#用户输入可能会有空格,使用strip()去掉空格
username = extract_username(input_string.strip())

user_input = input("What's your name ? ").strip()
print(input("What's your name ? "))
print(user_input)

1.1 基础知识

 1.1.1 变量常见用法

>>
>>> author = 'Maxwell'
>>> print('Hello, {}!'.format(author))
Hello, Maxwell!
>>>

在一行语句里同时操作多个变量,比如调换两个变量所指向的值 

>>>
>>> author,reader = 'Maxwell','Ray'
>>> author,reader = reader,author
>>> author
'Ray'
>>>

01.变量解包

变量解包(unpacking)是Python里的一种特殊赋值操作,允许我们把一个可迭代对象(比如列表)的所有成员,一次性赋值给多个变量:

# 注意: 左侧变量的个数必须和待展开的列表长度相等,否则会报错。

>>>
>>> usernames = ['Maxwell', 'Ray']
>>> author, reader = usernames
>>> author
'Maxwell'
>>>

假如在赋值语句左侧添加小括号(...),甚至可以一次展开多层嵌套数据: 

>>>
>>> attrs = [1, ['maxwell', 100]]
>>> user_id, (username, score) = attrs
>>> user_id
1
>>> username
'maxwell'
>>>

Python还支持更灵活的动态解包语法。只要用星号表达式(*variables)作为变量名,它便会贪婪[插图]地捕获多个值对象,并将捕获到的内容作为列表赋值给variables。

>>>
>>> data = ['Maxwell', 'apple','orange','banana', 100]
>>> username, *fruits, score = data
>>> username
'Maxwell'
>>> fruits
['apple', 'orange', 'banana']
>>> score
100
>>>

和常规的切片赋值语句比起来,动态解包语法要直观许多:

#1. 动态解包
>>> username, *fruits, score = data
# 2. 切片赋值
>>> username, fruits, score = data[0], data[1:-1], data[-1]
# 两种变量赋值方式完全等价
>>>
>>>
>>> for username, score in [('Maxwell',100), ('Ray', 60)]:
...     print(username)
...
Maxwell
Ray
>>>

 02.单下划线变量名_

在常用的诸多变量名中,单下划线_是比较特殊的一个。它常作为一个无意义的占位符出现在赋值语句中。_这个名字本身没什么特别之处,这算是大家约定俗成的一种用法。

举个例子,假如你想在解包赋值时忽略某些变量,就可以使用_作为变量名

#忽略展开时的第二个变量
>>> author, _ = usernames
# 忽略第一个和最后一个变量之间的所有变量
>>> username, *_, score = data

 而在Python交互式命令行(直接执行python命令进入的交互环境)里,_变量还有一层特殊含义——默认保存我们输入的上个表达式的返回值:

>>>
>>> 'foo'.upper()
'FOO'
>>> print(_)
FOO
>>>

1.1.2 给变量注明类型

为了解决动态类型带来的可读性问题,最常见的办法就是在函数文档(docstring)里做文章。我们可以把每个函数参数的类型与说明全都写在函数文档里。

def remove_invalid(items):
    """剔除 items 里面无效的元素

    :param items: 待剔除对象
    :type items: 包含整数的列表,[int, ...]
    """

下面是给remove_invalid()函数添加类型注解后的样子:

from typing import List

def remove_invalid(items: List[int]): ➊
    """剔除 items 里面无效的元素"""
    ... ...

❶List表示参数为列表类型,[int]表示里面的成员是整型

强烈建议在多人参与的中大型Python项目里,至少使用一种类型注解方案——Sphinx格式文档或官方类型注解都行。能直接看到变量类型的代码,总是会让人更安心。

1.1.3 变量命名原则

计算机科学领域只有两件难事:缓存失效和命名。——Phil Karlton

01.遵循PEP 8原则

给变量起名主要有两种流派:一是通过大小写界定单词的驼峰命名派CamelCase,二是通过下划线连接的蛇形命名派snake_case。这两种流派没有明显的优劣之分,似乎与个人喜好有关

ython制定了官方的编码风格指南:PEP 8。这份风格指南里有许多详细的风格建议,比如应该用4个空格缩进,每行不超过79个字符,等等。其中,当然也包含变量的命名规范:

· 对于普通变量,使用蛇形命名法,比如max_value;

· 对于常量,采用全大写字母,使用下划线连接,比如MAX_VALUE;

· 如果变量标记为“仅内部使用”,为其增加下划线前缀,比如_local_var;

· 当名字与Python关键字冲突时,在变量末尾追加下划线,比如class_。

除变量名以外,PEP 8中还有许多其他命名规范,比如类名应该使用驼峰风格(FooClass)、函数应该使用蛇形风格(bar_function),等等。给变量起名的第一条原则,就是一定要在格式上遵循以上规范。

PEP 8是Python编码风格的事实标准。“代码符合PEP 8规范”应该作为对Python程序员的基本要求之一。

02.描述性要强

#描述性弱的名字:看不懂在做什么
value = process(s.strip())
# 描述性强的名字:尝试从用户输入里解析出一个用户名
username = extract_username(input_string.strip())

表1-1 描述性弱和描述性强的变量名示例

 

03.要尽量短

中诀窍在于:为变量命名要结合代码情境和上下文。比如在上面的代码里,upgrade_to_level3(user)函数已经通过自己的名称、文档表明了其目的,那在函数内部,我们完全可以把how_many_points_needed_for_user_level3直接删减成level3_points。

04.要匹配类型

匹配布尔值类型的变量名

布尔值(bool)是一种很简单的类型,它只有两个可能的值:“是”(True)或“不是”(False)。

表1-2 布尔值变量名示例

 匹配int/float类型的变量名

自然就会认定它们是int或float类型。这些名字可简单分为以下几种常见类型:

  • 释义为数字的所有单词,比如port(端口号)、age(年龄)、radius(半径)等;
  • 使用以_id结尾的单词,比如user_id、host_id;
  • 使用以length/count开头或者结尾的单词,比如length_of_username、max_length、users_count。

匹配其他类型的变量名

字符串(str)、列表(list)、字典(dict)

05.超短命名

在众多变量名里,有一类非常特别,那就是只有一两个字母的短名字。这些短名字一般可分为两类,一类是那些大家约定俗成的短名字,比如:

· 数组索引三剑客i、j、k

· 某个整数n

· 某个字符串s

· 某个异常e

· 文件对象fp

其他技巧

除了上面这些规则外,下面再分享几个给变量命名的小技巧:

· 在同一段代码内,不要出现多个相似的变量名,比如同时使用users、users1、users3这种序列;

· 可以尝试换词来简化复合变量名,比如用is_special来代替is_not_normal;

· 如果你苦思冥想都想不出一个合适的名字,请打开GitHub[插图],到其他人的开源项目里找找灵感吧!

1.1.4 注释基础知识

注释(comment)是代码非常重要的组成部分。通常来说,注释泛指那些不影响代码实际行为的文字,它们主要起额外说明作用。

 Python里的注释主要分为两种,一种是最常见的代码内注释,通过在行首输入#号来表示:

#用户输入可能会有空格,使用strip去掉空格

username = extract_username(input_string.strip())

另一种注释则是我们前面看到过的函数(类)文档(docstring),这些文档也称接口注释(interface comment)。

class Person:
    """人

    :param name: 姓名
    :param age: 年龄
    :param favorite_color: 最喜欢的颜色
    """

    def __init__(self, name, age, favorite_color):
        self.name = name
        self.age = age
        self.favorite_color = favorite_color

接口注释有好几种流行的风格,比如Sphinx文档风格、Google风格等,其中Sphinx文档风格目前应用得最为广泛。上面的Person类的接口注释就属于Sphinx文档风格。

编程新手们常常会犯同类型的错误,以下是我整理的最常见的3种。

01.用注释屏蔽代码

#源码里有大段大段暂时不需要执行的代码
# trip = get_trip(request)
# trip.refresh()
# ... ...

对于不再需要的代码,我们应该直接把它们删掉,而不是注释掉。

02.用注释复述代码

在编写注释时,新手常犯的另一类错误是用注释复述代码。

#调用strip()去掉空格
input_string = input_string.strip()

指引性注释。这种注释并不直接复述代码,而是简明扼要地概括代码功能,起到“代码导读”的作用。

以下代码里的注释就属于指引性注释:

#初始化访问服务的client对象

token = token_service.get_token()

service_client = ServiceClient(token=token)

service_client.ready()

# 调用服务获取数据,然后进行过滤

data = service_client.fetch_full_data()

for item in data:

     if item.value > SOME_VALUE:

         ...

指引性注释并不提供代码里读不到的东西——假如没有注释,耐心读完所有代码,你也能知道代码做了什么事儿。指引性注释的主要作用是降低代码的认知成本,让我们能更容易理解代码的意图。

03.弄错接口注释的受众

接口文档主要是给函数(或类)的使用者看的,它最主要的存在价值,是让人们不用逐行阅读函数代码,也能很快通过文档知道该如何使用这个函数,以及在使用时有什么注意事项。

对于上面的resize_image()函数来说,文档里提供以下内容就足够了:

def resize_image(image, size):
    """将图片缩放到指定尺寸,并返回新的图片。
    注意:当文件超过 5MB 时,请使用resize_big_image()
    :param image: 图片文件对象
    :param size: 包含宽高的元组:(width, height)
    :return: 新图片对象
    """

 1.3 编程建议

1.3.1 保持变量一致性

在foo()函数的作用域内,users变量被使用了两次:第一次指向字典,第二次则变成了列表。虽然Python的类型系统允许我们这么做,但这样做其实有很多坏处,比如变量的辨识度会因此降低,还很容易引入bug。

建议在这种情况下启用一个新变量:

def foo():
    users = {'data': ['piglei', 'raymond']}
    ...
    # 使用一个新名字
    user_list = []
    ...

1.3.2 变量定义尽量靠近使用

def generate_trip_png(trip):
    """
    根据旅途数据生成 PNG 图片
    """
    # 预先定义好所有的局部变量
    waypoints = []
    photo_markers, text_markers = [], []
    marker_count = 0

    # 开始初始化 waypoints 数据
    waypoints.append(...)
    ...
    # 经过几行代码后,开始处理 photo_markers、text_markers
    photo_markers.append(...)
    ...
    # 经过更多代码后,开始计算 marker_count
    marker_count += ...

    # 拼接图片:已省略……

1.3.3 定义临时变量提升可读性

#为所有性别为女或者级别大于3的活跃用户发放10000个金币
user_is_eligible = user.is_active and (user.sex == 'female' or user.level > 3)

if user_is_eligible:
    user.add_coins(10000)
    return

1.3.4 同一作用域内不要有太多变量

代码清单1-3 局部变量过多的函数

def import_users_from_file(fp):
    """尝试从文件对象读取用户,然后导入数据库

    :param fp: 可读文件对象
    :return: 成功与失败的数量
    """
    # 初始化变量:重复用户、黑名单用户、正常用户
    duplicated_users, banned_users, normal_users = [], [], []
    for line in fp:
        parsed_user = parse_user(line)
        # …… 进行判断处理,修改前面定义的{X}_users 变量

    succeeded_count, failed_count = 0, 0
    # …… 读取 {X}_users 变量,写入数据库并修改成功与失败的数量
    return succeeded_count, failed_count

代码清单1-4 对局部变量分组并建模

class ImportedSummary:
    """保存导入结果摘要的数据类"""

    def __init__(self):
        self.succeeded_count = 0
        self.failed_count = 0

class ImportingUserGroup:
    """用于暂存用户导入处理的数据类"""

    def __init__(self):
        self.duplicated = []
        self.banned = []
        self.normal = []

def import_users_from_file(fp):
    """尝试从文件对象读取用户,然后导入数据库  

    :param fp: 可读文件对象
    :return: 成功与失败的数量
    """
    importing_user_group = ImportingUserGroup()
    for line in fp:
        parsed_user = parse_user(line)
        # …… 进行判断处理,修改上面定义的importing_user_group 变量

    summary = ImportedSummary()
    # …… 读取 importing_user_group,写入数据库并修改成功与失败的数量

    return summary.succeeded_count, summary.failed_count

1.3.5 能不定义变量就别定义

定义临时变量可以提高代码的可读性。但有时,把不必要的东西赋值为临时变量,反而会让代码显得啰唆:

def get_best_trip_by_user_id(user_id):
    # 心理活动:嗯,这个值未来说不定会修改/二次使用,我们先把它定义成变量吧!
    user = get_user(user_id)
    trip = get_best_trip(user_id)
    result = {
        'user': user,
        'trip': trip
    }
    return result

上面这段代码里的三个临时变量完全可以去掉,变成下面这样:

def get_best_trip_by_user_id(user_id):
    return {
        'user': get_user(user_id),
        'trip': get_best_trip(user_id)
    }

1.3.6 不要使用locals()

locals()是Python的一个内置函数,调用它会返回当前作用域中的所有局部变量:

def foo():
    name = 'piglei'
    bar = 1
    print(locals())

# 调用foo() 将输出:
{'name': 'piglei', 'bar': 1}

Python之禅:显式优于隐式

"Python之禅"中有一句“Explicit is better than implicit”(显式优于隐式)

1.3.7 空行也是一种“注释”

代码里的注释不只是那些常规的描述性语句,有时候,没有一个字符的空行,也算得上一种特殊的“注释”。

1.3.8 先写注释,后写代码

每个函数的名称与接口注释(也就是docstring),其实是一种比函数内部代码更为抽象的东西。你需要在函数名和短短几行注释里,把函数内代码所做的事情,高度浓缩地表达清楚。

在写出一句有说服力的接口注释前,别写任何函数代码。

1.4 总结

以下是本章要点知识总结。

(1)变量和注释决定“第一印象”
· 变量和注释是代码里最接近自然语言的东西,它们的可读性非常重要
· 即使是实现同一个算法,变量和注释不一样,给人的感觉也会截然不同

(2)基础知识·
Python的变量赋值语法非常灵活,可以使用*variables星号表达式灵活赋值
· 编写注释的两个要点:不要用来屏蔽代码,而是用来解释“为什么”
· 接口注释是为使用者而写,因此应该简明扼要地描述函数职责,而不必包含太多内部细节· 可以用Sphinx格式文档或类型注解给变量标明类型
(3)变量名字很重要

· 给变量起名要遵循PEP 8原则,代码的其他部分也同样如此
· 尽量给变量起描述性强的名字,但评价描述性也需要结合场景
· 在保证描述性的前提下,变量名要尽量短
· 变量名要匹配它所表达的类型
· 可以使用一两个字母的超短名字,但注意不要过度使用
(4)代码组织技巧
· 按照代码的职责来组织代码:让变量定义靠近使用
· 适当定义临时变量可以提升代码的可读性
· 不必要的变量会让代码显得冗长、啰唆
· 同一个作用域内不要有太多变量,解决办法:提炼数据类、拆分函数
· 空行也是一种特殊的“注释”,适当的空行可以让代码更易读
(5)代码可维护性技巧
· 保持变量在两个方面的一致性:名字一致性与类型一致性
· 显式优于隐式:不要使用locals()批量获取变量
· 把接口注释当成一种函数设计工具:先写注释,后写代码
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/423800.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【云原生】k8s集群命令行工具kubectl之应用部署命令详解

kubectl应用部署命令详解一、准备工作1.1、Replication Controller1.2、Deployment1.3、DaemonSet1.4、查看创建的svc和pod1.5、kubectl 命令自动补全设置二、应用部署命令2.1、diff2.2、apply2.3、replace2.4、rollout2.4.1、history2.4.2、pause2.4.3、resume2.4.4、restart2…

Ubuntu20.04安装matlab2022b

Ubuntu20.04安装matlab2022b 1.系统要求 Ubuntu 22.04 LTSUbuntu 20.04 LTSUbuntu 18.04 LTSDebian 11Debian 10Red Hat Enterprise Linux 9Red Hat Enterprise Linux 8 (minimum 8.4)Red Hat Enterprise Linux 7 (minimum 7.9)SUSE Linux Enterprise Desktop 15SUSE Linux E…

【攻城狮计划】Renesas RA2E1 详细介绍

🚩WRITE IN FRONT🚩 🔎介绍:"謓泽"正在路上朝着"攻城狮"方向"前进四"🔎🏅荣誉:2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2022博客之星TOP10…

A40i使用笔记:QT使用alsa采集音频pcm信息

一、前言 目的是实现一种效果,就是在音频识别时,能够实时显示当前人员说话的声音幅度,通过波形曲线的形式显示出来。如下效果(非我实现) 还可以实现在实时采集的同时,将需要的数据保存成指定格式文件&…

状态设计模式(State Pattern)[论点:概念、相关角色、图示、示例代码、框架中的运用、适用场景]

文章目录概念组成角色相关图示代码示例框架中的应用适用场景概念 状态模式(State Pattern)是一种行为型设计模式,用于解决对象在不同状态下的行为问题。它允许一个对象在其内部状态改变时改变它的行为。状态模式主要包含三个部分:…

Day924.自动化测试 -系统重构实战

自动化测试 Hi,我是阿昌,今天学习记录的是关于自动化测试的内容。 自动化测试是一个很容易产生“争议”的话题,也经常会有一些很有意思的问题。 自动化测试不是应该由测试同学来编写吗,开发是不是没有必要学吧?之前…

圆角升级啦,来手把手一起实现自定义ViewGroup的各种圆角与背景

作者:newki 定制圆角与背景的自定义ViewGroup实现 前言 目前线上的一些第三方圆角容器大部分都只支持四周固定圆角,我们一些使用场景只需要顶部圆角,或者底部圆角,或者一个角圆角。 (话说为什么我们的UI这么喜欢各种…

webpack plugin源码解析(四) HashedModuleIdsPlugin

文章目录作用涉及 webpack API获取chunkGraph获取当前编译过程中被使用过的 module id:compilation.usedModuleIds获取当前编译过程中所有的模块对象:compilation.modules判断 module 是否需要生成 id:module.needId获取指定module 的 module…

新品首发丨计讯物联智慧灯杆TG473-A6-5“滴水不进”,严苛环境下稳定运行无压力

近年来,智慧灯杆产业快速发展,已广泛应用于智慧城市、智慧交通、智慧园区、智慧停车等细分场景,提质增效、节本降耗的作用突显。值得注意的是,不同的场景有着不同的环境条件,如高低温、潮湿、沙尘等,对智慧…

Nginx学习(9)—— 负载均衡模块

文章目录Nginx负载均衡模块负载均衡配置指令钩子初始化配置初始化请求peer.get和peer.free回调函数小结Nginx负载均衡模块 负载均衡模块用于从”upstream”指令定义的后端主机列表中选取一台主机。nginx先使用负载均衡模块找到一台主机,再使用upstream模块实现与这…

应急响应 - Windows进程分析,Windows网络分析,tasklist,wmic process

「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 进程网络排查一、Tasklist1、tasklist /v1、tasklist /svc2、tasklist /m二、wmic proces…

JAVAWeb02-CSS

1. CSS CSS 指的是层叠样式表 (Cascading Style Sheets) 1.1 概述 1.1.1 官方文档 地址: https://www.w3school.com.cn/css/index.asp 1.1.2 为什么需要 CSS 在没有 CSS 之前,我们想要修改 HTML 元素的样式需要为每个 HTML 元素单独定义样式属性,费心…

nodegui搭建/你好/打包

0、github连接问题 警告:如果你的网络有任何有任何有任何有任何有任何有任何有任何有任何有任何有任何连接 github 的问题,彻底放弃该框架 请转到其他框架 electron-egg教程、electron-egg官网,或其他electron项目 Tauri教程、Tauri官网 NW.…

Smartbi电子表格软件架构与差异化特色

选择和Excel结合Smartbi电子表格软件选择与Excel结合的原因在于,Excel一直被模仿,从未被超越。虽然市场上的报表软件很多,但存在太多的不完美。国外的产品功能复杂、难于学习(控件方式),做不了中国式复杂格…

SpringBoot使用Redis实现分布式缓存

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…

Matlab群体智能优化算法之巨型睡莲优化算法(VAO)

Matlab群体智能优化算法之巨型睡莲优化算法(VAO) 摘要:介绍一种新型智能优化算法,巨型睡莲优化算法。其应用于24个基准测试函数,并与其他10个著名算法进行了比较。提出的算法在10个优化问题上进行了测试:最小生成树、枢纽位置分配…

技术复盘(4)--docker

技术复盘--docker资料地址环境初始docker安装dockerdocker常用命令-都需要先启动dockerdocker容器打包为镜像docker坑docker卸载docker数据卷dockerFile构建过程搭建私有仓库--非图形化界面搭建私有仓库--图形化界面docker理论知识资料地址 docker官网:https://www…

一种供水系统物联网监测系统

1.1供水系统 1.1.1监测范围选择依据 (1)管网老化区域管网 管网建设年代久远,通常管网发生破损问题较大,根据管网本身属性和历史发生事件的统计分析,结合数理统计,优先选择管网老化区域的管段所在区域进行…

基于imx8m plus开发板全体系开发教程4:Linux系统开发

前言: i.MX8M Plus 开发板是一款拥有 4 个 Cortex-A53 核心,运行频率 1.8GHz;1 个 Cortex-M7 核心,运行频率 800MHz;此外还集成了一个 2.3 TOPS 的 NPU,大大加速机器学习推理。 全文所使用的开发平台均为与NXP官方合作的FS-IMX8…

深入浅出Kafka

这个主题 武哥漫谈IT ,作者骆俊武 讲得更好 一、什么是Kafka? 首先我们得去官网看看是怎么介绍Kafka的: https://kafka.apache.org/intro Apache Kafka is an open-source distributed event streaming platform. 翻译成中文就是&#xff1…