函数是组织好的,可复用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。
def函数名(形式参数列表):
"""文档字符串"""
函数体
• 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
• 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
• 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
• 文档字符串可以通过函数名._doc_访问,也可以通过内建的help函数获取
• 函数内容以冒号起始,并且缩进。
• return [表达式] 结束函数,return后的语句不会被执行。
• 通过return语句可以选择性地返回一个值(或元组)给调用方。函数没有return语句或是执行了不返回任何值的return语句相当于返回 None。
• 函数的调用:
函数名(实际参数列表)
• 函数定义时括弧内为形参(formal parameter),一个函数可以没有形参,但是括弧必须要有,这样表示该函数不接受参数
• 函数调用时向其传递实参(actual parameter),将实参的值或引用传递给形参
• Python默认通过引用进行函数的参数传递,意味着不需要创建额外的拷贝就可以在程序中传递(很大的)对象,提高了效率,但是当参数传递像列表、字典这样的可变对象(mutable)时,函数中对可变对象参数的在原处的修改能够影响调用者(即改变实参)
• 如果不想要函数内部,对可变对象参数在原处的修改,影响外部,可以简单地创建一个可变对象的拷贝
• 调用函数时根据函数定义的参数位置来传递参数。
• 参数的顺序必须一一对应,且缺一不可
def 函数名([形参名,] 形参名=默认值, ……) :
函数体
• 默认值参数必须出现在函数参数列表的最右端,且任何一个默认值参数右边不能有非默认值参数。
• 调用带有默认值参数的函数时,可以不对默认值参数进行赋值,也可以赋值,具有较大的灵活性
• 使用函数名._defaults_可以查看函数所有默认值参数的当前值,其返回值为一个元组
• 多次调用函数且不为默认值参数传递值时,默认值参数只在第一次调用时进行解释。对于列表、字典类型的默认值参数,这一点可能会导致难以发现的错误。使用“空列表”作为函数的默认值参数是危险的。应当设置成None。
• 当然,如果是对可变对象的赋值操作(这样的操作生成了新的对象),没有关系:
• 考虑这一点也可以写出具有“Local caches / memorization”的函数
• 关键参数指实参,即调用函数时的传入的参数
• 通过关键参数传递,实参顺序可以和形参顺序不一致,并不影响传递结果,避免了用户需要牢记位置参数顺序的麻烦
(a)包裹参数
• *parameter包裹位置参数:用来接受多个实参并将其放在一个元组中
• **parameter包裹关键字参数:用来接受字典形式的实参
• 几种不同类型的参数可以混合使用,但这使得代码难懂,不要这样做
• 在调用函数时使用func(*parameter)和func(**parameter)可以执行相反的操作
(b)解包裹参数(参数解包)
• func(*parameter): 为含有多个形参的函数传递实参时,可使用列表、元组、字典、集合以及其它可迭代对象作为实参,并在实参名称前加一个星号,Python解释器将自动进行解包,然后将序列中的元素传递给多个形参,此时相当于位置参数传递
• 如果使用字典对象作为实参,则默认使用字典的“键”
• 一定要保证实参中元素个数与形参个数相等,否则出错
• func(**dict): 以“键-值”对的形式解包一个字典,此时相当于关键字参数传递,名字和函数定义的参数名必须要一一对应
在代码中,变量起作用的范围称为变量的作用域。一个变量在函数外部定义和在函数内部定义,其作用域是不同的
在函数内定义的变量只在该函数内起作用,称为局部变量。函数结束时,其局部变量被自动删除。
应当优先考虑使用局部变量。
•函数外部定义的变量为全局变量
• 全局变量会被局部变量屏蔽,如果局部变量的名字和全局变量相同,在函数内部将不能直接访问全局变量。对同名局部变量的操作只在内部作用域(局部命名空间)起作用,并不影响外部(全局)作用域中的全局变量。
• 如果全局变量被局部变量屏蔽,可以使用函数globals()[‘parameter’]来获取全局变量值
• 如果要重绑定全局变量(为全局变量赋新值,并要将这个赋值结果反映到函数外),可以在函数内用global将其声明为全局变量。
• 在函数内部直接将一个变量声明为全局变量,而在函数外没有声明,在调用这个函数之后,将增加为新的全局变量
• 命名空间就是一个从名称到对象的映射。命名空间可以看作一个字典,键为变量名,值是变量对应的值。
• 各个命名空间是独立没有关系的,一个命名空间中不能有重名,但是不同的命名空间可以重名而没有任何影响。
• 在Python程序执行期间,按照解释器执行的状态,可以将命名空间划分为以下3类:
• Local,局部命名空间,函数所拥有的命名空间。记录了函数中定义的所有变量,包括函数的入参、内部定义的局部变量。在函数被调用时才被创建,但函数返回结果或抛出异常时被删除。(每一个递归函数都拥有自己的命名空间)。
• Global,全局命名空间,模块所拥有的命名空间。记录了模块中定义的变量,包括模块中定义的函数、类、其他导入的模块、模块级的变量与常量。在模块被加载时创建,通常一直保留直到python解释器退出。
• Built-in,内建命名空间,Python内建的命名空间。放着内置的函数和异常,任何模块均可以访问。在Python解释器启动时创建,一直保留直到解释器退出。
• 一个作用域是指一段程序的文本区域,可以是一个函数或一段代码。一个变量的作用域是指该变量的有效范围。
• Python的作用域是静态作用域,变量的作用域源于它在代码中的位置。
• 作用域按照变量的定义位置可以划分为4类:
• Local(函数内部)局部作用域
• Enclosing(嵌套函数的外层函数内部)嵌套作用域(闭包)(见6.4)
• Global(模块全局)全局作用域
• Built-in(内建)内建作用域
x = int(10) #python内置作用域B
y = 2#当前模块中的全局变量G
defoutfuction():
outfx = 2#外层作用域E
def infunction():
infx = 3#局部作用域L
• Python解释器动态执行过程中,当遇到对变量进行解释时,会按照LEGB-Rule的顺序在命名空间里从里到外地寻找“变量-对象”映射,从而确定该变量对应的作用域(因此会发生优先级高的变量屏蔽优先级低的同名变量的情况)。因为命名空间是动态创建的,所以变量对应的作用域也会动态变化。
• 所有命名空间查找完成没有找到对应的变量,则抛出 NameError: name 'xxxx' is not defined的异常。
• Python除了def/class/lambda 外,其他如: if/elif/else/ try/except for/while并不能改变其作用域。定义在他们之内的变量,外部还是可以访问。
• 一定要在定义global后才能给外部作用域变量赋值
i=1
def func2():
i=i+1
func2();
#错误:UnboundLocalError: local variable 'i' referenced before assignment