python之函数与变量
python之函数与变量
-
函数简单介绍
-
函数的定义与调用
-
函数参数
-
变量与作用域
函数简单介绍
在数学里我们接触了函数,一般是数学家研究某个规律推导出的数学表达式,概括为f(x)的结果随着变量x的变化而改变。在编程语言中函数不像数学里的函数,数学里的函数的变量只能是数,而编程语言的函数变量有很多类型,这里面函数可以理解为人为逻辑化,函数是通过一个函数名封装好一串用来完成某一特定功能的逻辑。python语言也就是这样,使用函数的好处是实现代码的可重用性,提高代码可维护性、扩展性和可读性。总之,多使用函数肯定是很好的,现在起,多在代码里使用函数吧。
函数的定义与调用
函数的定义
定义函数的格式为:
def 函数名(参数1,参数2,...,参数n):
"""
函数使用说明、参数介绍等文档信息
"""
函数体(语句块)
return 值
说明:
-
函数名的命名规则要符合python中的命名要求。一般用小写字母和单下划线、数字等组合,和变量命名规则一样
-
def是定义函数的关键词,这个简写来自英文单词define
-
函数名后面是圆括号,括号里面,可以有参数列表,也可以没有参数
-
一定不要忘记了括号后面的冒号
-
函数体(语句块),相对于def缩进,按照python习惯,缩进四个空格
-
return语句 指定函数的返回值,可以不用指定返回值,不指定其实返回值就是None
示例:
定义无参函数:
无参函数:函数体内代码无需外部传入参数就可以执行。
def print_tag():
print('************')
print('hello,world!')
print('************')
print(print_tag())
#执行结果,可以看到无返回值的函数执行完就是返回None:
************
hello,world!
************
None
定义有参函数:
有参函数:函数体内代码依赖于外部传入参数才可以执行。
def add(a, b):
"""
计算并返回两个数的和
a: 被加数
b: 加数
"""
return a + b
print(add(2,3))
#运行结果:
5
定义空函数:
控函数一般用于占位,后期想好了代码再完成函数。
def func():
pass
特别说明:
函数定义阶段只检测语法,不会执行代码。如果定义的函数语法没有问题,但是逻辑有问题,不调用就不会报错。
例如:
def func():
x
#不调用就不会报错,如果调用就会报错,'x'没有定义
作用域关系也是在函数定义阶段就已经确定(后面作用域会涉及到)。
函数的调用
调用一个函数直接使用'函数名(参数)',函数调用必须遵循先定义后使用的原则。
函数参数
形参与实参:
形参:即形式参数,函数定义时指定的可以接受的参数即为形参,如下面的add(x,y)中x,y就是形参。
def add(x, y):
return x + y
实参: 即实际参数,调用函数时传递给函数参数的实际值即为实参。如下add(3,8)中3,8就是实参
def add(x, y):
return x + y
print(add(3,8))
可用参数类型:
-
位置参数(必选参数)
-
默认参数
-
关键字参数
-
不定长参数
位置参数
位置参数(必选参数):按照从左到右的顺序依次定义的参数,按位置定义的形参,必须被传值,传值个数必须一致。就是说传入的实参位置和个数都必须与形参一 一对应。
#定义函数,两个位置参数
def person(name, age):
print('NAME: %s' % name)
print('AGE: %d' % age)
#正确调用
>>> person('bob', 28)
NAME: bob
AGE: 18
#错误调用
>>> person(28,'bob')
NAME: 28
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
person(28,'bob')
File "<pyshell#2>", line 3, in person
print('AGE: %d' % age)
TypeError: %d format: a number is required, not str
>>>
>>> person('bob')
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
person('bob')
TypeError: person() missing 1 required positional argument: 'age'
>>>
>>> person('bob',28,'male')
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
person('bob',28,'male')
TypeError: person() takes 2 positional arguments but 3 were given
>>>
默认参数
在定义函数阶段,就已经为形参赋值,定义阶段有值,调用阶段可以不用传值。
默认参数注意事项:
-
必须放在位置形参后面
-
默认参数通常要定义成不可变类型(定义成可变类型也不会有问题)
-
默认参数只在定义阶段被赋值一次
#默认参数使用
>>> def person(name, age=28):
print('NAME: %s' % name)
print('AGE: %d' % age)
>>> person('bob')
NAME: bob
AGE: 28
>>> person('bob',30)
NAME: bob
AGE: 30
>>>
关键字参数
实参在定义时,按照key=value形式定义。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
关键字参数注意事项:
-
位置实参必须在关键字实参的前面
-
实参的形式可以用位置实参和关键字实参混合,但是一个形参不能重复传值
#关键字参数使用
>>> def person(name, age=28):
print('NAME: %s' % name)
print('AGE: %d' % age)
>>> person(name='bob',age=30)
NAME: bob
AGE: 30
>>> person('bob',age=25)
NAME: bob
AGE: 25
>>> person(age=25,'bob')
SyntaxError: positional argument follows keyword argument
>>> person(age=25,name='bob')
NAME: bob
AGE: 25
>>>
不定长参数
指长度可以改变的参数。通俗点来讲就是,可以传任意个参数(包括0个)。
按位置定义的可变长度的实参:*
按关键字定义的可变长度的实参:**
多个位置参数
在实参里遇到*()就按照位置参数解开传入
在实参里遇到**{}就按照关键字参数解开传入
def func(x,*arg): #这种方式接收,以字典的形式接收数据对象
print x #输出参数x的值
result = x
print arg #输出通过*arg方式得到的值
for i in arg:
result +=i
return result
print func(1,2,3,4,5,6,7,8,9) #赋给函数的参数个数不仅仅是2个
上述代码执行结果:
#执行结果
1 #这是函数体内的第一个print,参数x得到的值是1
(2, 3, 4, 5, 6, 7, 8, 9) #这是函数内的第二个print,参数arg得到的是一个元组
45
多个关键字参数
>>> def foo(**args): #这种方式接收,以字典的形式接收数据对象
print args
>>> foo(1,2,3) #这样就报错了
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 0 arguments (3 given)
>>> foo(a=1,b=2,c=3) #这样就可以了,因为有了键值对
{'a': 1, 'c': 3, 'b': 2}
总结
>>> def foo(x,y=2,*targs,**dargs):
print "x==>",x
print "y==>",y
print "targs_tuple==>",targs
print "dargs_dict==>",dargs
>>> foo('1x')
x==> 1x
y==> 2
targs_tuple==> ()
dargs_dict==> {}
>>> foo("1x","2y")
x==> 1x
y==> 2y
targs_tuple==> ()
dargs_dict==> {}
>>> foo("1x","2y","3t1","3t2")
x==> 1x
y==> 2y
targs_tuple==> ('3t1', '3t2')
dargs_dict==> {}
>>> foo("1x","2y","3t1","3t2",d1="4d1",d2="4d2")
x==> 1x
y==> 2y
targs_tuple==> ('3t1', '3t2')
dargs_dict==> {'d2': '4d2', 'd1': '4d1'}
变量与作用域
变量
变量名
变量名命名遵循下面规则:
-
字母或者下划线开头
-
由字母和数字下划线组成,中间不能有空格
-
不能和关键字重名(关键字:例如 if,while等)
变量赋值
-
创建对象
-
将变量名和对象进行绑定
变量赋值就是一个引用过程,赋值过程是变量名和对象进行绑定,变量名修改时进行解绑定。
python中回收机制
我们对变量进行赋值时,赋值对象的引用计数会加1,当我们修改变量或者变量作用域消失时候,引用计数减1,当对象的引用计数为0时,python解释器会释放这个对象,对内存进行回收。
导致引用计数+1的情况
-
对象被创建,例如a=23
-
对象被引用,例如b=a
-
对象被作为参数,传入到一个函数中,例如func(a)
-
对象作为一个元素,存储在容器中,例如list1=[a,a]
导致引用计数-1的情况
-
对象的别名被显式销毁,例如del a
-
对象的别名被赋予新的对象,例如a=24
-
一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
-
对象所在的容器被销毁,或从容器中删除对象
名称空间与作用域
名称空间
内置名称空间
随着Python解释器的启动而产生,比如python内置的一些函数:sun()、max()、min()等,这些函数随着python的启动就定义好了,所以在定义名称时不要与这些关键字重名
可以用以下方法查看python的内置函数:
import builtins
for i in dir(builtins):
print(i)
全局名称空间
py文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间
局部名称空间
如下示例:
# !/user/bin/env python
# -*- coding:utf-8 -*-
name = 'jack' #name全局变量
def func():
x = 1 #x局部变量
代码中:
变量name在全局名称空间
变量x在函数func()中,在局部名称空间
作用域
-
L (Local) 局部作用域
-
E (Enclosing) 闭包函数外的函数中
-
G (Global) 全局作用域
-
B (Built-in) 内建作用域
以 L --> E --> G -->B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。
局部名称空间 ---> 全局名称空间 ---> 内置名称空间
作用域
-
全局作用域:内置名称空间、全局名称空间
-
局部作用域:局部名称空间
查看全局作用域内的名字: globals()
查看局部作用域内的名字: locals()
说明
作用域关系也是在函数定义阶段就已经确定,与调用位置无关,无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系。
特别注意
在函数内部无法修改全局变量的值,只是说在函数内部无法修改全局不可变类型的值,像全局里的列表、字典等可变类型可以在函数内访问并且更改列表或者字典的一个元素。当然对于全局不可变类型要改变值可以使用global声明一下。
d = {'name':'tom','age':16}
l = [15,16,20]
x = 12
def foo():
print('hello,world!')
d['age'] = 20
l[2] = 50
x = 88
foo()
print(d)
print(l)
print(x)
#执行结果
hello,world!
{'name': 'tom', 'age': 20} #更改元素成功
[15, 16, 50] #更改元素成功
12 #更改失败
x = 12
def foo():
print('hello,world!')
global x
x = 88
foo()
print(x)
#执行结果
hello,world!
88 #global之后x更改成功,此时x是全局变量