Python函数篇
1.函数名的命名规则:
- 函数名必须以下划线或字母开头,可以包含任意字母、数字或下划线的组合。不能使用任何的标点符号;
- 函数名是区分大小写的。
- 函数名不能是保留字。
2. 形参和实参
形参:形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)
实参:实际参数,调用函数时传给函数的参数,可以是常量,变量,表达式,函数,传给形参
区别:形参是虚拟的,不占用内存空间,.形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参
3.参数
关键字参数:使用参数名提供参数叫做关键字参数。它的主要作用在于可以明确每个参数的作用。关键字参数最厉害的地方在于可以在函数中给参数提供默认值。
下面例子的必须参数也叫位置参数,因为它们的位置比它们的名字还要重要。
参数前的星号将所有值放置在同一个元组中。可以说是将这些值收集起来,然后使用。
两个星号能处理关键字参数的收集操作。
#必须参数 def f(name,age): print("My name is: %s and my age is: %d"%(name,age)) f('greg',18) #关键字参数 #f(16,'greg')报错 f(age=16,name='greg') #默认参数 def print_info(name, age, sex='male'): print('Name:%s' % name) print('age:%s' % age) print('Sex:%s' % sex) return print_info('Greg', 18) print_info('Wirt', 40, 'female') #不定长参数 def add(*args):#加法器 print(args) sum=0 for i in args: sum+=i print(sum) add(1,2,3,4,5) #加了星号(*)的变量名会存放所有未命名的变量参数。而加(**)的变量名会存放命名的变量参数 def p(*args,**kwargs): print(args) print(kwargs) for i in kwargs: print('%s:%s' % (i, kwargs[i])) # 根据参数可以打印任意相关信息了 p('greg',18,'male',job='IT',hobby="girls")
参数使用
def a(*args): print(args) a(*[1, 2, 5]) def b(**kargs): print(kargs) b(**{'name': 'alex'}) def c(x, y, d): return d(x) + d(y) res = c(3, -6, abs) print(res) def foo(): x=3 def bar(): return x return bar def func(name, *args, **kwargs): # def print_info(name,**kwargs,*args):报错 print('Name:%s' % name) print('args:', args) print('kwargs:', kwargs) return func('greg', 18, hobby='girl', nationality='Chinese', ability='Python')
四 函数的返回值
要想获取函数的执行结果,就可以用return语句把结果返回
注意:
- 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,也可以理解为 return 语句代表着函数的结束
- 如果未在函数中指定return,那这个函数的返回值为None
- return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出。
五 作用域
5.1 作用域介绍
python中的作用域分4种情况:
- L:local,局部作用域,即函数中定义的变量;
- E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
- G:globa,全局变量,就是模块级别定义的变量;
- B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
#__author: greg #date: 2017/9/6 9:02 x = int(2.9) # int built-in g_count = 0 # global def outer(): o_count = 1 # enclosing def inner(): i_count = 2 # local print(o_count) #1 # print(i_count) 找不到 inner() outer() # print(o_count) #找不到
当然,local和enclosing是相对的,enclosing变量相对上层来说也是local。
块级作用域:
# 块级作用域 if 1 == 1: name = "greg" print(name) #greg for i in range(10): age = i print(age) #9
代码执行成功,没有问题;在Java/C#中,执行上面的代码会提示name,age没有定义,而在Python中可以执行成功,这是因为在Python中是没有块级作用域的,代码块里的变量,外部可以调用,所以可运行成功;
作用域产生
>>> x=1 >>> scope=vars() >>> scope['x'] 1 >>> scope['x']+=1 >>> x 2
在执行x=1后,名称x引用到值1.这就像用字典一样,键引用值,当然,变量和所对应的值用的是这个不可见的字典,实际上这么说已经很接近真实情况了,内建的vars函数可以返回这个字典。这类不可见字典叫做命名空间或者作用域。除了全局作用于外,每个函数调用都会创建一个新的作用域:函数内的变量被称为局部变量。
全局变量可以使用globals函数获取全局变量值,该函数的近亲是vars,它可以返回全局变量的字典(locals返回局部变量的字典)
>>> def combine(parameter): ... print(parameter+globals()['parameter']) ... >>> parameter='berry' >>> combine('melon') melonberry
首先看函数的嵌套:
>>> def foo(): ... def bar(): ... print("Hello,world") ... bar() ... >>> foo() Hello,world
嵌套作用域:
>>> def multiplier(factor): ... def multiplyByFactor(number): ... return number*factor ... return multiplyByFactor ... >>> double=multiplier(x) >>> double(5) 5 >>> triple=multiplier(3) >>> triple(3) 9 >>> multiplier(5)(4) 20
类似multiplyByFactor函数存储子封闭作用域的行为叫闭包closure。
外部作用域的变量一般来说是不能进行重新绑定的。nonlocal关键字被引入。它和global关键字的使用方式类似,可以让用户对外部作用域(但并非全局作用域)的变量进行赋值。
>>> def foo():x=12 ... >>> x=1 >>> foo() >>> x 1
这里的foo函数改变了变量x,但在最后,x并没有改变。因为调用foo,新的命名空间就被创建了,它作用于foo内的代码块。
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)是不会引入新的作用域的,如下代码:
if 2>1: x = 1 print(x) # 1
这个是没有问题的,if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以使用。
def test(): x = 2 print(x) # NameError: name 'x2' is not defined
def、class、lambda是可以引入新作用域的。
>>> x=6 >>> def f(): ... print(x) ... x=5 ... >>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in f UnboundLocalError: local variable 'x' referenced before assignment
>>> def f2():
… x=5
… print(x)
…
>>> f2()
5
错误的原因在于print(x)时,解释器会在局部作用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,所以报错
global关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,
nonlocal关键字
global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了
代码如下:
count = 10 def outer(): global count print(count) #10 count = 100 print(count) #100 outer() print(count) #100 #nonlocal def outer2(): count = 10 def inner(): nonlocal count count = 20 print(count) #20 inner() print(count) #20 outer2()
总结一下
(1)变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;
(2)只有模块、类、及函数才能引入新作用域;
(3)对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量;
(4)内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。
面试题:
counter = 1 def doLotsOfStuff(): global counter for i in (1, 2, 3): counter += 1 doLotsOfStuff() print(counter)
答案是几?