pythoncookbook 第7章 函数

[toc]

第7章 函数

7.1 任意参数

*args **kwargs 使用

7.2 只能接受关键字参数 (py3)

  • 关键字 参数只能放在 位置参数的后面
def recv(maxsize, *, block):
'Receives a message'
    pass
recv(1024, True) # TypeError
recv(1024, block=True) # Ok 在*后面的关键字的位置参数,只能作为关键字参数才能起作用
# 相当于强制启用关键字参数,使recv()的调用更清晰

7.3 函数添加注解

-不推荐使用,用3引号的就可以了

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

存储于add.__annotations__

7.4 返回多个的函数

  • 函数返回多个值时候,其实际返回的是一个元组
  • ",,"以创建元组

7.5 默认参数

  • 1 默认参为不可变对象
def apam (a,b=42):
    return a,b
  • 2 默认参为可变对象
    默认参数是一个可修改的容器比如一个列表、集合或字典,可以使用 None作为默认值
def spam(a, b=None):
    if b is None:
        b = []

原因看下面代码,python的变量名称为一个对象的指针,对象为可变对象的话,内部是可以变化的。导致看上去你的默认参数在变。

def func(x,y=[]):
    y.append(x)
    return x,y

print func(1)
print func(2)

object()返还一个对象,None是一个对象。

_no_value = object()
def spam(a, b=_no_value):
    if b is _no_value:
        print('No b value supplied')
spam(123,None)
spam(123)

几点tips:

  • 默认参数的值仅仅在函数定义的时候赋值一次。
  • 默认参数不可,使用可变对象(原因看上文)
  • 长度为 0 的字符串、列表、元组、字典等都会被当做 False。
def spam(a, b=None):
    if not b: # NO! Use 'b is None' instead
        b = []
>>>spam(1)
>>>x = []
>>>spam(1,x) # Silent error. x value overwritten b
>>>spam(1,0) # Silent error. 0 ignored
>>>spam(1,'') # Silent error. '' ignored

object 是 python 中所有类的基类。
你可以创建 object 类的实例,但是这些实例没什么实际用处,因为它并没有任何有用
的方法,也没有哦任何实例数据 (因为它没有任何的实例字典,你甚至都不能设置任何
属性值)。你唯一能做的就是测试同一性。这个刚好符合我的要求,因为我在函数中就
只是需要一个同一性的测试而已。

7.6 匿名函数lambda

  • 常用于排序时的表达式
  • 惰性调用,存在
  • 让某个对象变成能callable

7.7 匿名函数捕获变量值

x =  10
a = lambda y: x+y
x = 20
b = lambda y: x+y
a(10) #30
b(10) #30

这其中的奥妙在于 lambda 表达式中的 x 是一个自由变量,在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。

注意不正确的使用列表推倒

funcs = [lambda x: x+n for n in range(5)]
for f in funcs:
print f(0)

正确的写成

funcs = [lambda x, n=n: x+n for n in range(5)]

原因解析:lambda 函数运行时绑定内部参数

funcs=[,,...]

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

functools.partial() 函数允许你给一个或多个参数设置固定的值,减少接下来被调用时的参数个数。

def spam(a, b, c, d):
    print(a, b, c, d)
from functools import partial
s1 = partial(spam, 1) # a = 1
s1(2, 3, 4)
>>>1 2 3 4 

partial() 固定某些参数并返回一个新的 callable 对象。这个新的 callable接受未赋值的参数,然后跟之前已经赋值过的参数合并起来,最后将所有参数传递给原始函数。
更进一步,partial() 通常被用来微调其他库函数所使用的回调函数的参数。

def output_result(result, log=None):
    if log is not None:
        log.debug('Got: %r', result)


# A sample function
def add(x, y):
    return x + y


if __name__ == '__main__':
    import logging
    from multiprocessing import Pool
    from functools import partial
    logging.basicConfig(level=logging.DEBUG)
    log = logging.getLogger("__file__")
    p = Pool()
    p.apply_async(add, (3, 4), callback=partial(output_result, log=log))
    p.close()  # 调用join之前,先调用close函数,否则会出错。
    p.join() # 执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束

partial在类实例化的使用

class EchoHandler(StreamRequestHandler):
# ack is added keyword-only argument. *args, **kwargs are
# any normal parameters supplied (which are passed on)
    def __init__(self, *args, ack, **kwargs):
        self.ack = ack
        super(EchoHandler,self).__init__(*args, **kwargs)
    def handle(self):
        for line in self.rfile:
            self.wfile.write(self.ack + line)

from functools import partial
serv = TCPServer(('', 15000), partial(EchoHandler, ack=b'RECEIVED:'))

7.9 两种保留函数外部环境变量的方法

  • 1 为函数的闭包
def urltemplate(template):
    def opener(**kwargs):
        return urlopen(template.format_map(kwargs))
return opener
yahoo = urltemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
  • 2 为类的实现
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))

$这其实也是两类装饰器的实现原理$

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

一般回调函数,载回调后,内部的变量,会被内存回收。有时候,需要回调函数状态变量。
用人话:如有一个“内部”变量保存回调函数的调用次数

我们先定义如下一个需要调用回调函数的函数:

def apply_async(func, args, *, callback):
    # Compute the result
    result = func(*args)
    # Invoke the callback with the result
    callback(result)

使用这个函数

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

def add(x, y):
    return x + y
apply_async(add, (2, 3), callback=print_result) # Got: 5
apply_async(add, ('hello', 'world'), callback=print_result) # Got: helloworld

注意到 print result() 函数仅仅只接受一个参数 result 。不能再传入其他信息。
而当你想让回调函数访问其他变量或者特定环境的变量值的时候就会遇到麻烦。

  • 1 类方法的实现
class ResultHandler:
    def __init__(self):
        self.sequence = 0
    def handler(self, result):
        self.sequence += 1
        print('[{}] Got: {}'.format(self.sequence, result))

如何使用这个方法

r = ResultHandler()
apply_async(add, (2, 3), callback=r.handler) 
#[1] Got: 5
apply_async(add, ('hello', 'world'), callback=r.handler)
#[2] Got: helloworld
  • 2 闭包捕获状态值
def make_handler():
    OneClass = type(repr('OneClass'), (object,), {"squence": 0}) #或者用字典
    def handler(result):
        #nonlocal sequence 这个nonlocal方法py2 没有,用类属性来传递变量
        OneClass.squence += 1
        print('[{}] Got: {}'.format(sequence, result))
    return handler

如何使用这个方法

handler = make_handler()
apply_async(add, (2, 3), callback=handler) 
# [1] Got: 5
apply_async(add, ('hello', 'world'), callback=handler)
# [2] Got: helloworld

总结:函数内部的变量,不会随函数结束,被回收。用两种方法被外部函数所记录。其实和7.9节差不多

  • 3 还有个更高级的办法,携程
def make_handler():
    sequence = 0
    while True:
        result = yield
        sequence += 1
        print('[{}] Got: {}'.format(sequence, result))

如何使用这个方法

handler = make_handler() # 产生一个生成器
handler.next() # 或者 handler.send(None) 运行里面的代码,遇到yield停下
apply_async(add, (2, 3), callback=handler.send) 
# [1] Got: 5
apply_async(add, ('hello', 'world'), callback=handler.send)
# [2] Got: helloworld

7.11 内联回调函数(*)

def apply_async(func, args, callback):
    # Compute the result
    result = func(*args)
    # Invoke the callback with the result
    callback(result)

from Queue import Queue
from functools import wraps



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


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"


class Async:
    def __init__(self, func, args):
        self.func = func
        self.args = args


if __name__ == '__main__':
    import multiprocessing
    pool = multiprocessing.Pool()
    apply_async = pool.apply_async
    # Run the test function
    test()

只能明白基础的流程,同一个回调函数,一系列的任务函数。

7.12 访问闭包中定义的变量

你想要扩展函数中的某个闭包,允许它能访问和修改函数的内部变量。

def sample():
    N = type(repr('OneClass'), (object,), {"value": 0}) #用类属性传递值
    #N = {"value":0}
 
    # Closure function
    def func():
        print'n=', N.value  #N["value"]
    # Accessor methods for n
    def get_n():
        return N.value
    def set_n(x):
        N.value = x
    # Attach as function attributes
    func.get_n = get_n
    func.set_n = set_n
    return func
 
f = sample()
f()
f.set_n(10)
f()
f.get_n()

在闭包的函数中定义,定义一些函数,并将这些函数变为内部函数的属性,来查看设置闭包函数的变量

同样,也可以用来的实现,内部的函数.

import sys
class ClosureInstance:
    def __init__(self, locals=None):
        if locals is None:
            locals = sys._getframe(1).f_locals
        # Update instance dictionary with callables
        self.__dict__.update((key,value) for key, value in locals.items()
                                    if callable(value) )
    # Redirect special methods
    def __len__(self):
        return self.__dict__['__len__']()
 
# Example use
def Stack():
    items = []
    def push(item):
        items.append(item)
    def pop():
        return items.pop()
    def __len__():
        return len(items)
return ClosureInstance()

-用闭包函数的外层函数,记住内部函数的变量,避免被回收.添加将定义的方法添加为属性.用来在全局中使用.查看或者改变闭包内的变量.

你可能感兴趣的:(pythoncookbook 第7章 函数)