note-PythonCookbook-第七章 函数

第七章 函数

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

* 参数接受任意数量的位置参数,放在最后一个位置参数后,接收到的参数组成一个元组;
** 参数接受任意数量的关键字参数,放置在最后一位,接收到的参数组成一个字典。
* 参数后面仍然可以定义其它参数,即强制关键字参数。

7.2 只接收关键字参数的函数

* 参数或单个 * 后面的参数必须用关键字传参。

7.3 给函数参数增加元信息

使用函数参数注解:

def add(x:int, y:int) -> int:
	return x + y

这些注解不影响运行效果,但增加代码可读性。
函数注解存储在函数的 __annotations__ 属性中。

>>> add.__annotations__
{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

7.4 返回多个值的函数

函数返回的多个值用逗号分隔,会将这些值包装为一个元组返回。

7.5 定义有默认参数的函数

函数定义时直接给参数指定默认值。
默认参数只在函数定义时赋值一次。

>>> x = 3
>>> def test(a, b=x):
...		print(a, b)
...
>>> test(1)
1 3
>>> x = 5
>>> test(1)
1 3

默认参数的值应该是不可变对象。因为默认值如果发生改变,再次调用这个函数时默认值就不一样了。如果默认参数需要是一个可修改的容器,可以使用 None 当默认值。对 None 的测试要使用 is 操作符。

>>> def test(a, b=[]):
... 	print(b)
... 	return b
...
>>> x = test(1)
>>> x
[]
>>> x.append(4)
>>> x
[4]
>>> test(2)
[4]

如果不想传默认值,而是只想检测有没有传进来东西,而 False、None、0 都是合法的值。这时需要定义一个私有对象。

_no_value = object()

def test(a, b=_no_value):
	if b is _no_value:
		print('no value')

用户不可能传递 object 的实例进来的。

7.6 定义匿名或内联函数

lambda 表达式能用来代替一些简单的函数。
lambda 只能指定单个表达式,不能包含更多的语句、条件表达式、迭代或异常处理等。
在排序和 reduce 操作时经常会用。

7.7 匿名函数捕获变量值

默认参数是在定义时赋值,而 lambda 表达式中的自由变量在调用时才会绑定值。

>>> x = 10
>>> a = lambda y: x + y
>>> x = 30
>>> a(0)
30

如果想在定义时就绑定值,可以把参数定义为默认参数:

>>> x = 10
>>> a = lambda y, x=x: x+y
>>> x = 20
>>> a(0)
10

最容易犯错的地方是在列表推导式使用 lambda 表达式的时候。

>>> testlist = [lambda x: x+i for i in range(5)]
>>> for t in testlist:
... 	print(t(0))
...
4
4
4
4
4

把参数 i 修改为默认参数,就能在定义时就绑定值。

>>> testlist = [lambda x, i=i: x+i for i in range(5)]
>>> for t in testlist:
... 	print(t(0))
...
0
1
2
3
4

7.8 减少可调用对象的参数个数

functools.partial() 函数可以为参数设置固定的值,返回一个可调用对象,
这个对象接收未被赋值的参数,然后跟已复制的参数合并起来一同传给原函数。

7.9 将单方法的类转换成函数

如果一个类只是为了给其中的某个方法提供状态信息的话,使用闭包能将其转换为函数。

from urllib.request import urlopen

class UrlTemplate:
	def __init__(self, template):
		self.template = template
	
	def open(self, **kwargs):
		return urlopen(self.template.format_map(kwargs))

yahoo = UrlTemplate('http://finance.yahoo.com/dquotes.csv?s={name}&f={fields}')
for line in yahoo.open(name='IBM,AAPL,FB',fields='sliciv'):
	print(line.decode('utf-8'))

上述实现使用闭包:

def urltemplate(template):
	def opener(**kwargs):
		return urlopen(template.format_map(kwargs))
	return opener

yahoo = urltemplate('http://finance.yahoo.com/dquotes.csv?s={name}&f={fields}')
for line in yahoo(name='IBM,AAPL,FB',fields='sliciv'):
	print(line.decode('utf-8'))

闭包在函数内部有一个额外的变量环境,它会记住自己被定义时的环境。

7.10 带额外状态信息的回调函数

def apply_async(func, args, *, callback):
	result = func(*args)
	callback(result)

def print_result(result):
	print('Got:', result)

def add(x, y):
	return x + y

apply_async(add, (2, 3), callback=print_result)
# 5

callback 这个参数是外部定义的函数,只接收一个参数。在想让这个函数访问额外的信息时会很麻烦。

# 类绑定方法
class ResultHandler:
	def __init__(self):
		self.sequence = 0
	
	def handler(self):
		self.sequence += 1
		print(f'{self.sequence} Got: {result}')

r = ResultHandler()
apply_async(add, (2,3), callback=r.handler)
# 闭包
def make_handler():
	sequence = 0
	def handler(result):
		nonlocal sequence	# 修改须先声明
		sequence += 1
		print(f'{sequence} Got: {result}')
	return handler

handler = make_handler()
apply_async(add, (2, 3), callback=handler)
# 协程
def make_handler():
	sequence = 0
	while True:
		result = yield
		sequence += 1
		print(f'{sequence} Got: {result}')

handler = make_handler()
next(handler)
apply_async(add, (2, 3), callback=handler.send)

7.11 内联回调函数

回调函数、生成器、控制流。
将复杂的控制流隐藏在生成器函数背后。

from queue import Queue
from functools import wraps

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):
    print(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


def add(x, y):
    return x + y

@inlined_async
def test():
    r = yield Async(add, (2,3))
    print(r)
    r = yield Async(add, ('ok', 'go'))
    print(r)
    for n in range(10):
        r = yield Async(add, (n, n))
        print('test', r)

7.12 访问闭包中定义的变量

可以在闭包内编写访问函数并将其作为函数属性绑定到闭包上。

def sample():
	n = 0
	def func():
		print('n=', n)
	
	def get_n():
		return n
	
	def set_n(value):
		nonlocal n
		n = value
	
	func.get_n = get_n
	func.set_n = set_n
	return func
>>> f = sample()
>>> f()
n= 0
>>> f.set_n(10)
n= 10
>>> f.get_n()
10

上述代码的扩展:让闭包模拟类的实例。这样写会比使用类稍微快一些。

import sys
class ClosureInstance:
    def __init__(self, locals=None):
        if locals is None:
            locals = sys._getframe(1).f_locals
        
        # 把局部变量中可调用的对象导入字典
        self.__dict__.update((key,value) for key, value in locals.items() if callable(value))
    
    def __len__(self):
        return self.__dict__['__len__']()


def Stack():
    items = []
    def push(item):
        items.append(item)
    
    def pop():
        return items.pop()
    
    def __len__():
        return len(items)
    return ClosureInstance()
>>> s = Stack()
>>> s
<__main__.ClosureInstance object at 0x0000005EF0145A58>
>>> s.push(3)
>>> s.push(4)
>>> s.push(5)
>>> len(s)
3
>>> s.pop()
5
>>> s.pop()
4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值