第4章自动化系列四之Python函数

Python自动化
时间:2016.08.09

笔者QQ:572891887、552408925
Linux架构交流群:471443208、524721466

1.1Python函数

面向对象
面向过程
1.可读性差
2.重用性差
函数式编程

1.1.1自定义函数

看一个函数的例子:db文件内容为xuliangwei|123

  1. def login(username,password):
  2. '''
  3. 用于用户登陆
  4. :param username: 用户输入用户名
  5. :param password: 用户输入密码
  6. :return: true,登陆成功 false,登陆失败
  7. '''
  8. file_name = open("db",'r')
  9. for line in file_name:
  10. line_list = line.split("|")
  11. if line_list[0] == username and line_list[1] == password:
  12. return True
  13. return False

解释:

  1. 1.def:表示函数的关键字
  2. 2.函数名:函数的名称,日后根据函数名调用函数
  3. 3.函数体:函数中进行一系列的逻辑计算,如:发送邮件
  4. 4.返回值:当函数执行完毕后,可以给调用者返回数据。
  5. 5.参数:为函数体提供数据(Python函数传递参数是传递一个引用。而不是重新赋值.)
  6. 1.普通参数(严格按照顺序,将实际参数赋值给形式参数)
  7. 2.默认参数(必须放置在参数列表的最后)
  8. 3.指定参数(将实际参数复制给指定的形式参数)
  9. 4.动态参数:
  10. *args 默认将位置参数,全部放置在元组中
  11. **kwargs 默认将关键字参数(字典),全部放置在字典中
  12. 5.万能参数

1.1.2 局部变量

1.在程序中定义的变量成为局部变量。
2.局部变量作用域是定义该变量的子程序。
3.当全局变量与局部变量冲突时,在定义局部变量的子程序内,局部变量起作用,在其他地方全局变量起作用。

  1. #!/usr/bin/env python
  2. # Author:xuliangwei
  3. name = "XuLiangWei"
  4. def change_name(name):
  5. print("before change:", name)
  6. name = "我是有Tesla的男人" #定义局部变量
  7. print("after chage:", name)
  8. change_name(name)
  9. print("在外面看name改了么?",name)

执行结果:

  1. before change: XuLiangWei
  2. after chage: 我是有Tesla的男人
  3. 在外面看name改了么? XuLiangWei

1.1.3 全局变量

1.在程序一开始定义的变量称为全局变量。

  1. 1.全局变量定义变量,必须都是大写(潜规则)。比如:NAME="xuliangwei"
  2. 2.函数优先读当前环境变量,如果没有则会读全局变量,全局变量在所有的作用域都可读。
  3. 3.修改全局变量需要先global name,表示name是全局变量(重新赋值)。
  4. 4.特殊:列表,字典,元组嵌套列表,可修改,不可重新赋值。

1.1.4内置函数

  1. # all #非0即真
  2. # any #
  3. # bool #0和,空字典空列表都是假
  4. # callable #表示是否可执行,或是否可调用
  5. # dir #快速查看对象提供了哪些功能
  6. # divmod #文章分页使用
  7. # isinstance #用于判断,对象是否是某个类的实例
  8. # globals #列出全局变量
  9. # locals #列出所有局部变量
  10. # hash #
  11. #enumerate
  12. #float
  13. #format
  14. #frozenset
  15. # max #最大
  16. # min #最小
  17. # sum #求和
  18. # int #整数
  19. # hex #十进制转换十六进制
  20. # bin #十进制转二进制
  21. # oct #十进制转十进制
  22. # filter #内部循环,参数比较
  23. # map #将函数返回值添加到结果中
  24. # compile #将字符串,编译成Python代码
  25. # eval #执行表达式,并且获取结果
  26. # exec #执行Python代码,接收:代码或者字符串
  27. #id #查看内存地址
  28. #input #等待用户输入
  29. # isinstance #查看某个对象是不是某个类的实例
  30. # issubclass #
  31. # len #查看长度
  32. # list #
  33. # memoryview #查看内存地址相关类
  34. # next #
  35. # iter #创建迭代器
  36. # object #所有类的复类
  37. # open #
  38. # ord #
  39. # pow #
  40. # print #
  41. # property #
  42. # range #
  43. # repr #
  44. # reversed #反转
  45. # round #
  46. # set #集合
  47. # slice #
  48. # sorted #
  49. # staticmethod #
  50. # str #字符串
  51. # super #面向对象
  52. # tuple #元祖
  53. # type #类型
  54. # vars #当前模块有哪些变量
  55. # zip #压缩
  56. # __import__ #导入模块
  57. # delattr()
  58. # getattr()
  59. # setattr()

1.1.5函数返回值

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

1.函数在执行过程中只要遇到return语句,就会停止执行并返回结果,也可以理解为return语句代表着函数的结果。
2.如果未在函数中指定return,那这个函数的返回值为None。

1.1.6递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身,这个函数就是递归函数。

  1. #!/usr/bin/env python
  2. # Author:xuliangwei
  3. def calc(n):
  4. print(n)
  5. if int(n/2) ==0:
  6. return n
  7. return calc (int(n/2))
  8. calc(10)

递归特征:
1.必须有一个明确的结束条件。
2.每次进入更深一层递归时,问题规模相比上次递归都应有所减少。
3.递归效率不高,递归层次过多会导致栈益处(在计算机中,函数调用是通过栈(stack)这种数据结果实现的,每当进入一个函数调用,栈就会加一层栈帧,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈益处。)

1.1.7匿名函数

匿名函数就是不需要显示的指定函数

  1. #!/usr/bin/env python
  2. # Author:xuliangwei
  3. def calc(n):
  4. return n**n
  5. print(calc(10))
  6. #换成匿名函数
  7. calc = lambda n:n**n
  8. print(calc(10))
  9. #从上看来好像并无卵用,如果是这么用确实没毛改进,不过匿名函数主要是和其他函数搭配使用,如下:
  10. res = map(lambda x:x**2,[1,5,7])
  11. for i in res:
  12. print(i)
  13. #结果:
  14. 1
  15. 25
  16. 49

1.1.8高阶函数

变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

  1. #!/usr/bin/env python
  2. # Author:xuliangwei
  3. def add (x,y,f):
  4. return f(x) + f(y)
  5. res = add(3,-6,abs)
  6. print(res)

1.1.9函数式编程

函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。

而函数式编程,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。
我们首先要搞明白计算机(Computer)和计算(Compute)的概念。
在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断和跳转指令,所以,汇编语言是最贴近计算机的语言。
而计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

  1. (1 + 2) * 3 - 4
  2. #传统的过程式编程,可能这样写:
  3.   var a = 1 + 2;
  4.   var b = a * 3;
  5.   var c = b - 4;
  6. #函数式编程要求使用函数,我们可以把运算过程定义为不同的函数,如下:
  7.   var result = subtract(multiply(add(1,2), 3), 4);
  8. #这就是函数式编程(就会这么多)

1.2装饰器

1.自动执行xx函数并且将其下面的函数名f1当作参数传递
2.将xx函数的返回值,重新赋值

定义:本质是函数,(装饰其他函数)就是为其他函数添加附件功能。

1.不能修改被装饰的函数的源代码
2.不能修改被装饰的函数的调用方式

实现装饰器知识储备:

1.函数即"变量"
2.高阶函数
3.嵌套函数
高级函数+嵌套函数=>装饰器
  1. def foo(func):
  2. print
  3. 'decorator foo'
  4. return func
  5. @foo
  6. def bar():
  7. print
  8. 'bar'
  9. bar()
  10. # 没有嵌套函数,增加了打印"decorator foo"功能,并且没有改变函数的调用方式。
  1. #!/usr/bin/env python
  2. # Author:xuliangwei
  3. import time
  4. def decorator(func):
  5. def wrapper(*args, **kwargs):
  6. start = time.time()
  7. func(*args, **kwargs)
  8. stop = time.time()
  9. print ('run time is %s ' % (stop - start))
  10. print ("timeout")
  11. return wrapper
  12. @decorator # 装饰器
  13. def test(list_test):
  14. for i in list_test:
  15. time.sleep(0.1)
  16. print ('-' * 20, i)
  17. # decorator(test)(range(10))
  18. test(range(10))
  19. # 可以看出装饰器decorator并没有参数
  1. import time
  2. def timer(timeout=0):
  3. def decorator(func):
  4. def wrapper(*args, **kwargs): # 会给装饰器传递参数,因为无法确定装饰器有多少参数,所以使用这个。
  5. start = time.time()
  6. func(*args, **kwargs)
  7. stop = time.time()
  8. print ('run time is %s ' % (stop - start))
  9. print (timeout)
  10. return wrapper
  11. return decorator
  12. @timer(2) # 装饰器的参数为2
  13. def test(list_test):
  14. for i in list_test:
  15. time.sleep(0.1)
  16. print ('-' * 20, i)
  17. # timer(timeout=10)(test)(range(10))
  18. test(range(10))
  19. # 装饰器timer的参数为2,@timer(2)相当于test=timer(2)(test)

1.3迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象:

  1. xuliangwei:~ xuliangwei$ Python3
  2. >>>
  3. >>> from collections import Iterable
  4. >>> isinstance([], Iterable)
  5. True
  6. >>> isinstance({}, Iterable)
  7. True
  8. >>> isinstance('abc', Iterable)
  9. True
  10. >>> isinstance((x for x in range(10)), Iterable)
  11. True
  12. >>> isinstance(100, Iterable)
  13. False

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:

  1. xuliangwei:~ xuliangwei$ Python3
  2. >>>
  3. >>> from collections import Iterator
  4. >>> isinstance((x for x in range(10)), Iterator)
  5. True
  6. >>> isinstance([], Iterator)
  7. False
  8. >>> isinstance({}, Iterator)
  9. False
  10. >>> isinstance('abc', Iterator)
  11. False

你可能会问,为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

  1. for x in [1, 2, 3, 4, 5]:
  2. pass

实际上完全等价于:

  1. # 首先获得Iterator对象:
  2. it = iter([1, 2, 3, 4, 5])
  3. # 循环:
  4. while True:
  5. try:
  6. # 获得下一个值:
  7. x = next(it)
  8. except StopIteration:
  9. # 遇到StopIteration就退出循环
  10. break

1.4生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

  1. l = [x * x for x in range(10)]
  2. print(l)
  3. g = (x * x for x in range(10))
  4. print(g)
  5. #执行结果:
  6. [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  7. <generator object <genexpr> at 0x101380938>

1.只有在调用时才会生成相应的数据。
只记录当前位置。
只有一个next()方法。next()