# 7.1 可接受任意数量参数的函数
# 注意,*参数后面仍然可以定义其他参数
def b(x, *args, y, **kwargs):
    pass

# 7.2 只接受关键字参数的函数(命名关键字参数,强制关键字参数)
# 希望函数的某些参数强制使用关键字参数传递,将该参数放到某个*参数或者单个*后面就可以
def recv(maxsize, *, block):
    print('receives a message')

# recv(1024,True)  # TypeError: recv() takes 1 positional argument but 2 were given,说明只接收一个位置参数
recv(1024,block=True)

help(recv)

# 7.3 给函数参数增加元信息
def recv(maxsize:int, *, block:bool) -> int:
    print('receives a message')
help(recv)
recv(1024,block=True)

# 7.4 函数返回多个值,直接用元组,return时不需要加()
def func():
    return 1,2,3
for j in func():
    print(j)

# 7.5 定义有默认参数的函数
x = 42
def spam(a, b=x):
    print(a, b)
spam(1)
x = 23
spam(1)  # 输出还是42,默认参数的值仅仅在函数定义的时候赋值一次
# 默认参数的值应该是不可变的对象,不能是[]等可变的对象
def spam(a, b=[]):
    print(b)
    return b
x = spam(1)
print(x)
x.append(99)
spam(1)
# 判断一个参数是否是None的时候要用 is None,不能not b,否则空字符串、列表、元组、字典not的值都是True
# 微妙的问题:判断某个可选参数是否被用户传递进来
_no_vlue = object()
def spam(a, b=_no_vlue):
    if b is _no_vlue:
        print('No b value supplied')
spam(1)

# 7.6定义匿名或内联函数
# lambda只能定义单个表达式
names = ['David Beazley', 'Brian Jones','Raymond Aettinger', 'Ned Batchelder']
print(sorted(names,key=lambda name: name.split()[-1].lower()))

# 7.7 匿名函数捕获变量值
x = 10
a = lambda y: x+y  # x在运行时才绑定值,而不是定义时绑定
x = 20
b = lambda y: x+y
print(a(10),b(10))

# 如果想lambda函数参数在定义时就捕获到值,将参数定义成默认参数
x = 10
a = lambda y,x=x: x+y  # x在运行时才绑定值,而不是定义时绑定
x = 20
b = lambda y,x=x: x+y
print(a(10),b(10))

x = 10
def c(y):
    return x+y  # 结果一样,x在运行时才绑定值,而不是定义时绑定
x = 20
def d(y):
    return x+y
print(c(10),d(10))

# 7.8减少可调用对象的参数个数
# functool.partial()
from functools import partial
def spam(a, b, c, d):
    print(a, b, c, d)
s1 = partial(spam, 1)
s1(2, 3, 4)
s2 = partial(spam,d=10)
s2(1,2,3)

# 7.9 将单方法的类转化为函数:使用闭包
from urllib.request import urlopen
class UrlTemplate:
    def __init__(self, template):
        self.template = template

    def open(self, **kwargs):
        return urlopen(self.template)
baidu = UrlTemplate('https://www.baidu.com')
for line in baidu.open(name='IBM,APPL,FB',fields='sl1c1v'):
    # print(line.decode('utf-8'))
    pass

# 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)

# 让回调函数访问外部信息
# 方法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)

# 方法2:作为类的替代,使用一个闭包捕获状态值
def make_handler():
    sequence = 0
    def handler(result):
        nonlocal sequence
        sequence += 1
        print('[{}] Got:{}'.format(sequence, result))
    return handler

handler = make_handler()
apply_async(add,(2,3),callback=handler)

# 方法3:使用协程达到同样的效果
def make_handler2():
    sequence = 0
    while True:
        result = yield
        sequence += 1
        print('[{}] Got:{}'.format(sequence, result))

handler2 = make_handler2()
next(handler2)  # 启动协程
apply_async(add,(2,3),callback=handler2.send)

# 7.11内敛回调函数 TODO 看不懂!
# 使用生成器和协程可以使得回调函数内联在某个函数中
# def apply_async(func, args, *, callback):
#     result = func(*args)
#     callback(result)
#
# from queue import Queue
# from functools import wraps
#
# 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


# 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()
f.set_n(10)
f()
print(f.get_n())

def f1():
    n = 3
    print('in f1')
    def f2():
        print(n)
        print('in f2')
        return 3
    return f2
print(f1()())