Python学习笔记之函数

函数就是对程序逻辑进行结构化或过程化的一种编程方法。函数可以将我们编写的代码进行格式化,便于管理和理解,以及节约空间,减少代码的重复性。

函数的定义

函数就是对程序逻辑进行结构化或过程化的一种编程方法。和我们数学中学习的函数基本相似。编程语言中函数的初现,有利于我们将整块代码巧妙地隔离成易于管理的小块,将重复代码放进函数中而不是进行大量的拷贝,节省空间,有助于一致性。

函数 VS 过程

函数和过程都是可以被调用的实体。函数相当于一个黑盒子,给予输入(或者不带任何输入),经过一定的处理,最后可以返回结果。过程则是简单、特殊、无返回值的函数。但是在Python中,过程等价于函数,解释器会隐式地返回默认值None。

函数的创建

在Python中,函数使用def语句进行创建。

def function_name(arguments):
    'function_documents_string' # 函数的描述,可选,建议书写,可使代码阅读者更好地理解程序
    function_body_suite

函数声明与定义

类似C、C++等编程语言中,函数声明和函数的定义是区分的,但在Python中,两者视为一体。函数的声明包括提供函数名、参数名、参数类型。函数的定义包括函数声明部分,并且还有函数的具体代码。

函数属性

函数属性是Python的一个使用"."属性标志并拥有命名空间的特性。我们可以通过'.'来访问函数的属性。

>>> def func():
    'func() -- proper created doc string.'
    pass

>>> func.__doc__
'func() -- proper created doc string.'
>>> func.__name__
'func'

函数的调用与引用

在Python中,函数是可以被引用(访问或者以其他变量作为其别名,指向同一块内存地址)的。函数的调用与引用最简单的区别就是。将一个函数名赋值给另外一个变量就是函数的引用,将一个函数赋值给另外一个变量就是函数的调用。

>>> def func1():
print 'in func1().'


>>> func2 = func1 # 函数引用
>>> func3 = func1() # 函数调用
in func1().
>>> func2()
in func1().
>>> func1()
in func1().
>>> func1
<function func1 at 0x02B044F0>
>>> func2
<function func1 at 0x02B044F0>

函数参数

Python函数的参数集合包括了无参数、带参数、默认参数、关键字参数以及可变长度的参数。

无参数

Python函数中没有任何参数的传入,在函数调用时无需传入。

>>> def func1():
    print 'Hello World.'

>>> func1()
Hello World.

带参数

在调用函数时,必须传入参数且个数与定义函数的参数个数相等。

>>> def func2(arg):
    print 'Hello ', arg

>>> func2('World')
Hello  World

默认参数

默认参数是指函数的定义时已经给出参数值。在调用函数时,如果没有提供参数将会按照默认参数值,如果重新给出,则按给出的参数值传入。

>>> def func3(arg = 'World'):
    print 'Hello ', arg

>>> func3()
Hello  World
>>> func3('Python')
Hello  Python

关键字参数

关键字参数是指一顺序或者不按顺序传入,但是带有参数列表中曾定义过的关键字。

>>> def cal(a,b = 4):
    print '%d + %d = %d' % (a, b, a + b)

>>> cal(2,3)
2 + 3 = 5
>>> cal(3)
3 + 4 = 7
>>> cal(a = 2,b = 8)
2 + 8 = 10

注意:所有必须参数(在函数定义时,无默认值传入的参数)都要在默认参数之前。

可变长度的参数

有时,我们可能需要用函数处理可变数量的参数,这时就需要可变长度的参数列表。变长的参数在函数声明时并不是显式命名的。在函数调用中使用*符号来指定元组和字典的元素作为非关键字以及关键字参数的方法。

非关键字可变长参数(元组)

当函数调用时,所有的形参都将值赋给了在函数声明中相对应的局部变量,剩下的非关键字参数按顺序插入到一个元组中便于访问。元组保存了所有传递给函数的"额外"的参数,如果没有给出额外的参数,元组将为空。

可变长的参数元组必须在默认和关键字参数之后。

def function_name(formal_args, *vargs_tuple):
    'function_documentation_string'
    function_body_suite

# eg
>>> def tupleVarArgs(arg1, arg2 = 'default', *theRest):
    'display regular args and non_keyword variable args'
    print 'formal arg 1:', arg1
    print 'formal arg 2:', arg2
    for eachXtrArg in theRest:
        print 'another arg:', eachXtrArg

>>> tupleVarArgs('abc')
formal arg 1: abc
formal arg 2: default
>>> tupleVarArgs('abc', 123, 'xyz', 37.2)
formal arg 1: abc
formal arg 2: 123
another arg: xyz
another arg: 37.2

关键字可变长参数(字典)

当我们有不定数目的或者额外的关键字参数时,我们需要将参数放入一个字典(字典的键为参数名,值对为相应的参数值)中。需要注意的是关键字可变长参数的位置在所有参数之后。

def function_name(formal_args,*vargs_tuple, **vargs_dict):
    'function_documentation_string'
    function_body_suite

# eg
>>> def dictVarArgs(arg1, arg2 = 'default', **theRest):
    'display regular args and non_keyword variable args'
    print 'formal arg 1:', arg1
    print 'formal arg 2:', arg2
    for eachXtrArg in theRest.keys():
        print 'Xtra arg %s: %s' % (eachXtrArg, str(theRest[eachXtrArg]))

    
>>> dictVarArgs(789,'hi',language = 'Python')
formal arg 1: 789
formal arg 2: hi
Xtra arg language: Python

变量作用域

变量的作用域是定义为其声明在程序里的可应用范围。变量可以分为全局变量和局部变量。在Python中,一个模块中最高级别的变量具有全局作用域,也就是全局变量,它的一个特点是除非被删除,否则将存活于整个脚本之中。局部变量是指在函数体内创建的,一旦函数完成,变量就会被销毁。我们需要注意的是小心地使用和全局变量同名的局部变量。

如果在函数体内声明一个和全局变量同名的局部变量,全局变量的值在函数体内就会被覆盖,而该变量的全局特性和局部特性就不那么清晰。如果要在函数体内使用全局变量时,请在变量名前加上关键字global

>>> name = 'Python'
>>> def func():
    global name
    print 'I like',name

>>> func()
I like Python

内嵌函数

内嵌函数是指在函数体内创建另外一个函数。内嵌函数在外部函数的作用域内,如果没有对内嵌函数的外部引用,那么除了在外部函数体内,任何地方都无法访问内嵌函数。

>>> def foo():
        def bar():
            print 'bar() called.'
        print 'foo() called.'
        return bar

>>> foo()
foo() called.
bar() called.
>>> bar()

Traceback (most recent call last):
  File "<pyshell#88>", line 1, in <module>
    bar()
NameError: name 'bar' is not defined

内嵌函数更重要的是体现在'闭包'上面。

闭包(closure)

如果内嵌函数的定义中包含了在外部函数里定义的对象的引用,内部函数就会被称为闭包

定义在外部函数内的但由内嵌函数引用或者使用的变量被称为自由变量

闭包的词法变量不属于全局名称空间域或者局部的,是属于其他的名称空间,带有流浪的作用域。

>>> def func(name):
        def inner_func(age):
            print 'name:', name, 'age:', age
        return inner_func

>>> name = func('Python')
>>> name(20)
name: Python age: 20

在这个例子中,当我们调用func时,就会产生一个闭包inner_func,并且该闭包持有自由变量name,当func的生命周期结束后,name这个变量依然存在,因为他被闭包引用了,暂时不会被回收。

在Python中,函数对象有一个closure属性,我们可以通过这个属性看看闭包的一些细节。

>>> print name.__closure__
(<cell at 0x02B02FD0: str object at 0x026913E0>,)
>>> print type(name.__closure__[0])
<type 'cell'>
>>> print name.__closure__[0].cell_contents
Python

通过上面的理解,我们可以得出在Python中创建一个闭包可以归结为以下三点:

  • 闭包函数必须有内嵌函数
  • 内嵌函数需要引用外部函数中的变量
  • 闭包函数必须返回内嵌函数

匿名函数

在Python中,可以使用关键字lambda来创建匿名函数。和标准函数几乎一样,只不过这个函数的定义和声明必须卸载同一行。匿名函数同样可以被调用或者引用。

标准函数:

def add(x, y):
    return x + y

函数add()改写单行函数:

def add(x, y): return x + y

函数add()改写匿名函数:

lambda x, y: x + y

看上去匿名函数是一个函数的单行版本,匿名函数运行起来就像一个函数,当被调用时,就会创建一个框架对象。

内建函数filter()、map()、reduce()

在列表的学习过程中,接触过这些函数式编程的内建函数,现在,使用lambda的形式来重新认识下。

filter()

filter(function,sequence)方法是返回一个sequence(序列),包括了给定序列中所有调用function(item)后返回值为true的元素。filter()函数可以看作一个过滤器来使用。

>>> def div_3_or_div_5(x):
    return x % 3 == 0 or x % 5 == 0
>>> filter(div_3_or_div_5,range(0,21))
[0, 3, 5, 6, 9, 10, 12, 15, 18, 20]

改写后:

>>> filter(lambda x: x % 3 == 0 or x % 5 == 0, range(0,21))
[0, 3, 5, 6, 9, 10, 12, 15, 18, 20]

map()

map(function,sequence)方法是为每一个元素调用function(item)并将返回值组成一个新的列表返回。

>>> def cube(x):
    return x * x * x
>>> map(cube,range(0,4))
[0, 1, 8, 27]

改写后:

>>> map(lambda x: x * x * x, range(0,4))
[0, 1, 8, 27]

reduce()

reduce(function,sequence)方法是返回一个单值,他的构造方法是:首先以序列的前两个元素调用函数function,再以返回值和第三个参数调用,依次下去。

>>> def sum(seq):
        def add(x, y):
            return x + y
        return reduce(add, seq, 0)
>>> sum(range(1,101))
5050

改写后:

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

推荐阅读更多精彩内容

  • 要点: 函数式编程:注意不是“函数编程”,多了一个“式” 模块:如何使用模块 面向对象编程:面向对象的概念、属性、...
    victorsungo阅读 1,457评论 0 6
  • 86.复合 Cases 共享相同代码块的多个switch 分支 分支可以合并, 写在分支后用逗号分开。如果任何模式...
    无沣阅读 1,340评论 1 5
  • 教程总纲:http://www.runoob.com/python/python-tutorial.html 进阶...
    健康哥哥阅读 1,999评论 1 3
  • 风中的茉莉 是记忆里外婆的香气 仲夏夜的久不寐 只因梦跌落在了星河 庭院食瓜听蛙声声睡 醒来已非少年时 不见前日床...
    方条条阅读 218评论 0 0
  • 章节目录图片发自简书App 十三 只要你开心,我就会快乐 时间就像是一个行色匆匆的战士,朝着它前进的方向,昂首阔步...
    牛赋阅读 412评论 0 7