设为首页 收藏本站
查看: 542|回复: 0

[经验分享] CrazyWing:Python自动化运维开发实战 十四、Python函数

[复制链接]

尚未签到

发表于 2018-8-3 10:52:46 | 显示全部楼层 |阅读模式
导语:
  函数是组织好,可重复使用,用来实现单一或相关联功能的代码段,能提高应用的模块性和代码的重复利用率。
  Python提供了许多内建函数,比如print()。
  也可以自己创建函数,被叫做用户自定义函数。

定义函数:
  规则:
  

函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。  
任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
  
函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  
函数内容以冒号起始,并且缩进。
  
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
  

  语法:
  

   def functionname( parameters ):  "函数_文档字符串"
  function_suite
  return [expression]
  

  例:
  #!/usr/bin/python
  def printme( str ):    # 定义函数
  "打印任何传入的字符串"
  print str
  return

调用函数
  

printme("我要调用用户自定义函数!")  
printme("再次调用同一函数")
  

  
以上实例输出结果:
  
我要调用用户自定义函数!
  
再次调用同一函数
  

按引用传递参数:
  

所有参数在Python里都是按引用传递。如果你在函数里修改了参数,那么在调用这个函数的  
函数里,原始的参数也被改变了。
  

  例:
  #!/usr/bin/python
  def changeme( mylist ):
  "修改传入的列表"
  mylist.append([1,2,3,4]);
  print "函数内取值: ", mylist
  return
  

# 调用changeme函数  
mylist = [10,20,30];
  
changeme( mylist );
  
print "函数外取值: ",mylist
  

  
传入函数的和在末尾添加新内容的对象用的是同一个引用。故输出结果如下:
  
函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
  
函数外取值:  [10, 20, 30, [1, 2, 3, 4]]
  

文档字符串
  我们在使用def 关键字定义一个函数时,其后必须跟有函数名和包括形式参数的圆括号。函数体的下一行开始,必须是缩进的。函数体的第一行可以是字符串,这个字符串就是文档字符串documentation string,通常也称作:docstring

定义:
  在函数体的第一行,可以使用一对三引号(''')或者(""")来定义文档字符串,文档字符串通常第一行以大写字母开头,以句号 (.)结束,第二行是空行,第三行开始是详细描述。强烈建议为你重要的函数写文档字符串都遵循此惯例。

作用:
  文档字符串是使用Python过程中一个很重要的工具,他对程序文档很有帮助,使程序很容易理解。甚至当程序运行的时候,可以从一个函数中返回文档字符串。把函数当做一个对象来看,这更有助于我们的理解,就相当于获取一个对象的属性(__doc__).

函数文档字符串举例:
  

#cat nester.py    这是一个模块文件  
#!/usr/bin/env python3
  
"这是模块文档字符串"
  
def print_list(name):
  '''这是函数文档字符串'''
  for each_item in name:
  if isinstance(each_item,list):
  print_list(each_item)
  else:
  print each_item
  

  
#python
  
>>> import nester
  
>>> print nester.print_list.__doc__
  
这是函数文档字符串
  
>>> help(nester)
  
Help on module nester:
  
NAME
  nester - 这是模块文档字符串
  
FILE
  /python/nester.py
  
FUNCTIONS
  print_list(name)
  这是函数文档字符串
  
(END)
  

参数:
  调用函数时可使用的参数类型:
  · 必备参数
  · 关键字参数
  · 默认参数
  · 不定长参数

必备参数:
  必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
  比如调用printme()函数,你必须传入一个参数,不然会出现语法错误:
  #!/usr/bin/python
  def printme( str ):
  print str;
  return;
  

#调用printme函数  
printme();
  

  输出结果:
  Traceback (most recent call last):
  File &quot;test.py&quot;, line 11, in <module>
  printme();
  TypeError: printme() takes exactly 1 argument (0 given)

关键字参数:
  关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
  使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配
  参数值。
  以下实例在函数 printme() 调用时使用参数名:
  #!/usr/bin/python3
  def printme( str ):
  print str;
  return;
  

#调用printme函数  
printme( str = "My string");
  

  
输出结果:
  
My string
  

  下例能将关键字参数顺序不重要展示得更清楚:
  #!/usr/bin/python3
  def printinfo( name, age ):
  print &quot;Name: &quot;, name;
  print &quot;Age &quot;, age;
  return;
  

#调用printinfo函数  
printinfo( age=50, name="miki" )
  

  
输出结果:
  
Name:  miki
  
Age  50
  

缺省参数:
  调用函数时,缺省参数的值如果没有传入,则被认为是默认值。
  下例会打印默认的age,如果age没有被传入:
  #!/usr/bin/python3
  def printinfo( name, age = 35 ):
  print &quot;Name: &quot;, name;
  print &quot;Age &quot;, age;
  return;
  

#调用printinfo函数  
printinfo( age=50, name="miki" );
  
printinfo( name="miki" )
  

  
输出结果:
  
Name:  miki
  
Age  50
  
Name:  miki
  
Age  35
  

不定长参数:
  如果需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述2种参数不
  同,声明时不会命名。
  语法:
  def functionname([formal_args,] var_args_tuple ):
  function_suite
  return [expression]
  #加了星号()的变量名会存放所有未命名的变量参数。
  例1:
  #!/usr/bin/python3
  def printinfo( arg1, *vartuple ):
  print &quot;输出: &quot;
  print arg1
  for var in vartuple:
  print var
  return
  

# 调用printinfo 函数  
printinfo( 10 );
  
printinfo( 70, 60, 50 )
  

  
输出结果:
  输出:
  10
  输出:
  70
  60
  50
  

  例2:
  #vim multi.py
  def multiarg(*args):
  for i in args:
  print i
  return
  

def multiargs(*args):  print args[2]
  return
  

  
print "第一个调用:"
  
multiarg('hello','lili','tom','wing')
  
print "第2个调用:"
  
multiargs('hello','lili','tom','wing')
  

  
执行结果:
  第一个调用:
  hello
  lili
  tom
  wing
  第2个调用:
  tom
  

*args和**kwargs
  

[wing@macserver ~]$ cat b.py  
#!/usr/bin/env python
  
def foo(*args,**kwargs):
  print 'args = ',args
  print 'kwargs = ',kwargs
  print '-'*20
  
if __name__ == '__main__':
  foo(1,2,3,4)
  foo(a=1,b=2,c=3)
  foo(1,2,3,4,a=1,b=2,c=3)    #同时使用*args和**kwargs,必须*args参数列在前
  foo('a',1,None,a=1,b='2',c=3)
  #*args是一个tuple,**kwargs表示关键字参数,是一个dict
  

  
执行结果:
  
[wing@macserver ~]$ python b.py
  
args =  (1, 2, 3, 4)
  
kwargs =  {}
  
--------------------
  
args =  ()
  
kwargs =  {'a': 1, 'c': 3, 'b': 2}
  
--------------------
  
args =  (1, 2, 3, 4)
  
kwargs =  {'a': 1, 'c': 3, 'b': 2}
  
--------------------
  
args =  ('a', 1, None)
  
kwargs =  {'a': 1, 'c': 3, 'b': '2'}
  
--------------------
  

return 语句:
  return [表达式] 用来退出函数,选择性地向调用方返回一个表达式。
  不带参数值的return语句返回None
  例:
  #!/usr/bin/python3
  def sum( arg1, arg2 ):
  total = arg1 + arg2
  return total
  

# 调用sum函数  
print sum( 10, 20 )
  

  
输出结果:
  
30
  

变量作用域:
  一个程序所有的变量并不是在任何位置都可以访问的。访问权限决定于这个变量是在哪里赋值的。
  变量的作用域决定了在哪一部分程序你可以访问哪个特定的变量名称。
  调用函数时,所有在函数内声明的变量名称都将被加入到作用域中
  两种最基本的变量作用域:全局变量和局部变量
  定义在函数内部的变量拥有一个局部作用域
  定义在函数外的变量拥有全局作用域
  局部变量只能在其被声明的函数内部访问
  全局变量可以在整个程序范围内访问
  例:
  #!/usr/bin/python
  total = 0                      # 这是一个全局变量
  def sum( arg1, arg2 ):
  total = arg1 + arg2  # total在这里是局部变量.
  print &quot;函数内是局部变量 : &quot;, total
  return total;
  

#调用sum函数  
sum( 10, 20 );
  
print "函数外是全局变量 : ",total
  

  
输出结果:
  
函数内是局部变量 :  30
  
函数外是全局变量 :  0
  

命名空间和作用域
  变量:
  是拥有匹配对象的名字(标识符)。
  命名空间:
  是一个包含了变量名称们(键)和它们各自相应的对象们(值)的字典。
  1.一个Python表达式可以访问局部命名空间和全局命名空间里的变量。
  2.如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量。
  3.每个函数都有自己的命名空间。类的方法的作用域规则和通常函数的一样。
  4.Python认为任何在函数内赋值的变量都是局部的。因此,如果要给全局变量在一个函数里赋
  值,必须使用global语句。
  global VarName表达式会告诉Python, VarName是一个全局变量,这样Python就不会在
  局部命名空间里寻找这个变量了。
  例如,在全局命名空间里定义一个变量money,再在函数内给变量money赋值,然后Python
  会假定money是一个局部变量。然而,我们并没有在访问前声明一个局部变量money,结
  果就是会出现一个UnboundLocalError的错误。取消global语句的注释就能解决这个问题。
  #!/usr/bin/python
  Money = 2000
  def AddMoney():
  #想改正代码就取消以下注释:
  #global Money
  Money = Money + 1
  print Money
  AddMoney()
  print Money

globals()和locals()函数
  根据调用地方的不同,globals()和locals()函数可被用来返回全局和局部命名空间里的名字。
  在函数内部调用locals():
  返回的是所有能在该函数里访问的命名。
  在函数内部调用globals():
  返回的是所有在该函数里能访问的全局名字。
  两个函数的返回类型都是字典。所以名字们能用keys()函数摘取。

匿名函数与 lambda
  python 使用 lambda 来创建匿名函数。
  · 匿名是因为不需要以标准的方式来声明,比如说, 使用 def 语句。
  ·  一个完整的 lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行。
  · lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  · lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
  · lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
  语法:
  lambda [arg1 [,arg2,.....argn]]:expression
  参数是可选的,如果使用参数,参数通常也是表达式的一部分。
  例:
  #!/usr/bin/python
  sum = lambda arg1, arg2: arg1 + arg2
  

# 调用sum函数  
print "相加后的值为 : ", sum( 10, 20 )
  
print "相加后的值为 : ", sum( 20, 20 )
  

  
输出结果:
  
相加后的值为 :  30
  
相加后的值为 :  40
  

  复习下单行语句:
  def true():
  return True
  上面的函数没有带任何的参数并且总是返回 True。python 中单行函数可以和标题写在同一行。
  

重写true()函数:  def true(): return True
  

  使用 lambda 的等价表达式(没有参数,返回一个 True)为:
  lambda :True
  

In [4]:     a = lambda x, y=2: x + y  
In [5]:     a(5)
  
Out[5]:   7
  

  
In [6]:     a(3,5)
  
Out[6]:   8
  

  
In [2]:     a=lambda *z:z
  
In [3]:     a(1,2,3,4)
  
Out[3]:   (1, 2, 3, 4)
  

函数不带括号:
  In [1]: def hello(a,b):             #函数定义
  ...:     print a + b
  ...:
  In [2]: hello(1,2)                     #函数调用,带括号表示告诉编译器”执行这个函数“
  In [6]: c=hello                        #不带括号表示把函数赋给另一个函数对象
  In [8]: c(3,4)
  7

递归函数
  递归函数不需要任何修改就可以处理任意深度的嵌套列表
  

#!/usr/bin/env python  
name=['wing',['tom',['jim',['lilei','han×××']]]]
  
def print_list(name):
  for each_item in name:
  if isinstance(each_item,list):
  print_list(each_item)
  else:
  print each_item
  

  
print_list(name)
  
def print_list(list_name,level=0):
  for each_item in list_name:
  if isinstance(each_item,list):
  print_list(each_item,level+1)
  else:
  for tab_num in range(level):
  print "\t",
  print each_item
  

  
print_list(name)
  

题目:利用递归方法求5!。  
程序分析:递归公式:fn=fn_1*4!
  
程序源代码:
  
#!/usr/bin/python
  
# -*- coding: UTF-8 -*-
  

  
def fact(j):
  sum = 0
  if j == 0:
  sum = 1
  else:
  sum = j * fact(j - 1)
  return sum
  

  
for i in range(5):
  print '%d! = %d' % (i,fact(i))
  
以上实例输出结果为:
  
0! = 1
  
1! = 1
  
2! = 2
  
3! = 6
  
4! = 24
  

内建函数
  Python中,按照对象是否可变,将类型分类为:
  不可变类型:
  对象的内容不能够改变(not mutable),这些类型中主要有数值类型(整数,浮点数,复数),字符
  串类型,元组等
  可变类型:
  对象的内容能够改变(mutable),主要有列表,字典
  Python针对众多的类型,提供了众多的内建函数来处理(内建是相对于导入import来说的,后面学
  习到包package时,将会介绍),
  这些内建函数功用在于其往往可对多种类型对象进行类似的操作,即多种类型对象的共有的操作;
  如果某种操作只对特殊的某一类对象可行,Python常将其设置为该种类型的方法(method)
  

内建函数的查看  
通过在python交互模式下,键入相应的命令即可查看当前python版本的一些内建函数
  
>>> dir(__builtins__)
  
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
  

  
也可以通过如下方式查看:
  
    >>> import __builtin__
  
    >>>dir(__builtin__)
  

  

  获取内建函数帮助:
  help()  
  获取帮助信息
  其完整的一般使用形式为:
  help(module.class.function)
  例子:
  In [3]: import sys
  In [4]: help(sys.getsizeof)
  

数值类型表示的内建函数  

  
bin()     获取一个整数(int类型或长整型),返回其2进制形式的字符串
  
oct()     获取一个整数,返回其8进制形式的字符串
  
hex()    获取一个整数,返回其16进制形式的字符串
  

  

  对象生成的内建函数
  int()    将数值或字符串转换为整数int,完整使用形式int(x,base),base用于指定进制
  long()   将数值或字符串转换为整数long,完整使用形式long(x, base),base用于指定进制
  float()  将数值或字符串转换为浮点数
  complex()返回一个复数,完整使用形式 complex(real,imag)
  str()    将所给对象转换为字符串,使用形式为str(object)
  list()   获取对象,转换为列表, list(object)
  dict()   获取映射转换为字典,dict(mapping)
  tuple()  获取一个可迭代的对象,返回一个元组, tuple(iterable)
  注:这里留意dict()内建函数创建字典的不同方式

装饰器Decorator
  装饰器是我们现在碰到的难点之一,不大好理解,可以简单的认为装饰器就是为了给函数添加额外的功能,而不用每次都手写那个功能,只需要用&quot;@装饰器名称&quot;调用就可以了
  不使用装饰器给函数添加一个额外的功能:
  下例中是给foo函数添加日志功能
  

#!/usr/bin/env python3  
import logging
  
def foo():
  print "hello world"
  

  
def use_logging(func):
  logging.warn("%s is running" % func.__name__)
  func()
  

  
use_logging(foo)   #每次执行foo的时候都得调用use_logging函数
  

  使用装饰器:
  @use_logging       #装饰器
  foo()
  

简陋写法:因为不能给带有参数的函数装饰,所以简陋  
#!/usr/bin/env python
  
#coding=utf8
  
import logging
  

  
def use_logging(func):
  logging.warn("%s is running" % func.__name__)
  return func
  

  
@use_logging
  
def foo():
  print "hello world"
  

  
foo()
  

  
标准写法:
  #!/usr/bin/env python
  #coding=utf8
  import logging
  

  def use_logging(func):
  def wrapper(*args,**kw):
  logging.warn("%s is running" % func.__name__)
  return func()
  return wrapper
  

  @use_logging
  def foo():
  print "hello world"
  

  foo()
  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
带参数:
  
#!/usr/bin/env python
  
#coding=utf8
  
import logging
  

  
def use_logging(func):
  def wrapper(*args,**kw):
  logging.warn("%s is running" % func.__name__)
  return func()               #会报错
  return wrapper
  

  
@use_logging
  
def foo(arg1,arg2):
  print "arg1+arg2=%s" % (arg1+arg2)
  

  
foo(5,3)
  

  
正确写法如下:
  
#!/usr/bin/env python
  
#coding=utf8
  
import logging
  

  
def use_logging(func):
  def wrapper(*args,**kw):
  logging.warn("%s is running" % func.__name__)
  return func(*args,**kw)
  return wrapper
  

  
@use_logging
  
def foo(*args,**kw):
  print "arg1+arg2=%s" % (args[0]+args[1])
  print "hello world"
  

  
foo(1,2)
  
因为装饰器会先执行,在没有wrapper函数的情况下return func(*args,**kw)会出现args未定义错误

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-545799-1-1.html 上篇帖子: CrazyWing:Python自动化运维开发实战 十八、Python面向对象 下篇帖子: Python itchat模块在微信上的各种小应用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表