Python攻克之路-函数

Python 函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
函数的特性:
  1.减少重复代码
  2.方便修改,更易扩展
  3.保持代码一致性

 

函数名的命名规则(与变量规则一样)
  a.函数名必须以下划线或字母开头,可以包含任意字母、数字或下划线的组合
  b.不能使用任何的标点符号
  c.函数名是区分大小写的
  d.函数名不能是保留字(python内置的)

 

函数的创建

In [1]: def f(): ##定义函数,f是函数名,()可以用来传参
...: print('ok') ##函数内容
...: 
In [2]: f() ##调用函数,要写括号
ok
In [4]: print(f) ##只写一个f是一个变量,变量指向的内存地址(print('ok'))
<function f at 0x7f6c730508c8>

  

函数传参数
一个参数

In [3]: def f(index):
...: print('function %s'%index) ##传参,实现动态打印的效果
...: 
In [4]: f(4)
function 4
In [5]: f(5)
function 5

两个参数:按顺序对应

In [1]: def add(x,y): ##形式的参数,定义时使用形参
...: print(x+y)
In [2]: add(4,5) ##实际参数,调用时使用实参
9
In [7]: def add(b,a):
...: print(a)
...: print(b)
In [8]: add(7,5)
5
7

使用函数实现日志记录(在没有使用日志记录模块的情况下)
描述:定义的def只是存储在内存中,没有真正执行,当执行action时,会传入参数1时,就执行def action1(1)里面的内容,再调用logger(1)

[root@zabbix day14]# cat log-function.py 
#!/usr/local/python3/bin/python3
def action1(n):
    print('starting action1..')
    with open('log-record','a') as f:
        f.write('end action%s\n'%n)

def action2(n):
    print('starting action2..')
    with open('log-record','a') as f:
        f.write('end action%s\n'%n)

def action3(n):
    print('starting action3..')
    with open('log-record','a') as f:
        f.write('end action%s\n'%n)

action1(1) 
action2(2)
action3(3)
[root@zabbix day14]# python3 log-function.py 
starting action1..
starting action2..
starting action3..
[root@zabbix day14]# cat log-record 
end action1
end action2
end action3

 优化:把日志调用提炼成函数,并记录时间

[root@zabbix day14]# cat log-function.py 
#!/usr/local/python3/bin/python3
import time                        ##时间模块
def logger(n):
    time_format = '%Y-%m-%d %X'
    current_time = time.strftime(time_format)
    with open('log-record','a') as f:
        f.write(current_time)
        f.write(' end action%s\n'%n)  #或者 f.write('%s end action%s\n'%(current_time,n))使用占位符


def action1(n):
    print('starting action1..')
    logger(n)

def action2(n):
    print('starting action2..')
    logger(n)

def action3(n):
    print('starting action3..')
    logger(n)

action1(11)
action2(22)
action3(33)
[root@zabbix day14]# python3 log-function.py 
starting action1..
starting action2..
starting action3..
[root@zabbix day14]# cat log-record 
2018-03-12 13:38:04 end action11
2018-03-12 13:38:04 end action22
2018-03-12 13:38:04 end action33

 

函数的参数分类
必需参数:须以正确的顺序传入函数,调用时的数量必须和声明时的一样

In [1]: def f(name,age):
...: print('I am %s,I am %d'%(name,age))
In [2]: f('reid',2)
I am reid,I am 2
In [3]: f('re',23)
I am re,I am 23

关键字参数:(在实参增加)

In [6]: def f(name,age):
...: print('I am %s,I am %d'%(name,age))
In [7]: f(age=11,name='tom')
I am tom,I am 11

默认参数:大多数情况下是固定值时就可以设定(在形参加)

[root@zabbix day14]# cat def.py
#!/usr/local/python3/bin/python3
def f(name,age,sex='male'):   ##设置默认
    print('Name: %s'%name)
    print('Age: %d'%age)
    print('Sex: %s'%sex)
f('tom',22)
f('tian',22)
f('lily',22,'female')        ##特殊情况时给一个新值
[root@zabbix day14]# python3 def.py 
Name: tom
Age: 22
Sex: male
Name: tian
Age: 22
Sex: male
Name: lily
Age: 22
Sex: female 

不定长参数

In [8]: def add(*args):  #*args取的是无命名参数
   ...:     print(args)
In [9]: add(1,2,3)   ##无命名参数
(1, 2, 3)            ##放在元组

一个星号的:把无命名参数放在元组
[root@zabbix day14]# cat add.py
#!/usr/local/python3/bin/python3    ##加法器
def add(*args):
    print(args)
    sum=0
    for i in args:
        sum+=i
    print(sum)
add(1,2,3,4)
[root@zabbix day14]# python3 add.py 
(1, 2, 3, 4)
10

两个星号的:把不定长的键值对存在字典
注:不定长参数的位置args放在左边,有键值对的参数放在右边

In [10]: def info(*args,**kwargs):
...: print(args)
...: print(kwargs)
...: info('jerry',12,'male',job='it',hobby='eat')
...: 
('jerry', 12, 'male') ##无命名在元组
{'job': 'it', 'hobby': 'eat'} ##键值对在字典

打印:遍历字典

[root@zabbix day14]# cat def.py 
#!/usr/local/python3/bin/python3
def f(*args,**kwargs):                 ##*args接收无命名参数,**kwargs接收有命名
    for i in kwargs:                   ##键和值
        print('%s:%s'%(i,kwargs[i]))
f(job='it',hobby='running')            ###键值对
[root@zabbix day14]# python3 def.py 
job:it
hobby:running

不定长和默认参数的使用
关键字参数优先最高--》默认参数放左边--》*args--》**kwargs
默认参数放在左边,调用不加参数的情况

[root@zabbix day14]# cat def.py 
#!/usr/local/python3/bin/python3
def f(sex='male',*args,**kwargs):
    print(sex)
    for i in kwargs:
        print('%s:%s'%(i,kwargs[i]))
f()     ##调用时不传任何参数显示正常,sex='male'不是必须的,后两个参数是不定长的包括0,所以*args可以是空元组,**kwargs是空字典
[root@zabbix day14]# python3 def.py 
male

赋sex一个新值

[root@zabbix day14]# cat def.py 
#!/usr/local/python3/bin/python3
def f(sex='male',*args,**kwargs):
    print(args)                   ###
    for i in kwargs:
        print('%s:%s'%(i,kwargs[i]))
f()
f('female',1,2,34)
f(1,2,34,'female')
f(1,2,34,'female',name='jerry')
[root@zabbix day14]# python3 def.py 
()
(1, 2, 34)   
##female和1,2,34一样,但是却没有被args接收,sex='male'占用一个位置,按必需参数的原则,但是按默认参数的原则sex='male'
  应该要放在右边,否则会报错,但是这里*args,**kwargs有可能为空,所以sex占用了第一个位置后,它就直接对应female,
(2, 34, 'female')
#把female的位置调换后,1的值没有了,因为当传入1时,首先去找有没有对应的一个参数占用位置,而这里sex刚好占用这个位置,
 剩下的就给无名参数所有
name:jerry
#第三个,当传入name='jerry'时,它会自动执行for循环打印有名参数

 

函数的return
return:返回什么,给谁?
作用:a.结束函数 b.返回对象
不写,默认是return none,返回的内容自定义,返回的内容给调用者f()

In [1]: def f():
   ...:     print('ok')
   ...:     return 10  
   ...: f() 
   ...: 
ok
Out[1]: 10

In [2]: def add(*args):
   ...:     sum=0
   ...:     for i in args:
   ...:         sum+=i
   ...:     print(sum)
   ...:     return sum  ##返回一个值,再做下一步操作
   ...: add(2,3)
   ...: 
5
Out[2]: 5

如果return多个对象,那么Python会把多个对象封装成一个元组返回

In [3]: def f():
...: return 1,2,[12,3]
...: f()
...: 
Out[3]: (1, 2, [12, 3])

  

函数的作用域****
作用域分为4种情况:
  L: local,局部作用域,即函数定义的变量
  E: enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的
  G: globa,全局变量,就是模块级别定义的变量
  B: built-in,系统固定模块里面的变量,比如int,bytearray等,搜索变量的优先级顺序依次是:
作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB


场景:

In [1]: if True:      ##if中没有作用域
   ...:     x=3
   ...: print(x)
   ...: 
3

In [2]: def f():      ##在函数中,定义一个值没有被执行,因为函数中有作用域
   ...:     a=10
   ...: f()
NameError: name 'a' is not defined

LEGB例子:
int:是函数,把2.9的浮点型转换成整型,x就为2,内置的

[root@zabbix day14]# cat func.py 
#!/usr/local/python3/bin/python3
x = int(2.9)
f_count = 3          #global
def outer():
    f_count = 2      #enclosing
    print(f_count)
    def inner():
        f_count = 1  #local
        print(f_count)
    inner()
outer()
print(f_count)
[root@zabbix day14]# python3 func.py 
2
1
3

 

局部作用域默认不能修改全局作用域的值

[root@zabbix day14]# cat func1.py 
#!/usr/local/python3/bin/python3
count = 10          ##全局已经定义
def outer():
    print(count)    ##局部没有定义,直接使用全局的变量,那么再使用count=4就等于修改全局变量的操作
    count=4              
    ##当print(count)时就表明是找全局,所以count=4相当于修改全局变量的值(或者count+=1是一样修改,count=count+1,首先会去找全局的count=10,再加1,再赋值给count,变成count=11),但是把print(count)注释可以,实际上是重新创建发个变量count=4,和全局的变量count的名称一样而已
outer()
[root@zabbix day14]# python3 func1.py 
UnboundLocalError: local variable 'count' referenced before assignment 

#最后的错实际是count=4应该定义在print(count)之上才可以,因为当count=10时,这个错误还是会报证明,print(count)时是在def counter()里面找的 

 

通过声明全局变量来修改全局变量
没有声明的情况:

[root@zabbix day14]# cat func1.py 
#!/usr/local/python3/bin/python3
count = 10
def outer():
    print(count)
    count=4
    print(count)
outer()
[root@zabbix day14]# python3 func1.py 
UnboundLocalError: local variable 'count' referenced before assignment

声明的情况:

[root@zabbix day14]# cat func1.py 
#!/usr/local/python3/bin/python3
count = 10
def outer():
    global count    ##声明
    print(count)
    count=4
    print(count)
outer()
[root@zabbix day14]# python3 func1.py 
10
4

   

变量在enclosing层

[root@zabbix day14]# cat func2.py 
#!/usr/local/python3/bin/python3
def outer():
    count=10       ##enclosing
    def inner():
        nonlocal count
        count = 20
        print(count)
    inner()
    #print(count)
outer()
[root@zabbix day14]# python3 func2.py 
20

 

Summary:
(1)、变量查找的顺序:LEGB
(2)、只有模块、类、及函数才能引入新的作用域
(3)、对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量
(4)、内部作用域要修改外部作用域变量的值时,全局要使用global关键字,嵌套作用域变量要使用nonlocal
关键字.nonlocal是python3新增的关键字,有了这个关键字,就能完美的实现闭包了

  

 

 

posted @ 2018-03-15 15:22  Reid21  阅读(316)  评论(0编辑  收藏  举报