python-函数进阶

1,名称空间

又名name space, 顾名思义就是存放名字的地方,存什么名字呢?举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方

名称空间共3种,分别如下

  • locals: 是函数内的名称空间,包括局部变量和形参
  • globals: 全局变量,函数定义所在模块的名字空间
  • builtins: 内置模块的名字空间,通过dir(_builtins_)查看..

>>> globals()
{'__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>,<br> '__builtins__': <module 'builtins' (built-in)>,
'x': 1, '__name__': '__main__', '__spec__': None}
>>>>>> locals()
{'__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, <br>'__builtins__': <module 'builtins' (built-in)>,
'x': 1, '__name__': '__main__', '__spec__': None}
>>>>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', <br>'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError',<br> 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError',
'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', <br>'FileExistsError', 'FileNotFoundError',
'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning'<br>, 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError',
'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError',<br> 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError',
'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'ReferenceError', <br>'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', <br>'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError',
'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', <br>'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', <br>'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError','_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', <br>'__name__', '__package__', '__spec__','abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', <br>'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr',<br> 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', <br>'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash',
'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', <br>'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next',
'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr',<br> 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted',
'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

View Code

2,作用域的查找顺序

不同变量的作用域不同就是由这个变量所在的命名空间决定的

作用域即范围

全局范围:全局存活,全局有效

局部范围:临时存活,局部有效

查看作用域方法 globals(), locals()

作用域的查找顺序

LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__

  • locals 是函数内的名字空间,包括局部变量和形参
  • enclosing 外部嵌套函数的名字空间
  • globals 全局变量,函数定义所在模块的名字空间
  • builtins 内置模块的名字空间

3,for本质

  优点(和其它循环(while)相比的):比whlle等循环功能强大,不仅遍历的对象种类多,而且比普通循环效率更高(自动把遍历对象生成迭代器)

  定义:遍历可迭代对象(string,list,dict等),如果是遍历可迭代对象,for会自动把in后面的可迭代对象转换成迭代器,把所有元素依次加载到内存,遍历完成后自动处理异常

1 for i in t:  # t被for转换成t.__iter__()
2     print(i)

  等效于

  

 1 t = [1, 2, 3, 4, 5].__iter__()  # 可以通过__iter__()方法或iter()内建函数把Iterable类型变成Iterator对象。
 2 # 循环:
 3 while True:
 4     try:
 5         # 获得下一个值:
 6         i = t.next()
 7         print(i)
 8     except StopIteration:
 9         # 遇到StopIteration就退出循环
10         break

4,闭包

  内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。

  说明:闭包指的是内层函数,之所以叫闭包,闭是因为在外层函数内,包是因为和外层函数的变量绑定在一起。

1.闭包的定义  # 结构:两个def,两个变量,返回内存地址
2.考虑三部:1,定义的函数是否是闭包2,看他怎么用(执行外层函数赋给一个变量,后面执行这个变量会导致这个这个不会销毁 )3,产生bug,内层波函数修改外层函数变量,

闭包定义

 1 def outer():
 2     n = 10
 3
 4     def inner():
 5         print("inner:", n)
 6     return inner
 7
 8 val = outer()
 9 print(val)
10 val()
11
12 # 执行结果<br><function outer.<locals>.inner at 0x0033A390>
13 inner: 10

闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

保存上次的运行环境(外层函数的的生命周期结束之后,外层函数的变量不被销毁)

 1 def outer(name):
 2     count = [0]
 3
 4     def inner():
 5         count[0] += 1
 6         print('Hello,', name, ', ', str(count[0]) + ' access!')
 7         print(count)
 8
 9     return inner
10
11
12 hello = outer('hy')
13 hello()
14 hello()
15 hello()
16
17 ###
18 Hello, hy ,  1 access!
19 [1]
20 Hello, hy ,  2 access!
21 [2]
22 Hello, hy ,  3 access!
23 [3]

  这里面调用outer的时候就产生了一个闭包——inner,并且该闭包持有自由变量——count,因此这也意味着,当函数outer的生命周期结束之后,count这个变量依然存在,因为它被闭包引用了,所以不会被回收。
另外再说一点,闭包并不是Python中特有的概念,所有把函数做为一等公民的语言均有闭包的概念。不过像Java这样以class为一等公民的语言中也可以使用闭包,只是它得用类或接口来实现。

闭包思考:

1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

 自由变量

1,在闭包中,被调用并改动,引发bug的那个值,是自由变量。

2,在装饰器中,被传进来的,但是未引发bug的那个值,是自由变量。(如装饰器的局部变量被嵌套函数调用并修改,从而引发bug,这个局部变量就是自由变量,从意义上来讲,这个局部变量被引用并改动,导致其不被释放,就会使局部变量升华成类似全局变量)。

比如闭包中这个被传进去的name,被闭包调用并且没有改变,所以就是自由变量。装饰器里面,count被引用并修改了,形成了bug,导致count不被销毁,升华成类似全局变量,它也是自由变量。

5,装饰器

    装饰器 = 高阶函数 + 函数嵌套 + 闭包

装饰器的定义:特殊的闭包1.自由变量是函数2.外层函数执行后赋给的变量名和使用装饰器的函数名相同,所以可以不改变用法。3.函数传到闭包中,函数并没有被修改,因此不会产生BUG)

装饰器定义

1,高阶函数

  变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

    编写高阶函数,就是让函数的参数能够接收别的函数。

    只需满足以下任意一个条件,即是高阶函数

      接受一个或多个函数作为输入

      return 返回另外一个函数本身

 1 # 接受一个或多个函数作为输入
 2 def add(x, y, f):
 3     return f(x)+f(y)
 4 val = add(5, 6, abs)  # 参数可以接受函数
 5 print(val)
 6
 7 # return返回另外一个函数本身
 8 def calc(x, y):
 9     return x+y
10 def add2():
11     return calc  # return可以返回另一个函数
12 val2 = add2()
13 print(val2(3, 6))

 2,嵌套函数

  用于先准备一个函数,即外层函数执行完后,内层函数仍可以使用外层函数的变量,装饰器应用了嵌套函数。

 1 def line_conf(a, b):
 2     def line(x):  # 参数其实传到了这里来了
 3         return a * x + b
 4
 5     return line
 6
 7 line1 = line_conf(1, 1)
 8 line2 = line_conf(4, 5)
 9 print(line1(5))
10 print(line2(5))<br><br>###<br>6<br>25

  通俗一点:执行的时候,line1=line_conf,带入函数,返回的是line,所以此时line1=line,变量a,b已经传入进去了,后面执行line1(5)的时候,其实就是执行第二行的line(x)。

  这个例子中,函数line与环境变量a,b构 成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,利用闭包,使开始赋予的值没有销毁,闭包也具有提高代码可复用性的作用。缺点消耗了内存,没有释放出来。

  如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。

嵌套函数和闭包的联系:

  你可以在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。它自身也形成了一个闭包。一个闭包是一个可以自己拥有独立的环境与变量的的表达式(通常是函数)。

既然嵌套函数是一个闭包,就意味着一个嵌套函数可以”继承“容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。

可以总结如下:

  • 内部函数只可以在外部函数中访问。
  • 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。

由于内部函数形成了闭包,因此你可以调用外部函数并为外部函数和内部函数指定参数

3,装饰器

  装饰函数

  定义:装饰器是特殊的闭包(1,自由变量是函数。2,外层函数执行后赋给的变量名和使用装饰器的函数名相同,所以可以不改变用法。3,函数传到闭包中,函数并没有被修改,因此不会产生BUG)

  作用:不改动原函数、不改变原函数调用方式的前提下,扩展函数功能,遵循了开放封闭原则

需求一:为源码函数拓展功能,且不改变调用方式

 # _*_ encoding:utf-8 _*_def login(func):print("passer user vertification...")func()def tv():print("Welcome [%s] to home page")tv = login(tv)  # tv的值为Nonetv()  # 这步出错:TypeError: 'NoneType' object is not callable# 输出
passer user vertification...
File "D:/PythonProject/0313/str.py", line 14, in <module> tv()
TypeError: 'NoneType' object is not callable问题:
1.调用方法改变了
2.调用端还没有调用 tv(),扩展的功能就先执行了

v1

def login(func):print("passer user vertification...")return func@login  #@login等效于tv = login(tv)
def tv():print("Welcome [%s] to tv page")tv()# 输出
# passer user vertification...
# Welcome [%s] to tv page
#
# 问题:
# 调用端还没有调用 tv(),扩展的功能就执行执行

v2

def login(func):print("passer user vertification...")return func@login  #@login等效于tv = login(tv)
def tv():print("Welcome [%s] to tv page")tv()# 输出
# passer user vertification...
# Welcome [%s] to tv page
#
# 问题:
# 调用端还没有调用 tv(),扩展的功能就执行执行

v2.1

def login(func):def inner():print("passed user vertification...")func()return innerdef home():print("Welcome [%s] to home page" % name)# @login # tv=login(tv)def tv():print("Welcome [%s] to tv page")def movie():print("Welcome [%s] to home movie" % name)tv=login(tv)tv()

v3.1

def outer(func):  def inner(args):  print("passed user vertification...")func(args)return inner@outer
#  这个@outer相当于将outer下面的函数当outer的参数执行,也即,tv = outer(tv)
def tv(name):print("Welcome [%s] to tv page" % name)tv('hy')###
passed user vertification...
Welcome [hy] to tv page

v3.2

需求二:需求一 + 传递参数 + 返回值

def outer(func):  def inner(args):  print("passed user vertification...")func(args)return inner@outer
#  这个@outer相当于将outer下面的函数当outer的参数执行,也即,tv = outer(tv)
def tv(name):print("Welcome [%s] to tv page" % name)tv('hy')###
passed user vertification...
Welcome [hy] to tv page

func无返回值+普通参数

def outer(func):  # func=tv,函数的地址其实传到了这里def inner(args):  # 参数其实传到了这里(记)print("passed user vertification...")func(args)  # tv(name)return inner@outer  # == tv=outer(tv)/inner1=outer(tv)#  tv=inner,tv()=inner()#  这个@outer相当于将outer下面的函数当outer的参数执行,也即,tv = outer(tv)
def tv(name):  # 传过去tv函数的地址print("Welcome [%s] to tv page" % name)tv('hy')  # inner('hy')
#  第一次@outer:tv函数还在,inner==tv;第二次:tv('hy')==inner('hy')# 第一次执行
# @outer → inner1=outer(tv) #这个tv是函数
#    def inner(args):  #参数其实传到了这里(记)
#        print("passed user vertification...")
#       func(args) # tv(name)
# 第二次执行
# tv('hy') → inner1('hy')# 执行结果
passed user vertification...
Welcome [hy] to tv page#-------------------------  对比 ---------------def line_conf(a, b):def line(x):  # line变量return a * x + breturn line  # 没有执行,指向函数的地址

line1 = line_conf(1, 1)  # 正常来说,函数调用结束,内部所有变量销毁,但是由于闭包会储存在内存中,利用这个bug,可以执行后面的。
#    def line(x):
#        return a*x + bprint(line1(5))  # 正常来说,a,b在这步没有了(但是有)#执行结果######
# 6
# 25

func无返回值 + 普通参数 理解

func无返回值+普通参数+理解

def outer(func):  # func=tvdef inner(*args, **kwargs):  # 参数其实传到了这里print("passed user vertification...")addfunc = func(*args, **kwargs)print("passed user logout...")return addfuncreturn inner@outer  # tv=outer(tv),tv=inner,tv()=inner()
def tv(name):print("Welcome [%s] to tv page" % name)return 6print(tv('hy'))
---------------------------------------------------------------
关于参数
'hy'
为什么传到了inner,因为第二次tv('hy') = inner1(arg)总结:
1.装饰器的原理是闭包;
2.闭包的作用是第一次执行后,函数tv还在inner中

func无返回值 + 普通参数 + 新函数

def outer(func):  # func=tvdef inner(*args, **kwargs):  # 参数其实传到了这里print("passed user vertification...")func(*args, **kwargs)return inner@outer  # tv=outer(tv),tv=inner,tv()=inner()
def tv(name):print("Welcome [%s] to tv page" % name)tv('hy')
#######
passed user vertification...
Welcome [hy] to tv page

func无返回值 + 动态参数

 1 def outer(func):  # func=tv
 2     def inner(args):  # 参数其实传到了这里
 3         print("passed user vertification...")
 4         return func(args)
 5     return inner
 6
 7
 8 @outer  # tv=outer(tv),tv=inner,tv()=inner()
 9 def tv(name):
10     print("Welcome [%s] to tv page" % name)
11     return 6
12
13 print tv('hy')
14
15 ########
16 passed user vertification...
17 Welcome [hy] to tv page
18 6

func有返回值 + 普通参数

def outer(func):  # func=tvdef inner(*args, **kwargs):  # 参数其实传到了这里print("passed user vertification...")return func(*args, **kwargs)return inner@outer  # tv=outer(tv),tv=inner,tv()=inner()
def tv(name):print("Welcome [%s] to tv page" % name)return 6print tv('hy')########
passed user vertification...
Welcome [hy] to tv page

func有返回值 + 动态参数

需求三:需求二 + 含参装饰器

def wrapper(*wrapperargs):def outer(func):  # func=tvdef inner(*args, **kwargs):  # 参数其实传到了这里print("passed user vertification...")print '[%s] looks like very [%s]' % (''.join(args), ''.join(wrapperargs))return func(*args, **kwargs)return innerreturn outer@wrapper('shuai')  # tv=outer(tv),tv=inner,tv()=inner()
def tv(name):print("Welcome [%s] to tv page" % name)return 6print tv('hy')########
passed user vertification...
[hy] looks like very [shuai]
Welcome [hy] to tv page
6

func有返回值+func动态参数+装饰器动态参数

def Before():print('before')def After():print('after')def wrapper(before_func, after_func):def outer(func):  # func=tvdef inner(*args, **kwargs):  # 参数其实传到了这里print("passed user vertification...")before_func()after_func()return func(*args, **kwargs)return innerreturn outer@wrapper(Before, After)
def tv(name):print("Welcome [%s] to tv page" % name)return 6print(tv('hy'))#######
passed user vertification...
before
after
Welcome [hy] to tv pagefunc有返回值+func动态参数+装饰器参数(函数作为参数)

func有返回值+func动态参数+装饰器参数(函数作为参数)

需求四:多层装饰器

def outer(func):  def inner(*args,**kwargs):  print('log')  r=func(*args,**kwargs)  print('after')  return r  return inner  def outer2(func):  def inner(*args,**kwargs):  if LOGIN_INFO['is_login']:  r=func()  return r  else:  print('please login')  return inner  #如果套两层装饰器,就是双层装饰器了,当然也有三层,四层,道理类似
#这里大家可能有疑惑,python在解释有 “@+函数”这种格式的语法时,会自动从里向外解读,再从外向内执行,
#也就是最里层的原函数被逐层装饰直到最外层,对应例子里,python先把f2(原函数)发给outer2(里层装饰器),被装饰后的outer2的inner再
#被outer(外层装饰器)装饰,最终返回的是outer的inner函数体。
@outer
@outer2
def f2(a,v):  print('F2')
#当然有人问主函数的调用为啥这样写呢,这个会在模块对于的blog中介绍  if __name__ == '__main__':  f2

随意组合上面几种类型装饰器

4,类装饰器

  装饰函数

再来看看类装饰器,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

class Outer(object):def __init__(self, func):self._func = funcdef __call__(self):self._func()@Outer  # tv=Outer(tv)
def tv():print("Welcome  to tv page")tv()###
Welcome  to tv page

无参数 + 无返回值

6,生成式

 1,列表生成器(推导式)

  python一种独特的语法,相当于语法糖的存在,可以帮你在某些场合写出比较精简炫酷的代码,带没有它,也不会有太多的影响。

  语法糖指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

  需求:把a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]里面的每个元素+1

 1 # sb版本
 2 >>> b = []
 3 >>> for i in a:b.append(i+1)
 4 >>> a = b
 5
 6 # 普通版本
 7 a = [1,3,4,6,7,7,8,9,11]
 8
 9 for index,i in enumerate(a):
10     a[index] +=1
11 print(a)
12
13 # 文艺版本
14 >>> a = map(lambda x:x+1,a)
15 >>> a
16 <map object at 0x02861810>
17 >>> list(a)
18 [2, 3, 4, 5, 6, 7, 8, 9]

装逼版本(列表生成式版本)

1 >>> a = [i+1 for i in range(10)]
2 >>> a
3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]<br><br>>>>a = [i for i in range(5) if i % 2 == 0 and not i == 2]<br>>>>print(a)<br>[0,4] 

加入三元运算式

1 # 三元运算加入
2 a = list(range(10))
3 c = [i if 8 < 5 else i+1 for i in a]  <br># 每次都会先执行for i in a,然后再循环前面的部分,每循环一次,前面都会执行一次,挨个得出相应的值并存放
4 # a可以循环字典,元组,甚至字符串<br>print(c)
5
6 # 执行结果
7 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 看下面代码回答输出的结果是什么?为什么?

result = [lambda x: x + i for i in range(10)]
print(result[0](10))这是一个结合了变量作用域、列表推导式和匿名函数的题目,较为复杂,比较考验Python基础知识掌握程度。有同学可能会回答10,其实答案是19,并且result[0~9](10)的结果都是19。这是因为函数具有调用时才查找变量的特性。在你没调用它之前,它不会保存也不关心它内部变量的具体值。只有等到你调用它的时候,它才逐一去找这些变量的具体值。这里的result[0]被调用的时候,变量i已经循环完毕,变成9了,而不是想象中的动态0-9值。那如果不想要这样的结果,想要i是循环的值怎么办?不要直接引用上层变量,把变量直接传进来。result = [lambda x, i=i: x + i for i in range(10)]
print(result[0](10))

面试真题 

2,生成器

  作用:

  1.逐步生成序列,不用像列表一样初始化时就要开辟所有的空间(相当于python2.x的xrange)

  2.模拟并发:协程(Python实现协程最简单的方法,就是使用yield)

  定义:如果函数中包含yield语法,那这个函数就会变成生成器,这个函数调用时返回一个迭代器,生成器属于迭代器

  通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

  所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

  生成器也可以理解为(迭代器对象返回的是函数;特殊的函数;特殊的迭代器(遍历无序数据结构),而特殊在迭代器对象是一个函数,不是列表,字符串(数字)的集合对象。)

  要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

1 >>> L = [x * x for x in range(10)]
2 >>> L
3 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
4 >>> g = (x * x for x in range(10))
5 >>> g
6 <generator object <genexpr> at 0x1022ef630>

 L是一个list,g是一个generator,如果要一个一个打印出来,使用next()函数获得generator的下一个返回值。

  特性:1,取一次创建一次。2,只能往前走,不能回退。

1 >>> next(g)
2 0
3 >>> next(g)
4 1
5 >>> next(g)
6 4
7 >>> next(g)
8 9

使用while执行完以后也会报错。

a = list(range(5))
while True:next(a)# 执行结果:正常的执行完以后还是会报错。

生成器循环最好使用for来进行。

  使用next,while生产完后会报错,但是用for循环不会报错,而generator也是可迭代对象:

1 >>> g = (x * x for x in range(4))
2 >>> for n in g:
3 ...     print(n)
4 ...
5 0
6 1
7 4
8 9

 斐波那契数列:除第一个和第二个数外,任意一个数都可由前面两个数相加得到。

  1, 1, 2, 3, 5, 8, 13, 21...

1 def fib(max):
2     n, a, b = 0, 0, 1
3     while n < max:
4         print(b)
5         a, b = b, a + b # 相当于 t = a + b, a = b, b = t
6         n = n + 1
7     return 'done'<br>fib(3)

 执行结果:1,1,2,3  

  利用yield改为生成器

 1 def fib(max):
 2     n, a, b = 0, 0, 1
 3     while n < max:
 4         print('before yield')
 5         yield b  # 把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
 6         print(b)
 7         a, b = b, a+b
 8         n = n+1
 9     return "done"
10 f = fib(15)  # turn function into a generator
11 next(f)  # fitst time call next()
12 next(f)
13 next(f)
14 next(f)
15 next(f)
16
17 # 执行结果
18 before yield
19 1
20 before yield
21 1
22 before yield
23 2
24 before yield
25 3
26 before yield

 在函数执行中,有些数据想返回到外部程序里,可以使用yield

  里边有yield,函数名一加括号,内部代码根本不执行,只是生成一个生成器对象,

 3,生成器调用方法

  在python2中,range = list, xrange = 生成器

  在python3中,range = 生成器, xrange 没有

 4,函数生成器

  支持更复杂的步骤,可以使用函数生成器。

 1 def range2(n):
 2     conut = 0
 3     while conut < n:
 4         print('count', count)
 5         count += 1
 6         yield count
 7 new_range = range2(10)
 8 r1 = next(new_range)
 9 print(r1)
10 r2 = next(new_range)  # 相等于 r2 = new_range.__next__
11 print(r2)

 yield vs return

    return 返回并中止函数

    yield 返回 数据,并冻结当前的执行过程

    next 唤醒冻结的函数执行过程,继续执行,直到遇到下一个yield

  函数有了yield之后

    函数名加()就变得到了一个生成器。

    return在生成器里,代表生成器的中止,直接报错。

 5,生成器send方法

  sent作用:

    唤醒并继续执行

    发送一个信息到生成器内部

 1 def range2(n):
 2     conut = 0
 3     while conut < n:
 4         print('count', count)
 5         count += 1
 6         sign = yield count     
 7         if sign == 'stop':
 8           print('----sign',sign)
 9 break
10 new_range = range2(3)
11 n1= next(new_range)
12
13 new_range.send('stop')
14 # 执行结果
15 count 0
16 ----sign stop
17 count 1        

6,协程函数

  python中yield浅析

  利用yield将函数变成一个generater(生成器),整个函数变成一个generater对象,函数返回一个iterable对象(迭代值)

  当一个函数在执行过程中被阻塞时,就用yield挂起,然后执行另一个函数。当阻塞结束后,可以用next()或者send()唤醒。相比多线程,协程的好处是它在一个线程内执行,避免线程之间切换带来的额外开销,而且多线程中使用共享资源,往往需要加锁,而协程不需要,因为代码的执行顺序是你完全可以预见的,不存在多个线程同时写某个共享变量而导致出错的情况。

 1 #如果在一个函数内部yield的使用方式是表达式形式的话,如x=yield,那么该函数成为协程函数
 2 def eater(name):
 3     print('%s start to eat food' % name)
 4     food_list = []
 5     while True:
 6         food = yield food_list
 7         print('%s get %s ,to start eat' % (name, food))
 8         food_list.append(food)
 9
10         print('done')
11
12 e = eater('钢蛋')
13 # print(e)
14
15 print(next(e))
16
17 print(e.send('包子'))
18 print(e.send('韭菜馅包子'))
19 print(e.send('大蒜包子'))
20
21 ###
22 钢蛋 start to eat food
23 []
24 钢蛋 get 包子 ,to start eat
25 done
26 ['包子']
27 钢蛋 get 韭菜馅包子 ,to start eat
28 done
29 ['包子', '韭菜馅包子']
30 钢蛋 get 大蒜包子 ,to start eat
31 done
32 ['包子', '韭菜馅包子', '大蒜包子']

yield的表达式形式:

 1 yield的表达式形式:
 2     food=yield
 3
 4 def eater(name):
 5     print('%s start to eat' %name)
 6     while True:
 7         food=yield
 8         print('%s eat %s' %(name,food))
 9
10 e=eater('钢蛋')
11
12 #e.send与next(e)的区别
13 #1.如果函数内yield是表达式形式,那么必须先next(e)
14 #2.二者的共同之处是都可以让函数在上次暂停的位置继续运行,不一样的地方在于<br>#  send在触发下一次代码的执行时,会顺便给yield传一个值

7,生成器总结

  生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

  生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

生成器的特点:

  1. 节约内存
  2. 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

 1 from collections import Iterator
 2 #生成器就是一个函数,这个函数内包含有yield这个关键字
 3 def test():
 4     print('one')
 5     yield 1 #return 1
 6
 7
 8 g=test()
 9 print(g)
10 print(isinstance(g,Iterator))
11
12 ###
13 <generator object test at 0x000001E80F8F0780>
14 True
15
16 ---------------------------------------------------------------------
17 from collections import Iterator
18 #生成器就是一个函数,这个函数内包含有yield这个关键字
19 def test():
20     print('one')
21     yield 1 #return 1
22
23
24 g=test()
25 print(g)
26 print(isinstance(g,Iterator))
27
28 print(next(g))
29
30 ###
31 <generator object test at 0x0000023F1F5E0780>
32 True
33 one  #调用next方法时,生成器才执行
34 1
35 --------------------------------------------------------------
36 def countdown(n):
37     print('start coutdown')
38     while n > 0:
39         yield n #1
40         n-=1
41     print('done')
42
43 g=countdown(5)
44 # print(g)
45
46 # print(next(g))
47 # print(next(g))
48 # print(next(g))
49 # print(next(g))
50 # print(next(g))
51 # print(next(g))
52
53 # for i in g: #iter(g)
54 #     print(i)
55
56 # while True:
57 #     try:
58 #         print(next(g))
59 #     except StopIteration:
60 #         break
61
62 ------------------------------------------
63 >>> def createGenerator() :
64 ...    mylist = range(3)
65 ...    for i in mylist :
66 ...        yield i*i
67 ...
68 >>> mygenerator = createGenerator() # create a generator
69 >>> print(mygenerator) # mygenerator is an object!
70 <generator object createGenerator at 0xb7555c34>
71 >>> for i in mygenerator:
72 ...     print(i)
73 0
74 1
75 4

示例

8,迭代式

  迭代器是一个可以记住遍历的位置的对象。

  迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

  迭代器有两个基本的方法:iter() 和 next()

  迭代器就类似一个循环,迭代一次,就是相当于循环一次。

  可以直接作用于for循环的数据类型:

  • 一类是集合数据类型,如listtupledictsetstr等;
  • 一类是generator(生成器),包括生成器和带yield的 generator function。

 1,可迭代对象

  直接作用于for循环的对象统称为可迭代对象:Iterable

 1 可以使用isinstance()判断一个对象是否是Iterable对象:
 2 >>> from collections import Iterable
 3 >>> isinstance([], Iterable)
 4 True
 5 >>> isinstance({}, Iterable)
 6 True
 7 >>> isinstance('abc', Iterable)
 8 True
 9 >>> isinstance((x for x in range(10)), Iterable)
10 True
11 >>> isinstance(100, Iterable)
12 False

  而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出Stopiteration错误表示无法继续。

 2,迭代器:是一种数据流

  可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

 1 可以使用isinstance()判断一个对象是否是Iterator对象:
 2
 3 >>> from collections import Iterator
 4 >>> isinstance((x for x in range(10)), Iterator)
 5 True
 6 >>> isinstance([], Iterator)
 7 False
 8 >>> isinstance({}, Iterator)
 9 False
10 >>> isinstance('abc', Iterator)
11 False

生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

  把listdictstrIterable变成Iterator可以使用iter()函数:

1 >>> isinstance(iter([]), Iterator)
2 True
3 >>> isinstance(iter('abc'), Iterator)
4 True

3,小结

  凡是可作用于for循环的对象都是Iterable类型;

  凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

  集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

  Python3的for循环本质上就是通过不断调用next()函数实现的,例如:

1 for x in [1, 2, 3, 4, 5]:
2     pass

实际上完全等价于:

 1 # 首先获得Iterator对象:
 2 it = iter([1, 2, 3, 4, 5])
 3 # 循环:
 4 while True:
 5     try:
 6         # 获得下一个值:
 7         x = next(it)
 8     except StopIteration:
 9         # 遇到StopIteration就退出循环
10         break

9,函数中的一些大坑

 ①函数的默认函数一定要是不变对象(immutable)

  • str
  • int
  • float
  • tuple
  • 数值型(number)

  看下面一个例子:

1 def foo(bar=[]):
2     bar.append('a')
3     return bar
4 print(foo())#['a']
5 print(foo())#['a','a']
6 print(foo())#['a','a','a']

乍一眼一看,每次调用foo(),变量bar都应该重置为[]啊,为什么上一次的结果会进行保留呢?
从Python文档中可以找到这样一句话

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:

翻译过来就是:重要警告:默认值只计算一次。当默认值是可变对象(如列表,字典或大多数类的实例)时,这会有所不同。例如,以下函数会累积在后续调用中传递给它的参数。

  这个原因是由于默认参数只计算一次,因为list 是可变数据类型,函数每次调用时,L 是同一个对象的引用。就相当于全局变量一般了

def foo(bar=None):if bar is None:bar=[]bar.append('a')
print(foo())#['a']
print(foo())#['a']
print(foo())#['a']

记住,默认参数一定要是不可变类型

转载于:https://www.cnblogs.com/herosyuan/p/10043238.html

python-函数进阶相关推荐

  1. python 函数进阶_Python学习入门基础:一篇文章搞定函数基础、函数进阶

    一.函数基础函数的快速体验 函数的基本使用 函数的参数 函数的返回值 函数的嵌套调用 在模块中定义函数私信小编001即可获取Python学习资料01. 函数的快速体验 1.1 快速体验 所谓函数,就是 ...

  2. Python — 函数进阶(2)

    文章目录 函数进阶(2) 可迭代对象 什么是可迭代对象 可迭代对象的意义 可迭代对象的使用 迭代器 什么是迭代器 迭代器的优势 迭代器的劣势 怎么使用迭代器 生成器 什么是生成器 怎么使用生成器 以函 ...

  3. 好好学python·函数进阶(递归函数,回调函数,闭包函数,匿名函数,迭代器)

    函数进阶 递归函数 回调函数 闭包函数 特点 匿名函数 lambda 表达式 迭代器 iter() next() 迭代器的取值方案 迭代器取值特点,取一个少一个,直到都取完,最后再获取就会报错 检测迭 ...

  4. Python函数进阶(11)

    函数进阶 函数参数类型 不可变数据类型和可变数据类型自加的区别 1. 判断gl_num和gl_list的值 命名空间 作用域 全局变量和局部变量 global 和nonlocal关键字 内置函数 1. ...

  5. python函数进阶小结_Python-进阶-functools模块小结

    文档 地址 functools.partial 作用: functools.partial 通过包装手法,允许我们 "重新定义" 函数签名 用一些默认参数包装一个可调用对象,返回结 ...

  6. python函数进阶小结_python之函数进阶

    1. 今日内容 1.1 函数的参数 *的魔性用法 函数形参最终顺序 1.2名称空间 全局名称空间,局部名称空间,内置名称空间 取值顺序与加载顺序 作用域 内置函数:globals() locals() ...

  7. 2.3.12 Python 函数进阶-装饰器

    代码运行期间动态增加功能的方式,称之为"装饰器"(Decorator) 本质上,decorator就是一个返回函数的高阶函数 所以,我们要定义一个能打印日志的decorator,可 ...

  8. Python函数进阶

    def eat(a,b,v,f,g): print(a,b,v,f,g) eat('方便面','包子','麻团','豆浆','咸菜') def eat(a,b,*args): # 聚合 打包 prin ...

  9. 2.3.14 Python 函数进阶-生成器

    通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素 ...

  10. python函数进阶小结_python函数的进阶

    形参角度: 万能参数 如果我们在传参数时不很清楚有哪些,或者说给一个函数传了很多实参,考虑用动态参数也叫万能参数. 万能参数,即动态参数,分为两种:动态接收位置参数 *args,动态接收关键字参数** ...

最新文章

  1. python使用缩进来体现代码之间的逻辑关系-Python 使用缩进来体现代码之间的逻辑关系 ....
  2. Linux之tcpdump
  3. leetcode486. 预测赢家(dp)
  4. CSS 制作垂直导航
  5. 数据科学和人工智能技术笔记 十二、逻辑回归
  6. 【ElasticSearch】Es 源码之 Exporters 源码解读
  7. python导入dat数据_Kaggle影评数据集,Python数据分析小例子14
  8. JavaEE学习14(应用)--用户自动登陆
  9. 几何学五大公理_【欧几里德的平面几何五大公理是什么?】作业帮
  10. 通俗易懂理解JAVA虚拟机
  11. 数学建模竞赛常考三大模型及十大算法【预测模型、优化模型、评价模型】
  12. Windows 手动与脚本自动重启Print Spooler打印服务
  13. c语言面试题-基本概念
  14. java处理金额大写为数字,Java中金额数字转换为大写数字
  15. IIS站点部署后频繁弹出“Visual Studio实时调试器”问题
  16. JS中的attribute和property的区别和联系
  17. 支持M1芯片AE2022已发布,After Effects 2022 for MAC中文安装教程,支持Monterey系统不闪退
  18. 佑道医生集团获风和资本数千万A轮投资,6个月实现盈亏平衡
  19. SQL on log : 同比分析各种指标
  20. laravel seeding

热门文章

  1. 请善用工具审核您的内核代码:)
  2. 跟我一起写 Makefile(十三)
  3. django开发Blog(1)
  4. go语言使用redis —— redigo
  5. linux minicom usb串口
  6. npm package.json那些事
  7. 《C Primer Plus》读书笔记——存储类、链接和内存管理
  8. 使用tomcat自带的连接池,报错
  9. script的加载方式与执行
  10. 【Java】StopWatch任务执行时间监视器