Python Cookbook第三版学习笔记【第七章】

第7章

7.1 编写可接受任意数量参数的函数

  • 接受任意数量的位置参数,以"*"开头:
def get_sum(*nums):
    return sum(nums)
  • 用以下的方式都可调用:
>>> get_sum(1,2,3)
6
>>> get_sum(*[1,2,3,4])
10
  • 接受任意数量的关键字参数,以“**”开头:
def get_sum(**kwargs):
    return kwargs['a'] + kwargs['b']
  • 用以下方式都可调用:
>>> get_sum(a=1, b=2)
3
>>> get_sum(**{"a": 1, "b": 2})
3
  • 函数定义时,*开头的参数只能作为最后一个位置参数(此后的参数必须以关键字形式给出),“**”开头的参数只能作为最后一个参数

7.2 编写只接受关键字参数的函数

  • 如7.1节提到的“*开头的参数只能作为最后一个位置参数”,以下函数foo中的b, c, d三个参数只能以键值对形式传入:
def foo(*a, b, c, d):
    pass

# 调用
foo(1, 2, 3, b=4, c=5, d=6)
  • 如果写一个单独的*,那么它之后的参数就只能传入键值对:
def bar(*, a, b, c)
  • 以上函数bar只接受关键字参数
  • 由于调用方只能以关键字形式传入参数,使得阅读代码的人更容易理解参数的含义

7.3 将元数据信息附加到函数参数上

  • 为函数的参数、返回值作类型注解:
def foo(x: int, y: int) -> int:
	return x + y
  • 使用help可以得到函数的注解:
>>> help(foo)
Help on function foo in module __main__:

foo(x: int, y: int) -> int
  • 从函数的__annotations__属性也可以得到这个信息:
>>> foo.__annotations__
{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
  • Python中不作类型声明,添加了注解也没有类型检查的作用,仅对阅读代码的人起提示作用。

7.4 从函数中返回多个值

- 将多个值逗号隔开即可:
def foo():
	return 1, 2, 3
  • 这里的返回值其实是个元组,获取返回值时可以传给多个变量,这就是解包(unpacking):
a, b, c = foo()
>>> a
1
>>> b
2
>>> c
3

a, *b = foo()
>>> a
1
>>> b
[2, 3]

7.5 定义带有默认参数的函数

  • def foo(a, b=1) 的形式即可定义默认参数,确保定义在参数列表的最后面
  • 如果默认值是可变的容器,列表、集合、字典,应该把None作为默认值
  • 如果调用者可能传入None作为参数,而又想判断调用者是否有传入值,可以用object()作为默认参数。
  • 因为在实际代码中,几乎不会有人将object()作为参数传入,所以这可以作为调用方是否有传入值的依据:
_no_value = object()

def foo(a, b=_no_value):
	pass
  • 默认参数必须是不可变对象,如果是可变的,将会发生意外的错误:
def foo(a, b=[]):
	b.append("Hello")
	return b

>>> x = foo(1)
>>> x
['Hello']
>>> x.append("World")
>>> foo(1)
['Hello', 'World', 'Hello']

  • 上面的函数中将b定义为了一个列表,并将b返回
  • 如果修改b返回的这个列表,对函数foo()造成了意外影响
  • 因此不用空列表,应用None

7.6 定义匿名或内联函数

  • 使用lambda表达式:
lambda a, b: a + b
  • lambda后面的就是参数列表,:后面的函数体同时也是它的返回值
  • 匿名函数作为简单的回调函数来使用:
arr = [
	{
		"name": "windy",
		"age": 20
	},
	{
		"name": "jess",
		"age": 16
	},
	{
		"name": "tim",
		"age": 22
	},

]

# 指定了以age字段来排序
arr.sort(key=lambda x: x['age'])

# 筛选age大于等于20的条目
filter(lambda x: x['age']>=20, arr)

7.7 在匿名函数中绑定变量的值

  • lambda 的参数可以绑定默认值:
x = 10
f = lambda y: x+y
  • 执行时只需要传入y,x使用默认值10
  • 参数仅在函数被调用时绑定,就是说,如果在调用前x被修改了,那在函数中用的将是修改后的值
  • 如果要在函数定义的时候就绑定变量值,可用默认参数:
x = 10
f = lambda y, x=x: x+y
  • 这样即便修改x,函数中的x仍为10

7.8 让带有N个参数的可调用对象以较少的参数形式调用

  • 使用functools.partial函数为函数绑定固定的参数:
在这里插入代码片from functools import partial


def foo(a, b):
	return a + b

f = partial(foo, b=1)
  • 这样,函数f的参数b就有了默认值,调用时就只传入a即可:
>>> f(2)
3
  • 但是,不可以再传入b:
>>> f(2,3)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: foo() got multiple values for argument 'b'

  • 如果想把一个多参数的函数作为回调传入另一个函数,而很不幸这个函数只能接收一个参数的时候,partial就起到作用:
def foo(a, b):
    pass


nums = [
    (1, 2),
    (1, 5, 1.5),
    (0.9, 3)
]

nums.sort(key=foo)
TypeError: foo() missing 1 required positional argument: 'b'
  • foo接受了两个参数,不能直接传入sort的key参数;如果利用partial给b传入一个默认值(如果这么做符合实际),就可以与sort联用
  • 使用lambda 也可以起到partial的作用:
x = 1
nums.sort(key=lambda x: foo(x, y))
  • 实际和:
nums.sort(key=partial(foo, x=1))
  • 起到相同效果,但是使用partial使代码意图更清晰,即为某些参数设置默认值

7.9 用函数替代只有单个方法的类

  • 使用单个方法的类一般只是为了保存一些额外的状态供方法使用(在__init__()方法中初始化一些属性)
  • 可以使用闭包(closure)或者说:嵌套函数来替代:
def outer(a, b):
    def inner(**kwargs):
        return "I received params a={}, b={} from outer function, "
        "but c={} is passed to myself".format(a, b, kwargs['c'])
    return inner

f = outer(1, 2)
  • 上面的代码定义了函数outer以及嵌套在里面的函数inner
  • outer接收参数a, b, f = outer(1, 2) 就像是构造了一个类实例一样,初始化了a, b 的值,在inner函数里可以直接使用:
>>> f(c=3)
'I received params a=1, b=2 from outer function, but c=3 is passed to myself'
  • 这样就把额外的参数传给了内部函数,如同先实例化、传入一些值,再调用实例方法一样。

7.10 在回调函数中携带额外的状态

  • 希望给回调函数携带一个额外的状态
  • 举个简单例子,如果我们希望在一个主要的处理函数之后,使用一个回调函数打印处理结果,可能会这样写:
def compute_sum(a, b):
    return a + b

def print_result(result):
    print("Get the result, it is:{}".format(result))

def run(func, args, *, callback):
    # 执行数据处理
    res = func(*args)
    # 使用回调打印结果
    callback(res)
  • 调用run()函数:
>>> run(compute_sum, [1,2], callback=print_result)
Get the result, it is:3
  • 这效果固然好,但如果想给回调函数添加一个状态:例如:这是第几次调用?
  • 可以利用7.9节中提到的闭包(closure)给回调添加状态,像这样:
def print_result():
    count = 0
    def do_print_result(result):
        nonlocal count
        count += 1
        print("{}th time to get the result, it is:{}".format(count, result))
    return do_print_result
  • 用一个变量count记住了调用的次数,nonlocal声明表示变量是在回调函数中修改的(必须有此声明)。
>>> print_result = print_result()
>>> run(compute_sum, [1,2], callback=print_result)
1th time to get the result, it is:3
>>> run(compute_sum, [1,2], callback=print_result)
2th time to get the result, it is:3
>>> run(compute_sum, [1,2], callback=print_result)
3th time to get the result, it is:3

7.11 内联回调函数

  • 假设有一个函数这样来调用回调函数:
def apply_async(func, args, *, callback):
	# 函数的主体逻辑
	result = func(*args)

	# 调用回调函数
	callback(result)
class Async:
    def __init__(self, func, args):
        self.func = func
        self.args = args

def inlined_async(func):
    @wraps(func)
    def wrapper(*args):
        f = func(*args)
        result_queue = Queue()
        result_queue.put(None)
        while True:
            result = result_queue.get()
            try:
                a = f.send(result)
                apply_async(a.func, a.args, callback=result_queue.put)
            except StopIteration:
                break
        return wrapper
  • 结合以上两段代码,使用yield语句,回调函数变成内联式的:
def add(x, y):
    return x + y

@inlined_async
def test():
    r = yield Async(add, (2, 3))
    print(r)
    r = yield Async(add, ("Hello", "World"))
    print(r)
    for n in range(10):
        r = yield Async(add, (n, n))
        print(r)
    print("Goodbye")

7.12 访问定义在闭包内的变量

  • 下面的代码中,函数outer和inner形成闭包:
def outer():
    val = 0
    def inner():
        print('value=', val)
  • 如7.9节中说到的一样,变量val类似于一个附加属性,使用闭包,类似于构造实例并调用其方法一样,但是,我们还需要方法来修改这个val:
def outer():
    val = 0
    def inner():
        print('value=', val)
  • 执行一下:
>>> f = outer()
>>> f()
value= 0
>>> f.set_val(10)
>>> f()
value= 10
>>> f.get_val()
10
  • set_val 和 get_val函数可以用来设置,取得val的值
  • nonlocal声明使得我们可以修改内层变量
  • 设置函数属性将set/get函数附加到闭包上

本章使用到的模块、方法

help(func) # 返回一个函数的注解
func.__annotations__ # 返回一个函数的注解
object() # object的实例,可以作为函数的默认参数,用来代替“None”,以判断用户是否有传入值
functools.partial(func, *args, **kwargs) # 为函数设置默认的参数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值