《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
, filter
和 reduce
函数。
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" 转载请保留原文链接及作者。