Python 函数及函数作用域

本文为廖雪峰Python教程的学习笔记
具体内容,可参考如下链接:
http://www.liaoxuefeng.com/

 

一、函数定义

函数是逻辑结构化和过程化的一种编程。过程:就是没有返回值的函数

函数是可以实现一些特定功能的小方法或是小程序。

在Python中有很多内建函数,当然随着学习的深入,你也可以学会创建对自己有用的函数。简单的理解下函数的概念,就是你编写了一些语句,为了方便使用这些语句,把这些语句组合在一起,给它起一个名字。使用的时候只要调用这个名字,就可以实现语句组的功能了。

在没用过函数之前,我们要计算一个数的幂时会用到**,方法是这样的:
>>>2**3   #此处为python 函数返回值  8
现在知道了函数,就可以用内建函数pow来计算乘方了:
>>>pow(2,3) #8

1)python内建函数,如何调用函数

python系统中自带的一些函数就叫做内建函数,Python内置的函数可以通过官方文档查看。也可以通过help()查看帮助信息。比如:dir()type()等等,不需要我们自己编写。还有一种是第三方函数,就是其它程序员编好的一些函数,共享给大家使用。前面说的这两种函数都是拿来就可以直接使用的。最后就是我们自己编些的方便自己工作学习用的函数,就叫做自定义函数了。

函数调用的方法虽然没讲解,但以前面的案例中已经使用过了。pow()就是一个内建函数,系统自带的。只要正确使用函数名,并添写好参数就可以使用了。

2)定义函数function的方法

语句定义函数,在缩进块中编写函数体,返回值用return语句返回,如果没有return语句,返回值为None,等价于return 。
根据需要,return语句也可以返回多个值组成的tuple,可以不写(),多个值之间用’,’隔开,调用函数时可以用多个变量接受返回值,多个变量按位置赋值。

定义函数需要用到def语句,具体的定义函数语法格式如图所示:

def 函数名(参数):

      代码块

3)函数名的命名规则

    (1)函数名必须以下划线或者字母开头,可以包含任意字母、数字或者下划线的组合。不能使用任何的标点符号。

       (2) 函数名是区分大小写的

     (3)函数名不能使保留字(关键字)

定义函数需要注意的几个事项:

1)def开头,代表定义函数

2)def和函数名中间要敲定一个空格

3)之后是函数名,这个名字用户自己起的,方便自己使用就好

4)函数名后跟圆括号(),代表定义的是函数,里边可以加参数

5)圆括号()后一定要加冒号:这个很重要,不要忘记

6)代码块部分,是由语句组成,要有缩进

7)函数要有返回值return

下面写个完整个的函数范例给大家参考:

def welcome(name):
    print("welcome %s" % name)

welcome("rxz")

结果:welcome rxz

有返回值的函数:

#自定义取绝对值函数
def my_abs(x):
    #isinstance()检查参数类型
    if not isinstance(x, (int, float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x

 将my_abs()保存在function.py文件中,在该目录下启动Python解释器,导入my_abs()函数,就可以调用函数 

>>> from function import my_abs
>>> print(my_abs(-1))
1
>>> print(my_abs(1))
1

pass语句可以用做占位符,表示什么都不做,可用来定义一个空函数。在没想好具体语句的时候,可以用pass语句使代码先运行起来。

#定义空函数
def nop():
    pass
#放在if语句中
if a > b:
    pass

二、函数参数

1、形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。

2、实参可以是常量,变量,表达式,函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确认的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值。

def calc(x,y):  #------------>形参

       res = x**y

      return res

c = calc (a,b)  #-------->实参

print(c)

3、默认参数

将变化小的参数作为默认参数,如果没有传入或改变默认参数的值,则使用默认参数。如果不需要改变默认参数,不用传入默认参数;需要改变默认参数时,传入默认参数即可。
必选参数在前,默认参数在后。有多个默认参数时,调用的时候,既可以按顺序提供默认参数,也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。表示该参数用传进去的值,其他默认参数继续使用默认值。

例如,学生注册信息,设置年龄和城市为默认参数

def student(name, gender, age = 29, city = 'suzhou'):
    print('name = ', name, '\tgender = ', gender, '\tage = ', age, '\tcity = ', city)

student('alex', 'M')
student('Lily', 'F', 6, 'Tianjin')
student('rose', 'F', 8)
student('Jack', 'M', city = 'Xi\'an')


#分别用四种方式调用enroll函数后,输出结果如下所示
name =  alex     gender =  M     age =  29    city =  suzhou
name =  Lily     gender =  F     age =  6     city =  Tianjin
name =  rose     gender =  F     age =  8     city =  suzhou
name =  Jack     gender =  M     age =  29    city =  Xi'an

默认参数必须指向不变对象。因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

def add_end(L = []):
    L.append('end')
    return L

#正常调用
print(add_end([1, 2, 3]))
print(add_end(['a', 'b', 'c']))
#使用默认参数调用
print(add_end())
print(add_end())


#函数add_end()在正常调用时,输出结果为:

[1, 2, 3, 'end']
['a', 'b', 'c', 'end']


#调用默认参数时,结果为:

['end']
['end', 'end']

Python函数在定义时,默认参数的值已经被计算出来。因为列表为可变对象,L指向可变对象,每次调用函数时,如果改变了L的内容,下次调用时,默认参数的内容就变了,不是函数定义时的列表了。函数add_end()可做如下修改,用不可变对象None实现:

def add_end(L = None):
    if L is None:
        L = []
    L.append('end')
    return L
#使用默认参数调用
print(add_end())
print(add_end())


#再使用默认参数调用时,结果为:

['end']
['end']

4、可变参数

传入的参数个数可变,可以为0到任意个。
在参数前加*实现可变参数。
可变参数在函数调用时自动组装为一个tuple。
在列表和元组前加*,可以将列表或元组中的元素变为可变参数传入函数。

def add_sum(*nums):
    sum = 0
    for num in nums:
        sum += num
    return sum

print(add_sum(1, 2, 3))
L = [1, 2, 3, 4]
print(add_sum(*L))
T = (1, 2)
print(add_sum(*T))


#运行结果为:

6
10
3

5、关键字参数

将任意个含参数名的参数组装为一个dict。
在参数前加**实现关键字参数。
在dict前加**,可以将dict作为为关键字参数传入函数。传入的为dict的拷贝,在函数中的修改不会影响函数外的dict。
例如,函数person()包含必选关键字name和age,还包含关键字参数kw

def person(name, age, city = 'Beijing', country = 'china', **kw):
    print('name: ', name, '\tage: ', age, '\tcity: ', city, '\tcountry: ', country, '\tother: ', kw)
#dict
other = {'gender': 'F', 'height': 168}
person('Tom', 18)
person('Lily', 20, city = 'Tianjin', gender = 'F', hobby = 'read')
person('Tim', 15, gender = 'M', city = 'Beijing')
#将dict作为关键字参数传入
person('Ann', 16, **other)

  运行结果如下:

name:  Tom  age:  18    city:  Beijing  country:  china     other:  {}
name:  Lily     age:  20    city:  Tianjin  country:  china     other:  {'gender': 'F', 'hobby': 'read'}
name:  Tim  age:  15    city:  Beijing  country:  china     other:  {'gender': 'M'}
name:  Ann  age:  16    city:  Beijing  country:  china     other:  {'gender': 'F', 

6、命名关键字参数

关键字参数可以传入任意多个名字,对名字没有限制。
如果要限制关键字参数的名字,需要在参数列表中使用*,*之后的关键字为命名关键字,只接受这些参数作为关键字名字。
如果已经有可变参数,不需要特殊分隔符*

def person(name, age, *, city, country):
    print('name: ', name, '\tage: ', age, '\tcity: ', city, '\tcountry: ', country)
#如果已经有可变参数,不需要特殊分隔符*
def person1(name, age, *args, city, country):
    print('name: ', name, '\tage: ', age, '\tcity: ', city, '\tcountry: ', country)

调用函数时,必须传入参数名,位置可以颠倒。

person('Tom', 18, city = 'Tianjin',country = 'china')
person('Tom', 18, country = 'china', city = 'Tianjin')
person1('Tom', 18, city = 'Tianjin',country = 'china')
person1('Tom', 18, country = 'china', city = 'Tianjin')

该函数只接受两个位置参数,如果调用时不用函数名(person(‘Tom’, 18, ‘Tianjin’, ‘china’)), Python解释器认为传入了四个位置参数,会报错。

如果命名关键字参数有缺省值,调用时可以不传入该参数。

7、组合参数

不同类型的函数可以组合使用,参数定义的顺序必须为:必选参数,默认参数,可变参数,命名关键字参数和关键字参数。

def func1(a, b, c = 0, *d, e, **kw):
    print(a, '\t', b, '\t', c, '\t', d, '\t', e, '\t', kw)

def func2(a, b, c = 0, *, d, e, **kw):
    print(a, '\t', b, '\t', c, '\t', d, '\t', e, '\t', kw)

def func3(a, b, c):
    print(a, '\t', b, '\t', c)

t = (3, 4, 5, 6, 7, 8, 9)
l = [3, 4, 5, 6, 7, 8, 9]
t1 = (4, 5, 6)
dic = {'name': 'Z', 'gender': 'M'}
print('func1')
func1(1, 2, e = 4, d = 3)
func1(1, 2, d = 3, e = 4)
func1(1, 2, d = 3, e = 4, name = 'L', gender = 'F')
func1(*t, e = 10)
func1(*l, e = 10)
func1(*t, e = 11, **dic)
func1(*l, e = 11, **dic)
print('func2')
func2(1, 2, d = 3, e = 4)
func2(1, 2, d = 3, e = 4, name = 'L', gender = 'F')
func2(*t1, d = 20, e = 20)
func2(*t1, d = 21, e = 21, **dic)
func3(*t1)

输出为:

func1
1    2   0   ()      4   {'d': 3}
1    2   0   ()      4   {'d': 3}
1    2   0   ()      4   {'name': 'L', 'gender': 'F', 'd': 3}
3    4   5   (6, 7, 8, 9)    10      {}
3    4   5   (6, 7, 8, 9)    10      {}
3    4   5   (6, 7, 8, 9)    11      {'name': 'Z', 'gender': 'M'}
3    4   5   (6, 7, 8, 9)    11      {'name': 'Z', 'gender': 'M'}
func2
1    2   0   3   4   {}
1    2   0   3   4   {'name': 'L', 'gender': 'F'}
4    5   6   20      20      {}
4    5   6   21      21      {'name': 'Z', 'gender': 'M'}
4    5   6

func1中,
d为可变参数,可以为空,d=3被认为是关键字参数;
e为命名关键字参数,调用时必须使用名字调用;
用*t, *l, *dic调用时,*t, *l的前三个元素作为必选参数a, b, 默认参数c,后面的部分作为命名关键字e = 10,剩下的部分作为可变参数构成tuple d = (6, 7, 8, 9), **dic作为关键字参数。
func2中,
d, e均为命名关键字,必须使用名字调用。
因为不包含可变参数,如果要使用tuple, list调用,tuple和list的长度必须不超过固定参数加默认参数的长度,所以只能使用t1进行调用。

8、位置参数和关键字

标注调用“实参与形参位置一一对应;关键字调用:位置无须固定;

def func1(a, b, c):
    print(a)
    print(b)
    print(c)

#位置参数:实参与形参位置一一对应,缺一不行,多一个也不行
func1(1, 2, 3)

#关键字参数,无须固定位置,缺一不行,多一个也不行
function(b=1,a=2,c=3)

注意:位置参数必须在关键参数左边

使用函数的好处:

1)代码重用

2)保持一致性,易维护

3)可扩展性

三、函数的返回值

要想获取函数的执行结果,就可以用return语句把结果返回

注意:

1)函数在执行过程中只要遇到return语句,就会停止执行并且返回结果,也可以理解return语句的出现就代表函数的结束

2)如果未在函数中指定return,那么这个函数的返回结果就是None

3)return多个对象,解释器会把这么多的对象组装成一个元组 ,元组在作为一个整体输出

def add(a,b):
    res = a+b
    return res

result = add(8,9)
print("result = %s"% result)

 四、作用域

4.1、作用域介绍

Python中的作用域分4种情况

   1)L:local,局部作用域,即函数中定义的变量

   2)E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但是不是全局的

   3)G:global,全局变量,就是模块级别定义的变量

   4)B:built-in,系统固定模块里面的变量,比如:int,bytearray等,搜索变量的优先级顺序依次是:

         作用域局部  > 外出作用域 > 当前模块中的全局变量  > python内置作用域

         也就是:LEGB

x = int(2.9)  #int built_in
g_count = 0  # gloab

def outer():
    a =10  # enclosing
    def inner():
        i_count = 2 # local
        print(i_count)
    #print(i_count)  # 找不到

    inner()
    print(a)

outer()

4.3、变量修改

一个不再局部作用域的变量默认是只读,如果试图为其绑定一个新的值,Python认为是在当前的局部作用域里创建一个新的变量,也就是说在当前局部作用域中,如果直接使用外部作用域的变量,那么这个变量是只读的,不能修改的

g_count = 10  # gloab

def outer():
    #print(g_count)

    g_count = 100
   
    print(g_count )

outer()


def outer1():
    glocal g_count

    g_count = 100
   
    print(g_count )

outer()

 

4.3小结

    1)变量查找顺序:LEGB,作用域局部  > 外出作用域 > 当前模块中的全局变量  > python内置作用域

    2)只有模块,类,及函数才能引入新的作用域

    3) 对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量

    4)内部作用域要修改外部作用域变量的值时,全局变量要是用global关键字,嵌套作用域变量要使用nonlocal关键字,

        nonloal是python3新增的关键字,有了这个关键字,就能完美的实现闭包了。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值