美文网首页
Effective Python 笔记摘录1

Effective Python 笔记摘录1

作者: KyoDante | 来源:发表于2022-04-15 21:08 被阅读0次
    • 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的编写方式。

    相关文章

      网友评论

          本文标题:Effective Python 笔记摘录1

          本文链接:https://www.haomeiwen.com/subject/mjlqertx.html