Python 小贴士(1)pythonic 思维

1. 查询当前使用的 python 版本:

在命令行中查询的方法是:

$ python --version
Python 3.9.0rc2
$ python -V
Python 3.9.0rc2

在执行代码过程中,也可以查询:

>>> print(sys.version_info)
sys.version_info(major=3, minor=9, micro=0, releaselevel='candidate', serial=2)
>>> print(sys.version)
3.9.0rc2 (tags/v3.9.0rc2:2bd31b5, Sep 17 2020, 00:37:38) [MSC v.1927 32 bit (Intel)]

2. PEP 8 风格指南:

空格相关:

  • 用空格表示缩进,而不要用制表符。
  • 和语法相关的每一层缩进都用 4 个空格表示。
  • 每行不超过 79 个字符。
  • 对于占据多行的长表达式来说,除了首行之外的其余各行都应该在通常的缩进级别之上再加 4 个空格。
  • 在同一个文件中,函数与类之间用两个空行隔开。
  • 在同一个类中,方法与方法之间用一个空行隔开。
  • 使用字典时,键与冒号之间不加空格,写在同一行的冒号和值之间应该加一个空格。
  • 给变量赋值时,赋值符号的左边和右边各加一个空格,并且只加一个空格。
  • 给变量的类型做注解时,不要把变量名和冒号隔开,但在类型信息前应该有一个空格。

命名相关:

  • 函数、变量及属性用小写字母来拼写,各单词之间用下划线相连。
  • 受保护的实例属性,用一个下划线开头。
  • 私有的实例属性,用两个下划线开头。
  • 类(包括异常)命名时,每个单词的首字母均大写。
  • 模块级别的常量,所有字母都大写,各单词之间用下划线相连。
  • 类中的实例方法,应该把第一个参数命名为 self,用来表示该对象本身。
  • 类方法的第一个参数,应该命名为 cls,用来表示这个类本身。

表达式和语句相关:

  • 采用行内否定,即把否定词直接写在要否定的内容前面,而不要放在整个表达式的前面。
  • 不要通过长度判断容器或序列是不是空的,而要使用 if not ... 这种格式,因为 Python 会把空值自动评估为 False。
  • 如果要判断容器或序列里面有没有内容,也不应该通过长度来判断,而是采用 if somelist 语句,因为 Python 会把非空的值自动判定为 True。
  • 不要把 if 语句、for 循环、while 循环及 except 复合语句挤在一行,应该分成多行来写,这样更加清晰。
  • 如果表达式一行写不下,可以用括号括起来,而且要适当地添加换行与缩进以便于阅读。
  • 多行的表达式,应该用括号括起来,而不是用 \ 符号续行。

引入相关:

  • import 语句总是放在文件开头。
  • 引入模块时,总是应该使用绝对名称,而不应该根据当前模块路径来使用相对名称。
  • 如果一定要用相对名称来编写 import 语句,那就应该明确地写成 from . import foo。
  • 文件中的 import 语句应该按顺序划分成三个部分:首先引入标准库里的模块,然后引入第三方模块,最后引入自己的模块。

3. bytes 与 str 的区别:

bytes 和 str 都可以表示字符序列。

bytes 实例包含的是原始数据,即 8 位的无符号值(通常按照 ASCII 编码标准来显示)。

>>> a = b'h\x65llo'
>>> print(list(a))
[104, 101, 108, 108, 111]
>>> print(a)
b'hello'

str 实例包含的是 Unicode 码点,这些码点与人类语言之中的文本字符相对应。 

>>> a = 'a\u0300 propos'
>>> print(list(a))
['a', '̀', ' ', 'p', 'r', 'o', 'p', 'o', 's']
>>> print(a)
à propos

要把 Unicode 数据转换成二进制数据,必须调用 str 的 encode 方法;要把二进制数据转换成 Unicode 数据,必须调用 bytes 的 decode 方法。

两个好用的辅助函数,在两种情况之间进行转换:

def to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes):
        value = bytes_or_str.decode('utf-8')
    else:
        value = bytes_or_str
    return value
def to_bytes(bytes_or_str):
    if isinstance(bytes_or_str, str):
        value = bytes_or_str.encode('utf-8')
    else:
        value = bytes_or_str
    return value

可以用 + 操作符将 bytes 添加到 bytes,str 也可以这样。但不能将 bytes 添加到 str,反之亦然。bytes 可以用二元操作符与 bytes 进行比较,str 与 str 之间也可以,但 bytes 与 str 不行。这两种实例不能在某些操作符(例如 >、==、+、%)上面混用。从文件中读取二进制数据(或写入二进制数据到文件),应该使用 'wb' 或 'rb' 这样的二进制模式打开文件。如果要从文件中读取的是 Unicode 数据,必须注意系统默认的文本编码方案,若无法肯定,可通过 encoding 参数明确指出。

4. f-string:

这种语法要求在格式字符串的前面加上字母 f 作为前缀。

>>> key = 'my_var'
>>> value = 1.234
>>> formatted = f'{key} = {value}'
>>> print(formatted)
my_var = 1.234

可以通过 ! 符号把值转化成 Unicode 及 repr 形式的字符串:

>>> formatted = f'{key!r:<10} = {value:.2f}'
>>> print(formatted)
'my_var'   = 1.23

5. 用辅助函数取代复杂的表达式:

 要把 URL 中的查询字符串拆分成键值对,只需要使用 parse_qs 函数就可以了:

>>> from urllib.parse import parse_qs
>>> my_values = parse_qs('red=5&blue=0&green=', keep_blank_values=True)
>>> print(repr(my_values))
{'red': ['5'], 'blue': ['0'], 'green': ['']}

可以发现,有的参数可能带有多个值,有的参数可能只有一个值,有的参数可能是空白值,也会遇到根本没提供这个参数的情况。如果能把参数缺失与参数为空白值这两种情况都默认当成 0 就更好了。

def get_first_int(values, key, default=0):
    found = values.get(key, [''])
    if found[0]:
        return int(found[0])
    return default

6. 把数据结构直接拆分到多个变量里,不要专门通过下标访问:

tuple 可以用不可变的序列把许多元素依次保存起来。

>>> snack_calories = {
	'chips': 140,
	'popcorn': 80,
	'nuts': 190,
	}
>>> items = tuple(snack_calories.items())
>>> print(items)
(('chips', 140), ('popcorn', 80), ('nuts', 190))

可以用整数作下标,通过下标访问元组里边对应的元素:

>>> item = ('Peanut butter', 'Jelly')
>>> first = item[0]
>>> second = item[1]
>>> print(first, 'and', second)
Peanut butter and Jelly

还可以使用一种更好的方法,叫作“拆分”。这种方法可以把元组中的元素分别赋给多个变量。

>>> first, second = item
>>> print(first, 'and', second)
Peanut butter and Jelly

通过拆分来赋值要比通过下标访问元组内的元素更清晰,而且这种写法所需的代码量通常比较少。赋值操作的左边除了可以罗列单个变量,也可以写成列表、序列或任意深度的可迭代对象(iterable)。也可以原地交换两个变量,而不需要创建临时变量。

def bubble_sort(a):
    for _ in range(len(a)):
        for i in range(1, len(a)):
            if a[i] < a[i-1]:
                a[i-1], a[i] = a[i], a[i-1]

拆分机制还有一个重要的用法,就是可以在 for 循环(或类似的结构)中把复杂的数据拆分到相关的变量之中:

>>> snacks = [('bacon', 350), ('donut', 240), ('muffin', 190)]
>>> for rank, (name, calories) in enumerate(snacks, 1):
	print(f'#{rank}: {name} has {calories} calories')

	
#1: bacon has 350 calories
#2: donut has 240 calories
#3: muffin has 190 calories

拆分机制可以用在许多方面,例如构建列表(13)、给函数设计参数列表(22)、传递关键字参数(23)、接受多个返回值(19)等。

7. 尽量用 enumerate 取代 range:

enumerate 是 Python 的 内置函数,它能把任意一种迭代器(iterator)封装成惰性生成器。这样每次循环的时候只需要从 iterator 里获取下一个值就行了,同时还会给出本轮循环的序号,即生成器每次产生的一对输出值。

>>> flavor_list = ['vanilla', 'chocolate', 'pecan', 'strawberry']
>>> it = enumerate(flavor_list)
>>> print(next(it))
(0, 'vanilla')
>>> print(next(it))
(1, 'chocolate')
>>> for i, flavor in enumerate(flavor_list):
	print(f'{i+1}: {flavor}')

	
1: vanilla
2: chocolate
3: pecan
4: strawberry
>>> for i, flavor in enumerate(flavor_list, 1):
	print(f'{i}: {flavor}')

	
1: vanilla
2: chocolate
3: pecan
4: strawberry

8. 用 zip 函数同时遍历两个迭代器: 

zip 函数可以把两个或更多的 iterator 封装成惰性生成器。每次循环时,它会分别从这些迭代器里获取各自的下一个元素,并把这些值放在一个元组里面。

longest_name = None
max_count = 0
for name, count in zip(names, counts):
	if count > max_count:
		longest_name = name
		max_count = count
>>> print(longest_name)
Cecilia
>>> print(max_count)
7

如果迭代器的长度不一致,只要其中任意一个迭代器处理完毕,就不再继续处理了。这时可以用 itertools.zip_longest 函数。

9. 不要在 for 和 while 循环后面写 else 块:

只有在整个循环没有因为 break 提前跳出的情况下,else 块才会执行。

10. 用赋值表达式减少重复代码:

使用海象表达式 :=

比如有几种水果要做成果汁,顾客点柠檬汁之前,要先确认现在还有没有柠檬可以榨汁,所以要先查出柠檬的数量,然后判断是不是非零的值:

if count := fresh_fruit.get('lemon', 0):
    make_lemonade(count)
else:
    out_of_stock()

如果是苹果汁,每杯果汁需要 4 颗苹果,可以这么写:

if (count := fresh_fruit.get('apple', 0)) >= 4:
    make_lemonade(count)
else:
    out_of_stock()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值