06-抽象

懒惰是一种美德

抽象和结构

  1. 抽象可节省人力,但实际上还有个更重要的优点:抽象是程序能够被人理解的关键所在。

自定义函数

  1. 函数执行特定的操作并返回一个值,你可以调用它
  2. 调用时可能需要提供一些参数——放在圆括号中的内容
  3. 要判断某个对象是否可调用,可使用内置函数callable
import math
x = 1
y = math.sqrt
callable(x)
# False
callable(y)
# True

给函数编写文档

  1. 放在函数开头的字符串称为文档字符串(docstring)
def square(x):
    '请测试'
    return x * x
square.__doc__
  1. 特殊的内置函数help很有用
  2. 在交互式解释器中,可使用它获取有关函数的信息,其中包含函数的文档字符串。

其实并不是函数的函数

  1. 所有的函数都返回值。如果你没有告诉它们该返回什么,将返回None。

参数魔法

  1. 在def语句中,位于函数名后面的变量通常称为形参,而调用函数时提供的值称为实参.
  2. 在函数内部给参数赋值对外部没有任何影响。
  3. 如果参数是可变的数据结构,函数内部修改后可能会影响外部
names = ['Mrs. Entity', 'Mrs. Thing']
n = names[:]
# 现在n和names包含两个相等但不同的列表

关键字参数和默认值

  1. 关键字参数最大的优点在于,可以指定默认值。

收集参数

def print_params(*params):
    print(params)
# 星号将提供的所有值都放在一个元组中
  1. 与赋值时一样,带星号的参数也可放在其他位置(而不是最后),但不同的是,在这种情况与赋值时一样,带星号的参数也可放在其他位置(而不是最后),但不同的是,在这种情况下你需要做些额外的工作:使用名称来指定后续参数。
  2. 星号不会收集关键字参数。
  3. 要收集关键字参数,可使用两个星号。
def print_params_3(**params):
    print(params)

print_params_3(x=1, y=2, z=3)
# {'z':3, 'x':1, 'y':2}

分配参数

def add(x, y):
    return x + y
params = (1, 2)
add(*params)
# 3
  1. 使用这些拆分运算符来传递参数很有用,因为这样无需操心参数个数之类的问题
  2. foo(*args, **kwds) : 这在调用超类的构造函数时特别有用

作用域

  1. 变量到底是什么呢?可将其视为指向值的名称。
  2. 有一个名为vars的内置函数,它返回这个不可见的字典
x = 1
scope = vars()
scope['x']
# 1
  1. 这种“看不见的字典”称为 命名空间或作用域。
  2. 参数类似于局部变量,因此参数与全局变量同名不会有任何问题。
  3. 访问全局变量是众多bug的根源。务必慎用全局变量。
  4. “遮盖”的问题: 读取全局变量的值通常不会有问题,但还是存在出现问题的可能性。
  5. 如果有一个局部变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为它被局部变量遮住了。
  6. 可使用函数globals来访问全局变量
  7. 这个函数类似于vars,返回一个包含全局变量的字典。
  8. locals返回一个包含局部变量的字典。
def combine(parameter):
    print(parameter + globals()['parameter'])
parameter = 'berry'
combine('Shrub')
# Shrubberry

x = 1
def chage_global():
    global x
    x = x + 1
chage_global()
print(x)
# 2 

作用域嵌套

  1. 使用一个函数来创建另一个函数。
def multiplier(factor):
    def multiplyByFactor(number):
        return number * factor
    return multiplyByFactor
  1. 一个函数位于另一个函数中,且外面的函数返回里面的函数。
  2. 像multiplyByFactor这样存储其所在作用域的函数称为闭包。
  3. 使用关键字nonlocal, 让你能够给外部作用域(非全局作用域)内的变量赋值.

递归

  1. 递归意味着引用(这里是调用)自身。
  2. 递归函数通常包含下面两部分:基线条件,递归条件
  3. 基线条件(针对最小的问题):满足这种条件时函数将直接返回一个值。
  4. 递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分。
  5. 两个经典案例:阶乘和幂
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1)

def power(x , n):
    if n == 0:
        return 1
    else:
        return x * power(x, n-1)
  1. 使用循环的效率可能更高。然而,在很多情况下,使用递归的可读性更高,且有时要高得多.
  2. 另一个经典案例:二分查找
def search(sequence, number, lower=0, upper=None):
    if upper is None: upper = len(sequence) - 1
    if lower == upper:
        assert number == sequence[upper]
        return upper
    else:
        middle = (lower + upper) // 2
        if number > sequence[middle]:
            return search(sequence, number, middle + 1, upper)
        else:
            return search(sequence, number, lower, middle)

函数式编程

  1. map: 将序列的所有元素传递给函数。
list(map(str, range(10)))
# 与[str(i) for i in range(10)]等价
# ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] 
  1. filter: 根据布尔函数的返回值来对元素进行过滤。
def func(x):
    return x.isalnum()

seq = ["foo", "x41", "?!", "***"] 
list(filter(func, seq))
# ['foo', 'x41'] 
  1. Python提供了一种名为lambda表达式的功能,让你能够创建内嵌的简单函数
seq = ["foo", "x41", "?!", "***"]
filter(lambda x: x.isalum(), seq)
  1. reduce: 它使用指定的函数将序列的前两个元素合二为一,再将结果与第3个元素合二为一,依此类推,直到处理完整个序列并得到一个结果。
numbers = [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] 
from functools import reduce
reduce(lambda x, y: x + y, numbers)
# 1161
  1. 与使用自定义函数相比,使用模块operator中的函数总是效率更高。
上一篇:浦镇质量系统平台-任务管理Week02


下一篇:【PTA-python】第3章-18 输出10个不重复的英文字母 (30 分)