速撸《python学习手册》--第16-20章-函数

第16章 函数基础

为何使用函数

  • 最大化的代码重用和最小化代码冗余
  • 流出分解

编写函数

  • def 是可执行的代码,当python运行到def时,函数就被创建了。但是当函数被调用的时候,函数才会被执行。
  • def 创建了一个对象并且赋值给某一个变量
  • lambda创建一个对象但将其作为结果返回
  • return 返回结果
  • yield 向调用者发回一个结果对象,同时记住它离开的地方
  • global声明了一个模块级的变量并被复制。
  • nonlocal声明了将要赋值的一个封闭的函数变量。
  • 函数是通过赋值(对象引用)传递的。
  • 参数、返回值以及变量并不是声明。

17章 作用域

在默认情况下,一个函数的所有变量名都是与函数命令空间关联的。即

  • 一个在def内定义的变量名能够被def内的代码使用。但是不能被在函数外部引用这样的变量名。
  • def 之中的变量名与def之外的变量名并不冲突。即def之外的变量名和def 之内的变量名毫无关系。
  • 注意,每一个函数调用都会创建一个新的本地作用域。

变量名解析:legb原则

对于一个def语句:

  • 变量名引用分为三个作用域进行查找,首先是本地,之后是函数内,之后是全局,最后是内置。
  • 默认情况下,变量名赋值会创建或者改变本地变量。
  • 全局声明和非本地声明将赋值的变量名映射到模块文件内部的作用域。

LEGB原则:

  • 本地作用域
  • def 中本地作用域(闭包)
  • 全局作用域
  • 内置作用域

global语句

全局变量如果在函数内部被赋值的话,必须经过声明

尽量少用 global

导入进来的别的模块的全局变量,也是全局变量。

nonlocal语句

用于定义函数时,内层变量引用外层变量,不然修改时会报错,类似global。

# 不改变变量时,不报错
In [133]: def tester(start):
     ...:     state=start
     ...:     def nested(label):
     ...:         print(label,state)
     ...:     return nested

In [134]: F=tester(0)

In [135]: F
Out[135]: <function __main__.tester.<locals>.nested(label)>

In [136]: F('damao')
damao 0

In [137]: F('ermao')
ermao 0

# 一旦改变变量,不用nonlocal声明,报错
In [138]: def tester(start):
     ...:     state=start
     ...:     def nested(label):
     ...:         print(label,state)
     ...:         state+=1
     ...:     return nested

In [139]: F=tester(0)

In [140]: F('damao')
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-140-776037024ad4> in <module>()
----> 1 F('damao')

<ipython-input-138-c3d251ed857c> in nested(label)
      2     state=start
      3     def nested(label):
----> 4         print(label,state)
      5         state+=1
      6     return nested

UnboundLocalError: local variable 'state' referenced before assignment

# 内层函数用之前,一定要先声明,哪怕第一次使用时候,并没有改变变量
In [142]: def tester(start):
     ...:     state=start
     ...:     def nested(label):
     ...:         print(label,state)
     ...:         nonlocal state
     ...:         state+=1
     ...:     return nested
     ...:
     ...:
  File "<ipython-input-142-a0b8a3802677>", line 5
    nonlocal state
    ^
SyntaxError: name 'state' is used prior to nonlocal declaration


# 注意 不同的工厂函数可以生产出不同的值
In [143]: def tester(start):
     ...:     state=start
     ...:     def nested(label):
     ...:         nonlocal state
     ...:         print(label,state)
     ...:         state+=1
     ...:     return nested

In [144]: F=tester(0)

In [145]: F('damao')
damao 0

In [146]: F('ermao')
ermao 1

In [147]: F('sanmao')
sanmao 2

In [148]: F('damao')
damao 3

In [149]: F('ermao')
ermao 4

In [150]: G=tester(100)

In [151]: G('damao')
damao 100

In [152]: G('damao')
damao 101

In [153]: F('damao')
damao 5

In [154]: F('damao')
damao 6

还有一个注意的是,nonlocal声明的变量,毕竟在上层def中已经存在,不然立马报错。

另外,nonlocal 只会在嵌套的def中去查找变量,不会上升到全局。

# 如果不使用nonlocal声明,可以绑定内层函数
In [168]: def tester(start):
     ...:     def nested(label):
     ...:         print(label,nested.state)
     ...:         nested.state+=1
     ...:     nested.state=start
     ...:     return nested

In [169]: F=tester(1)

In [170]: F('damao')
damao 1

In [171]: F('damao')
damao 2

In [172]: F('damao')
damao 3

In [173]: G=tester(1000)

In [174]: G('damao')
damao 1000

In [175]: G('damao')
damao 1001

列举python中保持状态的方法?

  • 共享的全局变量
  • 嵌套函数中嵌套函数的作用域引用
  • 使用默认参数值来让一个python函数保持状态信息
  • 利用函数属性将状态加载到函数自身(上个例子)
  • 使用类

第18章 参数

  • 参数的传递是通过自动将对象赋值给本地变量名来实现的。指针实现。
  • 在函数内部,参数名的赋值不会影响调用者。
  • 改变函数的可变对象参数的值也许会对调用者有影响。

通过对象引用传递:

  • 不可变参数通过值进行传递。效果上就是一份拷贝。
  • 可变对象通过指针进行传递。

python类就是依靠传入的self来更新对象状态。

特定参数匹配模型

python内部赋值进行参数匹配:

  • 通过位置分配非关键字参数
  • 通过匹配变量名分配关键字参数
  • 其他额外的非关键字参数分配到*name元组中
  • 其他额外的关键字参数分配到** name字典中
  • 用默认值分配给在头部位得到分配的参数

模拟 python print函数

import sys


def print_redef(*args, **kargs):
    sep = kargs.get('sep', ' ')
    end = kargs.get('end', '\n')
    file = kargs.get('file', sys.stdout)
    output = ''
    for arg in args:
        output += str(arg) + str(sep)
    file.write(output[:-len(sep)] + end)

def print_redef_1(*args, **kargs):
    sep=kargs.pop('sep', ' ')
    end=kargs.pop('end', '\n')
    file=kargs.pop('file',sys.stdout)
    if kargs:raise TypeError('extra keywords:' % kargs)
    output = ''
    for arg in args:
        output += str(arg) + str(sep)
    file.write(output[:-len(sep)] + end)
    
def print_redef_2(*args, sep=' ', end='\n', file=sys.stdout):
    output = ''
    for arg in args:
        output += str(arg) + str(sep)
    file.write(output[:-len(sep)] + end)


print_redef(1, 2, 3)
print_redef(1, 2, 3, sep='--')
print_redef(1, [2, 20], (3, 30), sep='--')

print_redef_2(1, 2, 3)
print_redef_2(1, 2, 3, sep='__', end='!\n')
print_redef_2(1, 2, 3, sep='__')

第19章 函数的高级话题

函数设计概念

  • 耦合性:对于输入使用参数并且对于输出使用return语句
  • 耦合性:只有在真正必要的情况下使用全局变量
  • 耦合性:不要改变可变类型的参数,除非调用者希望这么做。
  • 聚合性:每一个函数都应该有一个单一的,统一的目标。
  • 大小:每一个函数应该相对较小
  • 耦合:避免直接改变在另一个模块中的变量。

递归

即函数内调用本身

In [176]: def mysum(L):
     ...:     print(L)
     ...:     if not L:
     ...:         return 0
     ...:     else:
     ...:         return L[0]+mysum(L[1:])
     ...:

In [177]:

In [177]: mysum([1,2,3,4,5,6])
[1, 2, 3, 4, 5, 6]
[2, 3, 4, 5, 6]
[3, 4, 5, 6]
[4, 5, 6]
[5, 6]
[6]
[]
# 计算一个嵌套的子列表结构中所有数字和
In [184]: def sumtree(L):
     ...:     tot=0
     ...:     for x in L:
     ...:         if not isinstance(x,list):
     ...:             tot+=x
     ...:         else:
     ...:             tot+=sumtree(x)
     ...:     return tot
     ...:
     ...:

In [185]: sumtree([1,[2,[3,4,[5,6,[7,8],10]]]])
Out[185]: 46

函数对象:属性和注解

匿名函数

  • lambda是一个表达式,不是一个语句
  • lambda的主题是一个单个的表达式,而不是一个代码块

第20章 迭代和解析,第二部分

python对于延迟的只是:

  • 生成器函数
  • 生成器表达式

生成器函数

状态挂起

生成器yield一个值,而不是return一个值,yield语句挂起函数后,向调用者发送一个值,保留足够的状态能够从它离开的地方继续。

迭代协议整合

可迭代对象定义了一个<next>方法,它要么返回迭代中的下一项,要么引发一个特殊的StopIteration。一个对象的迭代器用iter内置函数接受。

生成器是单迭代器对象

# 实现map函数
def mymap(func, *args):
    res = []
    for arg in zip(*args):
        res.append(func(*arg))
    return res


def mymap(func, *args):
    return [func(*arg) for arg in zip(*args)]


# yield版本,包含进入list中即可一次产生所有值
def mymap(func, *args):
    for arg in zip(*args):
        yield func(*arg)


def mymap(func, *args):
    return (func(*arg) for arg in zip(*args))


# print(mymap(abs,[1,2,-1,-100,2]))tuple
# print(list(mymap(abs,[1,2,-1,-100,2])))

# 截断zip
def myzip(*seqs):
    seqs = [list(S) for S in seqs]
    print(seqs)
    res = []
    # 利用数组原处改动的原理
    while all(seqs):
        #  相当于列表解析
        res.append(tuple(S.pop(0) for S in seqs))
    return res


# 不截断zip
def myzip(*seqs, pad=None):
    seqs = [list(S) for S in seqs]
    res = []
    while any(seqs):
        # pop完list之后是一个空数组
        res.append(tuple((S.pop(0) if S else pad) for S in seqs))

    return res


# 改成yield版本
def myzip(*seqs):
    seqs = [list(S) for S in seqs]
    while all(seqs):
        yield tuple(S.pop(0) for S in seqs)


def myzip(*seqs, pad=None):
    seqs = [list(S) for S in seqs]
    while any(seqs):
        # pop完list之后是一个空数组
        yield tuple((S.pop(0) if S else pad) for S in seqs)


# print(list(myzip([1,2,3],[4,5,6,7,8],[100,299,222])))

# 采用了判断长度的方法,返回值换成圆括号,就是生成器
def myzip(*seqs):
    minlen = min(len(S) for S in seqs)
    return [tuple(S[i] for S in seqs) for i in range(minlen)]




def myzip(*seqs, pad=None):
    maxlen = max(len(S) for S in seqs)
    return [tuple((S[i] if len(S) > i else pad) for S in seqs) for i in range(maxlen)]

# 为什么这里while iters 可以知道长度。
def myzip(*args):
    iters=list(map(iter,args))
    while iters:
        res=[next(i) for i in iters]
        yield tuple(res)

print(list(myzip([1, 3, 4, 5], [6, 7, 8, 9, 10])))

列表解析的语法

In [238]: [x*x for x in range(5)]
Out[238]: [0, 1, 4, 9, 16]

In [239]: [x*x for x in range(5) if x%2 == 0]
Out[239]: [0, 4, 16]

In [240]: [x*x if x%2 ==0 for x in range(5)]
  File "<ipython-input-240-7254a199a58d>", line 1
    [x*x if x%2 ==0 for x in range(5)]
                      ^
SyntaxError: invalid syntax


In [241]: [(x*x if x%2 ==0) for x in range(5)]
  File "<ipython-input-241-100abab91a2e>", line 1
    [(x*x if x%2 ==0) for x in range(5)]
                    ^
SyntaxError: invalid syntax


In [242]: [(x*x if x%2 ==0 else 0) for x in range(5)]
Out[242]: [0, 0, 4, 0, 16]

In [243]: [(x*x if x%2 ==0 else None) for x in range(5)]
Out[243]: [0, None, 4, None, 16]

注意map函数可能比列表解析快,他俩都比for要快的多

函数的陷阱

  • 本地变量是静态检测的。当编译def代码时,不是通过发现赋值语句在运行时进行检测的。python看到赋值语句,就是认为赋值语句就是本地变量。
  • 默认和可变对象。这个参数看起来是全局变量,其实是本地变量,它不会与其他变量发生冲突。
In [245]: def saver(x=[]):
     ...:     x.append(1)
     ...:     print(x)
     ...:

In [246]: saver([2])
[2, 1]

In [247]: saver()
[1]

In [248]: saver()
[1, 1]

In [249]: saver()
[1, 1, 1]

In [250]: saver()
[1, 1, 1, 1
 
In [251]: def saver(x=None):
     ...:     if x is None:
     ...:         x=[]
     ...:     x.append(1)
     ...:     print(x)
     ...:

In [252]: saver([2])
[2, 1]

In [253]: saver([2,2])
[2, 2, 1]

In [254]: saver()
[1]

In [255]: saver()
[1]

In [256]: saver()
[1]
 
 
# 利用函数的属性记录状态
 In [257]: def saver():
     ...:     saver.x.append(1)
     ...:     print(saver.x)
     ...:

In [258]: saver.x=[]

In [259]: saver()
[1]

In [260]: saver()
[1, 1]

In [261]: saver()
[1, 1, 1]

python迭代器和生成器的区别

relations
  1. 容器(container):

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特例,并不是所有的元素都放在内存,比如迭代器和生成器对象)在Python中,常见的容器对象有:

  • list, deque, ....
  • set, frozensets, ....
  • dict, defaultdict, OrderedDict, Counter, ....
  • tuple, namedtuple, …
  • str
  1. 可迭代对象

但凡是可以返回一个迭代器的对象都可称之为可迭代对象,可迭代对象实现了__iter__方法,该方法返回一个迭代器对象。

  1. 迭代器(iterator)

那么什么迭代器呢?它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter____next__()(python2中实现next())方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常,至于它们到底是如何实现的这并不重要。

所以,迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值的时候给你返回。有很多关于迭代器的例子,比如itertools函数返回的都是迭代器对象。

  1. 生成器(generator)

生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面的类一样写__iter__()__next__()方法了,只需要一个yiled关键字。 生成器一定是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成值。

  1. 生成器表达式(generator expression)

生成器表达式是列表推倒式的生成器版本,看起来像列表推导式,但是它返回的是一个生成器对象而不是列表对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容