《Python基础教程》读书笔记06-抽象

判断函数是否可调用

# Python2,callcble 函数
>>> import math
>>> x = 1
>>> y = math.sqrt
>>> callable(x)
False
>>> callable(y)
True
# Python3,表达式 hasattr(func, "__call__")
>>> hasattr(x, "__call__")
False
>>> hasattr(y, "__call__")
True

文档化函数

def 语句的后面,及模块的开头,文档字符串

def square(x):
    '文档注释:计算两个数的平方值'
    return x*x

>>> square.__doc__  # 获得文档字符串
'文档注释:计算两个数的平方值'
>>> help(square)    # help函数
Help on function square in module __main__:

square(x)
    文档注释:计算两个数的平方值

关键字参数

def hello(greet, name): # 一般的位置参数
    print (greet + "," + name + "!")

>>> hello('hello', 'zhang')
hello,zhang!

>>> hello(name='wang', greet='hi') # 参数多的时候,顺序不好记,可以提供参数的名字
hi,wang!
>>> hello('hi', name='wang')
hi,wang!
>>> hello(greet='hi', 'wang') # 如果使用关键参数,之后的就不能是 位置参数
SyntaxError: positional argument follows keyword argument
def hello(greet='hello', name='zhang'): # 可以在函数中提供 默认值
    print (greet + "," + name + "!")

>>> hello()
hello,zhang!
>>> hello(name='wang')
hello,wang!

位置参数和关键字参数联合使用,位置参数需要在关键字的前面,避免使用联合

Python3

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收 city 和 job 作为关键字参数。这种方式定义的函数如下:

def person(name, age, *, city, job):
    print(name, age, city, job)

* 后面的参数被视为命名关键字参数。

调用方式如下:

>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:

>>> person('Jack', 24, 'Beijing', 'Engineer')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given

由于调用时缺少参数名 city 和 job,Python 解释器把这 4 个参数均视为位置参数,但 person() 函数仅接受 2 个位置参数。

命名关键字参数也可以有缺省值,从而简化调用:

def person(name, age, *, city='Beijing', job):
     print(name, age, city, job)

由于命名关键字参数 city 具有默认值,调用时,可不传入 city 参数:

>>> person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer

使用命名关键字参数时,要特别注意,* 不是参数,而是特殊分隔符。如果缺少 *,Python 解释器将无法识别位置参数和命名关键字参数。

可变参数

def print_params(name, *params):
    print (name + ":" )
    print (params)

>>> print_params("Number", 1,2,3,4,5)
Number:
(1, 2, 3, 4, 5)
>>> print_params("Number") # 可变参数,可选
Number:
()

关键字的可变参数

**,这些关键字参数在函数内部自动组装为一个 dict,也是可选的
也可以接收一个 dict 参数,但就不能像下方和普通的可变参数联合使用了,如:extra = {'foo': 1, 'bar': 2},注意 params 获得的 dict 是 extra 的一份拷贝,对 kw 的改动不会影响到函数外的 extra。

def print_params(x, y, z=3, *name, **params):
    print (x,y,z)
    print (name)
    print (params)

>>> print_params(1,2,3,5,6,7, foo=1, bar=2)
1 2 3
(5, 6, 7)
{'bar': 2, 'foo': 1}
>>> extra = {'foo': 1, 'bar': 2}
>>> print_params(1,2,3,5,6,7, extra) # 传递dict参数,不能和普通的可变参数联合使用,使用 **extra 就可以
1 2 3
(5, 6, 7, {'bar': 2, 'foo': 1})
{}
>>> print_params(1,2,3,5,6,7, **extra)
1 2 3
(5, 6, 7)
{'foo': 1, 'bar': 2}
def person(name, age, **kw):  # 没有普通的可变参数,调用必须加 **
    print('name:', name, 'age:', age, 'other:', kw)

>>> person('Jack', 24, **extra)  # 没有 ** 报错
name: Jack age: 24 other: {'bar': 2, 'foo': 1}

作用域

内建函数 vars() 返回作用域值的字典

>>> x = 1
>>> scope = vars() # 这个字典,不要修改
>>> scope['x']
1

全局变量

局部变量和全局变量重名时,可以使用 globals() 函数返回全局变量的字典(locals() 函数返回局部变量的字典)

def combine(param):
    print (param + ':' + globals()['param'])

>>> param = 'zhang'
>>> combine('name')
name:zhang

在函数内部重绑定全局变量,global 没有 global 语句,是不可能为定义在函数外的变量赋值的

x = 1
def change_global():
    global x
    x = x + 1

>>> change_global()
>>> x
2

嵌套作用域

Python 的函数是可以嵌套的

def multiplier(factor):
    def multiplyByFactor(number):
        return number * factor
    return multiplyByFactor

>>> multiplier(2) # 函数本身被返回,包含函数的环境(和相关局部变量)
<function multiplyByFactor at 0x027CEB30>
>>> multiplier(2)(5)
10

每次调用外层函数,它的内部函数都被重新绑定,factor 变量每次都有新的值,稍后会被内层函数访问

>>> x = multiplier(2) # 每次调用都会返回一个新的函数
>>> x(3)
6
>>> y = multiplier(5)
>>> y(5)
25

类似 multiplyByFactor 函数存储子封闭作用域的行为叫做 闭包

闭包

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs
>>> f1, f2, f3 = count()
>>> f1(), f2(), f3() # 这里不是(1,4,9)
(9, 9, 9)

全部都是 9,原因就在于返回的函数引用了变量 i,但它并非立刻执行。等到 3 个函数都返回时,它们所引用的变量 i 已经变成了 3,因此最终结果为 9

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量,方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i) 立刻被执行,因此 i 的当前值被传入 f()
    return fs

>>> f1, f2, f3 = count()
>>> f1(), f2(), f3()
(1, 4, 9)

缺点是代码较长,可利用 lambda 函数缩短代码。

Python3 对外部作用域赋值

nonlocal 关键字

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

>>> def make_counter_test():
  mc = make_counter()
  print(mc())
  print(mc())
  print(mc())

>>> make_counter_test()
1
2
3

函数式编程

map, filterreduce 函数。

map, filter 不是很有用,并可以用列表推导式替换。

map 函数

可以使用 map 函数将序列中的元素全部传递给一个函数。

>>> [str(i) for i in range(10)] # 列表推导式
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
# Python3
>>> x = []
>>> map(str, range(10)): # map第一个参数为类型,也可以int
<map object at 0x000002B1FA21F940>

>>> list(map(str, range(10)))
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

# Python2
>>> map(str, range(10))
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

filter 函数

filter 函数可以基于一个返回布尔值的函数对元素进行过滤。

def func(x):
    return x.isalnum()

>>> seq = ['foo', 'x41', '?!', '***']
>>> filter(func, seq)
<filter object at 0x03AAF6F0> # Python2.7,返回 ['foo', 'x41']
>>> list(filter(func, seq))
['foo', 'x41']
# 使用列表推导式:
>>> [x for x in seq if x.isalnum()]
['foo', 'x41']
# 使用 lambda
>>> filter(lambda x: x.isalnum(), seq)
['foo', 'x41']

reduce 函数

将序列的前两个元素与给定的函数联合使用,并且将它们的返回值和第 3 个元素继续联合使用,直到整个序列处理完毕,得到一个最终结果。

>>> from functools import *
# 计算列表元素的合
>>> z = [11, 22, 33, 44, 55, 66, 77]
>>> reduce(lambda x, y: x + y, z)
308
>>> sum(z) # 可以使用内建函数 sum
308

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 bin07280@qq.com

文章标题:《Python基础教程》读书笔记06-抽象

文章字数:2.2k

本文作者:Bin

发布时间:2017-01-01, 22:30:32

最后更新:2019-08-06, 00:07:35

原始链接:http://coolview.github.io/2017/01/01/Python%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/%E3%80%8APython%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B006-%E6%8A%BD%E8%B1%A1/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录