- Item1:确认python版本(由于2失去官方支持了,强制要求使用3):
python3 --version
或者
import sys
print(sys.version_info)
print(sys.version)
- Item2:使用PEP8的风格来写代码:
用空格来分隔,4个空格来分语法句
每行不应超过79个字符
长表达式应该分隔开语义便于理解
一个文件内,函数和类应该用两个空行分隔
一个类内,方法应该用一个空行分隔
字典中,key和:之间没有空格,:和value之间有一个空格(如果在同一行)
=号前后加一个空格
-
在明确变量的类型时,变量名与:没有空格,:和变量类型名之间有一个空格
命名: 函数和变量,用lowercase_underscore命名
受保护的实例变量,用_leading_underscore命名
__double_leading_underscore格式
模块级的常量应该用ALL_CAPS格式
-
实例函数用self作为第一个参数变量,类方法用cls作为第一个参数变量。
表达式 用行内否定句(if a is not b),不用肯定句的否定格式(if not a is b)。
不要通过长度为0(if len(somelist) == 0)来检查空的容器或序列(比如[]或者''),用if not somelist来假定空的列表为False
同理,if somelist隐式为True,对于非空的容器或序列([1]或者'hi')
-
多行表达式可以用括号和\来分隔
导入: 把import放在文件的最开始(from x import y)
绝对路径进行导入,比如从包bar导入,应该用from bar import foo,而不是import foo
如果一定要用相对路径,则from . import foo
按照标准库,第三方库,自己的库的顺序来组织import语句,同级别的库则按照字母顺序排列。
使用Pylint等静态分析器来辅助检查PEP8风格的代码编写
- Item3:bytes和str的区别
a = b'h\x65llo'
print(list(a))
print(a)
>>>
[104, 101, 108, 108, 111]
b'hello'
b = 'a\u0300 propos'
print(list(b))
print(b)
>>>
['a', 'ˋ', ' ', 'p', 'r', 'o', 'p', 'o', 's']
à propos
-
bytes没有对应的二进制编码,str没有对应的文本编码。要转换,需要通过str的encode方法或者bytes的decode方法。
-
统一编码为UTF-8的str。
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 # Instance of str
print(repr(to_str(b'foo')))
print(repr(to_str('bar')))
>>>
'foo'
'bar'
- 统一为8-bits的bytes
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 # Instance of bytes
print(repr(to_bytes(b'foo')))
print(repr(to_bytes('bar')))
>>>
b'foo'
b'bar'
- 同类型可以相加,不同类型不可以
print(b'one' + b'two')
print('one' + 'two')
>>>
b'onetwo'
onetwo
>>> b'one' + 'two'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't concat str to bytes
>>> 'one' + b'two'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: must be str, not bytes
- 同理,同类型的可以进行比较,不同则不可以
assert b'red' > b'blue'
assert 'red' > 'blue'
>>> assert 'red' > b'blue'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'str' and 'bytes'
>>> assert b'blue' < 'red'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'bytes' and 'str'
- 类型不同,比较永远为False,尽管文本内容相同
print(b'foo' == 'foo')
>>>
False
- %操作符对于字符串格式的占位影响(同类型可以,不同类型则情况不同:1)把str放到bytes里面,由于不知道二进制编码方式,所以报错;2)把bytes放到str里面,会把bytes以repr的调用结果放进去)
print(b'red %s' % b'blue')
print('red %s' % 'blue')
>>>
b'red blue'
red blue
print(b'red %s' % 'blue')
>>>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: %b requires a bytes-like object, or an object that implements __bytes__, not 'str'
print('red %s' % b'blue')
>>>
red b'blue'
- 写文件时,注意写入的格式(带b为传入bytes,不带则默认为str)
with open('data.bin', 'wb') as f:
f.write(b'\xf1\xf2\xf3\xf4\xf5')
with open('data.bin', 'w') as f:
f.write(b'\xf1\xf2\xf3\xf4\xf5')
>>>
Traceback ...
TypeError: write() argument must be str, not bytes
- 读文件时,同理,如下:
with open('data.bin', 'r') as f:
data = f.read()
>>>
Traceback ...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf1 in position 0: invalid continuation byte
with open('data.bin', 'rb') as f:
data = f.read()
assert data == b'\xf1\xf2\xf3\xf4\xf5'
- 查看默认的平台编码,最好应该显式指定编码的格式。(下面使用错误的编码格式,导致数据以另一种编码方式来解读;原本应该使用b来解读,这样就不需要指定encoding的方式。)(进一步可以使用help(open)来查看open这个函数的说明。)
import locale
print(locale.getpreferredencoding())
>>>
cp936
with open('data.bin', 'r', encoding='cp1252') as f:
data = f.read()
assert data == 'ñòóôõ'
-
Item4:用F字符串,而不用C风格的Format串和str.format
-
左边用预定义的文本模板,右边提供数值(比如用%d来解码二进制和十六进制数)
a = 0b10111011
b = 0xc5f
print('Binary is %d, hex is %d' % (a, b))
>>>
Binary is 187, hex is 3167
- Format串和str.format的各种例子(不足):
# 格式需要很好的指定,一旦指定错误,就会报错
key = 'my_var'
value = 1.234
formatted = '%-10s = %.2f' % (key, value)
print(formatted)
>>>
my_var = 1.23
reordered_tuple = '%-10s = %.2f' % (value, key)
>>>
Traceback ...
TypeError: must be real number, not str
reordered_string = '%.2f = %-10s' % (key, value)
>>>
Traceback ...
TypeError: must be real number, not str
# 多个串的时候,可读性和处理起来复杂
pantry = [
('avocados', 1.25),
('bananas', 2.5),
('cherries', 15),
]
for i, (item, count) in enumerate(pantry):
print('#%d: %-10s = %.2f' % (i, item, count))
>>>
#0: avocados = 1.25
#1: bananas = 2.50
#2: cherries = 15.00
# 当修改样式的时候,前后的格式需要对应,比如f对应浮点,d对应整型
for i, (item, count) in enumerate(pantry):
print('#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count)))
>>>
#1: Avocados = 1
#2: Bananas = 2
#3: Cherries = 15
# 这种断层的代码,可读性较差,需要前后跳跃来阅读,遇到bug也比较难找到。
template = '%s loves food. See %s cook.'
name = 'Max'
formatted = template % (name, name)
print(formatted)
>>>
Max loves food. See Max cook.
name = 'brad'
formatted = template % (name.title(), name.title())
print(formatted)
>>>
Brad loves food. See Brad cook.
# 通过字典来格式化,使得语句更加复杂。
key = 'my_var'
value = 1.234
old_way = '%-10s = %.2f' % (key, value)
new_way = '%(key)-10s = %(value).2f' % {
'key': key, 'value': value} # Original
reordered = '%(key)-10s = %(value).2f' % {
'value': value, 'key': key} # Swapped
assert old_way == new_way == reordered
name = 'Max'
template = '%s loves food. See %s cook.'
before = template % (name, name) # Tuple
template = '%(name)s loves food. See %(name)s cook.'
after = template % {'name': name} # Dictionary
assert before == after
for i, (item, count) in enumerate(pantry):
before = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
after = '#%(loop)d: %(item)-10s = %(count)d' % {
'loop': i + 1,
'item': item.title(),
'count': round(count),
}
assert before == after
# 直接传入字典,一定程度缓解了上面的问题
soup = 'lentil'
formatted = 'Today\'s soup is %(soup)s.' % {'soup': soup}
print(formatted)
>>>
Today's soup is lentil.
menu = {
'soup': 'lentil',
'oyster': 'kumamoto',
'special': 'schnitzel',
}
template = ('Today\'s soup is %(soup)s, '
'buy one get two %(oyster)s oysters, '
'and our special entrée is %(special)s.')
formatted = template % menu
print(formatted)
>>>
Today's soup is lentil, buy one get two kumamoto oysters, and our special entrée is schnitzel.
# format则是指定格式,在简单语句上比较直观。
a = 1234.5678
formatted = format(a, ',.2f') # 千分位以“,”分隔
print(formatted)
b = 'my string'
formatted = format(b, '^20s') # “^”指定居中的位数
print('*', formatted, '*')
>>>
1,234.57
* my string *
key = 'my_var'
value = 1.234
formatted = '{} = {}'.format(key, value)
print(formatted)
>>>
my_var = 1.234
formatted = '{:<10} = {:.2f}'.format(key, value)
print(formatted)
>>>
my_var = 1.23
print('%.2f%%' % 12.5)
print('{} replaces {{}}'.format(1.23))
>>>
12.50%
1.23 replaces {}
formatted = '{1} = {0}'.format(key, value)
print(formatted)
>>>
1.234 = my_var
formatted = '{0} loves food. See {0} cook.'.format(name)
print(formatted)
>>>
Max loves food. See Max cook.
# 但是一旦句式复杂,依然影响了可读性。
for i, (item, count) in enumerate(pantry):
old_style = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
new_style = '#{}: {:<10s} = {}'.format(
i + 1,
item.title(),
round(count))
assert old_style == new_style
#
formatted = 'First letter is {menu[oyster][0]!r}'.format(
menu=menu)
print(formatted)
>>>
First letter is 'k'
old_template = (
'Today\'s soup is %(soup)s, '
'buy one get two %(oyster)s oysters, '
'and our special entrée is %(special)s.')
old_formatted = template % {
'soup': 'lentil',
'oyster': 'kumamoto',
'special': 'schnitzel',
}
new_template = (
'Today\'s soup is {soup}, '
'buy one get two {oyster} oysters, '
'and our special entrée is {special}.')
new_formatted = new_template.format(
soup='lentil',
oyster='kumamoto',
special='schnitzel',
)
assert old_formatted == new_formatted
- 解释的格式化串(interpolated format strings, f-strings)登场
key = 'my_var'
value = 1.234
formatted = f'{key} = {value}'
print(formatted)
>>>
my_var = 1.234
# 也可以用上前面的格式指定
formatted = f'{key!r:<10} = {value:.2f}'
print(formatted)
>>>
'my_var' = 1.23
# 各种方式的对比,f字符串简要(terseness),可读性强。
f_string = f'{key:<10} = {value:.2f}'
c_tuple = '%-10s = %.2f' % (key, value)
str_args = '{:<10} = {:.2f}'.format(key, value)
str_kw = '{key:<10} = {value:.2f}'.format(key=key,
value=value)
c_dict = '%(key)-10s = %(value).2f' % {'key': key,
'value': value}
assert c_tuple == c_dict == f_string
assert str_args == str_kw == f_string
# 之前的例子改造为f串,显然可读性比前两者要好。
for i, (item, count) in enumerate(pantry):
old_style = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
new_style = '#{}: {:<10s} = {}'.format(
i + 1,
item.title(),
round(count))
f_string = f'#{i+1}: {item.title():<10s} = {round(count)}'
assert old_style == new_style == f_string
# 或者也可以拆分成多个f字符串,增强单行可读性
for i, (item, count) in enumerate(pantry):
print(f'#{i+1}: '
f'{item.title():<10s} = '
f'{round(count)}')
>>>
#1: Avocados = 1
#2: Bananas = 2
#3: Cherries = 1
# 由于可以运行表达式,指定的格式也可以以变量形式嵌入,而不用写死。
places = 3
number = 1.23456
print(f'My number is {number:.{places}f}')
>>>
My number is 1.235
以上是Item1到Item4的摘录,主要以代码例子为主。后续依然以代码摘录为主,体会pythonic的编写方式。
网友评论