# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.15.2
#   kernelspec:
#     display_name: Python 3
#     name: python3
# ---

# ### 迭代器
#
# ```python
# x = [1, 2, 3]; it = iter(x)
# print(it.__next__()) #输出1
# print(it.__next__()) #输出2
# print(it.__next__()) #输出3
# print(it.__next__()) #返回 StopIteration
# ```

# ## function

def myfunc():
    '''the doc string'''
    pass
print(myfunc.__doc__)  #输出：the doc string


# ### 1.函数的调用方式
#

# +
#星号（*）后面的形参 必须 要指定参数名称
def recorder(name, *, age):
    print(name, ' ', age)
#recorder('Gary', 32)        #错误，没有指定形参age
recorder('Gary', age=32)    #正确，指定形参age

#有默认值的形参必须放在没有默认值的后面
def recorder(name, age=32): #正确
    pass  
#def recorder(age=32, name):  #错误

#func(*参数) 解包tuple或者list
#使用解包列表或元组，则解包参数不可修改
def recorder(*person):
    for a in person:
        if not isinstance(a, (int, str)):
            raise TypeError('bad operand type')
    #注：参数不可修改
    print(person[0], person[1]) 
mylist = ['Gary', 32]; recorder(*mylist)    #输出：Gary 32
mytuple = ['Gary', 32]; recorder(*mytuple)  #输出：Gary 32

#func(**dict) 解包字典
def recorder(**person):
    for a in person.values():
        if not isinstance(a, (int, str)):
            raise TypeError('bad operand type')
    print(person['name'], person['age'])
mydict = {'age':32, 'name':'gary'}
recorder(**mydict)
recorder(age=32, name='gary')

# -

# #### 1.1混合使用

# +
# 单个形参在前，列表元组字典在后，调用不需要指定单个形参名字
def recorder(ttt, *person1, **person2):
    if len(person1) != 0:
        print(person1[0], person1[1])
    if len(person2) != 0:
        print(person2['name'], person2['age'])

recorder('abc', 'Gary', 32)            #传入不指定形参的实参，由person1接收
recorder('abc', name='Gary', age=32)   #传入指定形参的实参，由person2接收
recorder(ttt='abc')                    #不需要指定
# -

# ### 2.生成器函数(generator) 见5
#
# 生成器对象只能迭代一次，所以对可迭代函数的结果只能取一次。迭代器对象（iter)可迭代多次。

# +
#匿名函数
myfunc = lambda x,y: x+y
print(myfunc(1, 2))         #输出3

# reduce函数：按照sequence的顺序，依次调用function，每次调用传入两个参数
#            一个是sequence当前元素，一个是上一个元素在function的返回值
from functools import reduce
a = reduce(lambda x,y: x+y, range(1, 101)); print(a) #输出5050

b = map(lambda x: x**2, [1, 2, 3, 4, 5]); print(list(b))   #输出[1, 4, 9, 16, 25]
#map函数：   可以处理多个函数，lambda函数的参数个数要和列表（序列）数据个数相同
#           当两个序列长度不相等，以最小长度对所有序列进行提取
c = map(lambda x,y: x+y, [1, 2, 3], [4, 5, 6, 7]); print(list(c)) #输出[5, 7, 9]


#filter函数：   把序列对象中的元素依次放到处理函数中，True则留下
t = filter(lambda x: x%2==0, range(10))
print(list(t))      #输出[0, 2, 4, 6, 8]
#生成器对象只能迭代一次，结果只能取一次
print(list(t))      #输出[]

# -

# ### 3.偏函数
# 偏函数用于截取原函数功能。可以使用一个原函数，将其封装多个偏函数。

# +
from functools import partial
def recorder(name, age):
    print(name, ' ', age)

partial_recorder = partial(recorder, name='Gary')
partial_recorder(age=32)
# -

# ### 4. eval与exec函数
# eval执行要返回结果，适合放置有结果返回的语句; eval()用于将'[]'(包含组合对象的字符串）转换为[]组合对象
#
# exec执行完不返回结果，适合放置运行后没有结果的语句

a = exec('2+3'); print(a)       #返回None
a = eval('2+3'); print(a)       #返回5
#eval()用于将'[]'(包含组合对象的字符串）转换为[]组合对象
b = '[1, 2, 3]'; print(eval(b)) #返回[1, 2, 3]
c = '"hello"'; print(eval(c))    #返回hello


# ### 5. 生成器函数（generaotr）
#
# 1. 迭代器函数（iterator）将所有的内容放在内存里面，使用next函数来遍历，节约系统运算资源
# 2. 生成器函数（generaotr）不会把内容放在内存里，调用next时会计算然后立刻销毁，节约内存

# +
#使用yield语句返回
def print_list(a):
    for i in a:
        yield i
for i in print_list([1, 2, 3]):
    print(i)        #输出1， 2， 3

#使用（）
a = (x**2 for x in range(3))
for i in a:
    print(i)        #输出0， 1， 4
# -

# ### 6.变量的作用域
# 1. L：本地作用域，当前函数
# 2. E：（函数嵌套）上一层结构中的def的本地作用域
# 3. G：全局作用域，不被任何函数包括
# 4. B：内置作用域，是python内部的命名空间
#
# 代码用到某个变量a，系统内部会按照LEGB的顺序去不同的作用域里面找变量a，找到后停下来，否则报错

# +
# global语句
