Effective Python 学习笔记 4

Effective Python 学习笔记 4

尽量使用异常来表示特殊情况,而不要返回None

    _表示用不到的变量

要点

  1. 用None这个返回值来表示特殊意义的函数,很容易使调用者犯错,以为None和0及空字符串之类的值,在表达式里面都会评估为False
  2. 函数遇到特特殊情况时应该抛出异常,而不是返回None

了解如何在闭包里使用外围作用域中的变量

    '''
        eg. 有一份列表,其中元素都是数字,现在对其排序,要把出现在某个群组内的数字,放在群组外的那些数字之前。
    '''
    def sort_pri(values, group):
        def helper(x):
            if x in group:
                return (0, x)
            return (1, x)
        values.sort(key = helper)
        '''
            上述函数成立的原因:
                1. 函数是一等对象(first-class object)
                2. python支持闭包
                3. python中使用特殊的规则来比较两个元组。它首先比较各元组中下标为0的对应元素,如果相等,再比较下标为1的元素,如果还是想等,就继续依次比较。
        '''

    numbers = [8, 3, 1, 2, 5, 4, 7, 6]
    group = {2, 3, 5, 7}
    sort_pri(numbers, group)
    print(numbers)
    >>> [2, 3, 5, 7, 1, 4, 6, 8]

要点

  1. 对于定义在某作用域内的闭包来说,它可以引用这些作用域中的变量
  2. 使用默认方式对闭包内的变量赋值,不会影响外围作用域的同名变量
  3. 在python 3中,程序可以在闭包内用nonlocal语句来修饰某个名称,使该闭包能够修改外围作用域中的同名变量
  4. 除了简单的函数,尽量不要使用nonlocal语句

考虑使用生成器来改写直接返回列表的函数

    # eg.返回字符串中英文单词的首字母和其下标
    def index_words(text):
        result = []
        if text:
            result.append(0)
        for index, letter in enumerate(text):
            if letter == ' ':
                result.append(index + 1)
        return result

    words = 'I am python'
    re = index_words(words)
    print(re)
    >>> [0, 2, 5]

以上程序的问题:

  1. 代码拥挤,每次找到新的结果,都要调用append方法。而我们真正强调的不是对append的调用,而是该方法给列表中添加的那个值且函数首位都要对resut进行创建和返回
    # 生成器改写
    def index)words_iter(text):
        if text:
            yield 0
        for index, letter in enumerate(text):
            if letter == ' ':
                yield index + 1

        ### ···

        re = list(index_words_iter(address))
  1. index_words函数在它返回前,要把所有结果都放在列表中。如果数据量非常大,那么程序可能会耗尽内存。用生成器改写后,可以应对任意长度的输入数据

要点

  1. 使用时生成器比用list返回结果更加清晰
  2. 由生成器函数所返回的那个迭代器,可以把生成器函数体中,传给yield表达式的那些值,逐次生产出来
  3. 无论数据量多大,生成器都能产生一系列输出,不会对内存造成压力

在参数上面迭代时要多加小心

    细节见书本第17条

要点

  1. 如果参数是迭代器,那么可能会导致奇怪的行为并错失某些值
  2. python的迭代器协议,描述了容器和迭代器应该如何与iter和next内置函数、for循环及相关表达式相互配合
  3. iter方法时限为生成器,即可定义自己的容器类型
  4. 想判断某个值是迭代器还是容器,可以拿该值为参数,两侧调用iter函数,若结果相同,则是迭代器,调用内置的next函数,即可令该迭代器前进一步

用数量可变的位置参数减少视觉杂讯

    令函数接受可选位置参数(由于这种参数习惯上写为*args,所以又称为star args,星号参数),能够使代码更加清晰,并减少视觉杂讯(visual noise)。

    visual noise:一种比喻,意思是使代码看起来不要太过杂乱,以强调其中的重要内容。

出现的问题

  1. 变长参数在传给函数时,总是先转化为元组。这就意味着,如果用带有*操作符的生成器为参数,来调用这种参数,python必须把该生成器完整迭代一轮,并把所生成的每个值,都放入元组之中。这可能会消耗大量内存。所以只有当我们确定参数个数较少时,才采用这种写法
  2. 如果以后要给函数添加新的位置参数,那就必须修改原来调用该函数的那些旧代码

要点

  1. def语句中用*args,即可令函数接受数量可变的位置参数
  2. 调用函数时,可以采用*操作符,把序列中的元素当成位置参数,传给该函数
  3. 对生成器使用*操作符,可能导致内存耗尽
  4. 在已经接受*args参数的函数上继续添加位置参数,可能会产生难以排查的bug

用关键字参数来表达可选行为

    def func(arg1, arg2):
        return arg1 + arg2
    # 以下写法等效
    func(1, 1)
    func(arg1 = 1, 1)
    func(1, arg2 = 1)
    func(arg1 = 1, arg2 = 1)

关键字参数的好处

  1. 易于理解,参数含义与参数值都呈现在面前
  2. 可以在函数中提供默认值
  3. 它可以提供一种扩充函数参数的有效方式,使得扩充之后的函数依然能与原有的那些调用代码兼容

要点

  1. 函数参数可以按照位置或关键字来指定
  2. 只是用位置参数来调用函数,可能会导致这些参数数值含义不够明确,而关键字参数则能够阐明每个参数的意图
  3. 给函数添加新行为时,可以使用带默认值的关键字参数,以便与原有的函数点用代码保持兼容
  4. 可选的关键字参数,总是应该以关键字形式来指定,而不是以位置参数的形式来指定

参考资料

  1. python 闭包和装饰器详解
  2. Python3 List sort()方法
  3. 深入理解 Python yield

你可能感兴趣的:(Effective Python 学习笔记 4)