Python基础(函数部分)

写在前面



    加勒比海盗今天上映!



一、函数的基本概念

 - 函数是什么?

    函数,就是一个'锤子',一个具有特定功能的'锤子',使用者可以在适当的时候使用这个'锤子',而不用再去从头做一个'锤子';即可以直接拿来使用;

  函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

  函数能提高应用的模块性,和代码的重复利用率。

 - 不使用函数会有什么问题?

  1.组织结构不清晰,可读性差;

  2.代码冗余;

  3.无法统一管理且维护难度大;

 - 函数的分类

  1.内置函数;Python解释器自带的具有某些基础功能的'锤子';

  2.自定义函数;用户根据需求,自己定义写好的'锤子';

 - 函数的两个阶段

  1.定义阶段;

  2.使用阶段;

二、函数的定义

 - 为何要定义函数?

  函数即变量,变量必须先定义后使用,未定义而直接引用函数,就相当于在引用一个不存在的变量名,必然会出错;

 - 函数的定义都干了什么?

  只检测语法,不执行代码;

  函数调用阶段才会去执行具体代码;

 - 如何定义函数?

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

 - 定义函数的三种形式

  1.无参函数;

    应用场景仅仅只是执行一些操作,比如与用户交互,打印;    print()

  2.有参函数;

    需要根据外部传进来的参数,才能执行相应的逻辑;    len(string)

  3.空函数;

    编写代码之初,用来设计代码结构;    def func():pass

 - 三元表达式

1 x = 1
2 y = 7
3 res = x if x > y else y
4 print(res)
5 
6 ---
7 7

三、函数的调用

 - 函数的调用

  1.先找到函数名字;

  2.根据 名字+() 调用函数,执行函数体内的代码;

1 def foo():
2     print("From foo func")
3 print(foo)          # 打印foo函数代码的内存地址
4 foo()
5 
6 ---
7 <function foo at 0x0000000000B5C268>
8 From foo func

 - 函数调用的三种形式

  1.语句形式:foo()

1 def my_max(x,y):
2     res=x if x >y else y
3     return res
4 
5 res = my_max(1,9)
6 print(res)
7 
8 ---
9 9

  2.表达式形式:3*len('hello')

1 def my_max(x,y):
2     res=x if x >y else y
3     return res
4 
5 res = my_max(1,9)*100
6 print(res)
7 
8 ---
9 900

  3.函数的执行结果当做另外一个函数的参数:range(len('hello'))

1 def my_max(x,y):
2     res=x if x >y else y
3     return res
4 
5 res=my_max(my_max(10,20),30)
6 print(res)
7 
8 ---
9 30

四、函数的参数

 - 函数参数的分类

   - 形参

    即函数定义处()里的变量标识符;

   - 实参

    即函数调用处()里的变量;

   - 意义

    形参即变量名,实参即变量值;函数调用则将值绑定到形参变量上,函数调用结束,解除绑定;

 - 具体应用

   - 1.位置参数

    按照从左到右的顺序定义的参数;位置实参需要按照顺序一一对应给位置形参传值绑定;

1 def foo(name,age):
2     print(name,age)
3 
4 foo('alex',20)
5 
6 ---
7 alex 20

   - 2.关键字参数

    按照 key=value 的形式定义实参;位置实参无需按照位置顺序给形参传值;

    注意:关键字实参必须在位置实参右面,并且对同一个形参不能重复传值;

1 def foo(name,age):
2     print(name,age)
3 
4 foo('alex',age=19)            # ok
5 
6 foo(age=19,'alex')            # SyntaxError: positional argument follows keyword argument
7 
8 foo('alex',name='egon')       # TypeError: foo() got multiple values for argument 'name'

   - 3.默认参数

    形参在定义时就已经为其赋值,可以传值也可以不传值;经常需要变的参数定义成位置形参,变化较小的参数定义成默认参数(形参)

    注意:

      1. 只在定义时赋值一次;

      2. 默认参数的定义应该在位置形参右面;

      3. 默认参数通常应该定义成不可变类型

1 def foo(name,gender='male'):
2     print(name,gender)
3 
4 foo('alex')
5 foo('kelly','female')
6 
7 ---
8 alex male
9 kelly female

     - 4.可变长参数

    参考:egon函数草稿

    针对实参在定义时长度不固定的情况,应该从形参的角度找到可以接收可变长实参的方案,这就是可变长参数(形参)

    而实参有按位置按关键字两种形式定义,针对这两种形式的可变长,形参也应该有两种解决方案,分别是 *args**kwargs

    - 按位置的可变长参数示例1:

 1 # * 会把溢出的按位置定义的实参都接收,以元组的形式赋值给args,可以通过 *args 得到溢出的各个参数;
 2 def foo(x,y,*args):
 3     print("x -> {}, y -> {}, args -> {}".format(x,y,args))
 4     print("*args is: ", *args)
 5 
 6 foo(1,2)
 7 foo(1,2,3)
 8 foo(1,2,[1,2],{'k1':'v1'},10,'AAAAA')
 9 
10 ---
11 x -> 1, y -> 2, args -> ()
12 *args is: 
13 x -> 1, y -> 2, args -> (3,)
14 *args is:  3
15 x -> 1, y -> 2, args -> ([1, 2], {'k1': 'v1'}, 10, 'AAAAA')
16 *args is:  [1, 2] {'k1': 'v1'} 10 AAAAA

     - 按位置的可变长参数示例2:

 1 def foo(x, y, *args):
 2     print(x, y)
 3     print(args)
 4     print(*args)
 5 
 6 foo(1, 2, *[3, 4, 5])
 7 
 8 ---
 9 1 2
10 (3, 4, 5)
11 3 4 5

     - 按位置的可变长参数示例3:

1 def foo(x, y, z):
2     print(x, y, z)
3 
4 foo(100,*(2, 3,))
5 
6 ---
7 100 2 3

    - 按位置的可变长参数示例4:

1 def foo(x, y, z):
2     print(x, y, z)
3 
4 foo(*(1,2, 3,))
5 foo(*[1,2, 3,])
6 
7 ---
8 1 2 3
9 1 2 3

    - 按位置的可变长参数示例5:

 1 def foo(x,y,*args):
 2     print("x -> {}, y -> {}, args -> {}".format(x,y,args))
 3     for item in args:
 4         print(type(item),item)
 5     print('------------------------')
 6 
 7 foo(1,2,[1,2],{'k1':'v1'},10,'AAAAA')
 8 
 9 ---
10 x -> 1, y -> 2, args -> ([1, 2], {'k1': 'v1'}, 10, 'AAAAA')
11 <class 'list'> [1, 2]
12 <class 'dict'> {'k1': 'v1'}
13 <class 'int'> 10
14 <class 'str'> AAAAA
15 ------------------------

    - 按关键字的可变长参数示例1:

 1 # ** 会把溢出的按关键字定义的实参都接收,以字典的形式赋值给 kwargs
 2 def foo(x, y, **kwargs):
 3     print(x, y)
 4     print(kwargs)
 5     print(*kwargs)
 6 
 7 foo(1, y=2, a=1, b=2, c=3)
 8 
 9 ---
10 1 2
11 {'a': 1, 'c': 3, 'b': 2}
12 a c b

    - 按关键字的可变长参数示例2:

 1 def foo(x, y, **kwargs):
 2     print(x, y)
 3     print(kwargs)
 4     print(*kwargs)
 5 
 6 foo(1, y=2, **{'a': 1, 'b': 2, 'c': 3})
 7 
 8 ---
 9 1 2
10 {'a': 1, 'c': 3, 'b': 2}
11 a c b

    - 按关键字的可变长参数示例3:

1 def foo(x, y, z):
2     print("x is %s
y is %s
z is %s" % (x, y, z))
3 
4 foo(**{'z': 1, 'x': 2, 'y': 3})
5 
6 ---
7 x is 2
8 y is 3
9 z is 1

    - 按关键字的可变长参数示例4:

1 def foo(x, y, z):
2     print("x is %s
y is %s
z is %s" % (x, y, z))
3 
4 foo(x=99,**{'z': 1, 'y': 3})
5 
6 ---
7 x is 99
8 y is 3
9 z is 1

    - 按关键字的可变长参数示例5:

 1 # ** 会把溢出的按关键字定义的实参都接收,以字典的形式赋值给kwargs;可以通过 *kwargs 得到溢出的参数的key值;但不能用 **kwargs 获取其中的值;
 2 def foo(x,y,**kwargs):
 3     print("x -> {}, y -> {}, kwargs -> {}".format(x, y, kwargs))
 4     print(*kwargs)
 5     for key,value in kwargs.items():
 6         print(key,value)
 7     print('------------------------')
 8 
 9 foo(1,y=9)
10 foo(x=1,y=9)
11 foo(1,y=9,m=88,n=99)
12 foo(1,y=9,m=[3,4],n={'k1':'v1'},l=(1,2,))
13 
14 ---
15 x -> 1, y -> 9, kwargs -> {}
16 
17 ------------------------
18 x -> 1, y -> 9, kwargs -> {}
19 
20 ------------------------
21 x -> 1, y -> 9, kwargs -> {'n': 99, 'm': 88}
22 n m
23 n 99
24 m 88
25 ------------------------
26 x -> 1, y -> 9, kwargs -> {'n': {'k1': 'v1'}, 'm': [3, 4], 'l': (1, 2)}
27 n m l
28 n {'k1': 'v1'}
29 m [3, 4]
30 l (1, 2)
31 ------------------------

   - 5.命名关键字参数

    - * 后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递; 可以保证,传入的参数中一定包含某些关键字;

    - 命名关键字参数示例1:

 1 def foo(name,age,*,sex='male',height):
 2     print(name,age)
 3     print(sex)
 4     print(height)
 5 
 6 foo('egon',17,height='185')
 7 print('---------')
 8 foo('egon',17,sex='female',height='185')
 9 
10 ---
11 egon 17
12 male
13 185
14 ---------
15 egon 17
16 female
17 185

     - 命名关键字参数示例2:

 1 def foo(x, y, *args, a=1, b, **kwargs):
 2     print(x, y)
 3     print(args)
 4     print(a)
 5     print(b)
 6     print(kwargs)
 7 
 8 foo(1, 2, 3, 4, 5, b=3, c=4, d=5)
 9 
10 ---
11 1 2
12 (3, 4, 5)
13 1
14 3
15 {'d': 5, 'c': 4}

    - 命名关键字参数示例3:

 1 def foo(name,age=10,*args,sex='male',height,**kwargs):
 2     print(name)
 3     print(age)
 4     print(args)
 5     print(sex)
 6     print(height)
 7     print(kwargs)
 8 
 9 foo('alex',1,2,3,4,5,sex='female',height='150',a=1,b=2,c=3)
10 
11 ---
12 alex
13 1
14 (2, 3, 4, 5)
15 female
16 150
17 {'c': 3, 'a': 1, 'b': 2}

  - 扩展和思考

1 def foo(x,y):
2     print(x,y)
3 
4 foo(**{'y': 2, 'x': 100})   # 等价于 foo(x=100,y=2)
5 
6 ---
7 100 2
1 def foo(x,y,z):
2     print(x,y,z)
3 
4 foo(*[1,2,3])               #foo(1,2,3)
5 foo(*(1,2,3))               #foo(1,2,3)
6 
7 ---
8 1 2 3
9 1 2 3
1 def foo(*args):
2     print(args)
3     print(*args)
4 
5 foo(1,2,4)
6 
7 ---
8 (1, 2, 4)
9 1 2 4
1 def foo(**kwargs):
2     print(kwargs)
3     print(*kwargs)
4 
5 foo(x=1,y=['a','b','c'],z='hello')
6 
7 ---
8 {'y': ['a', 'b', 'c'], 'x': 1, 'z': 'hello'}
9 y x z
1 def wrapper(*args,**kwargs):
2     print(args)
3     print(kwargs)
4 
5 wrapper(1,2,3,a=1,b=2)
6 
7 ---
8 (1, 2, 3)
9 {'a': 1, 'b': 2}

五、函数对象

  - 1 可以被引用

 1 def foo():
 2     print('from foo')
 3 
 4 func=foo
 5 print(foo)
 6 print(func)
 7 func()
 8 
 9 ---
10 <function foo at 0x0000000000B6C268>
11 <function foo at 0x0000000000B6C268>
12 from foo

  - 2 可以当作参数传递

 1 def foo():
 2     print('from foo')
 3 
 4 def bar(func):
 5     print(func)
 6     func()
 7 
 8 bar(foo)
 9 
10 ---
11 <function foo at 0x000000000072C268>
12 from foo

  - 3 返回值可以是函数

 1 def foo():
 2     print('from foo')
 3 def bar(func):
 4     return func
 5 
 6 f=bar(foo)
 7 print(f)
 8 f()
 9 
10 ---
11 <function foo at 0x00000000010DC268>
12 from foo

  - 4 可以当作容器类型的元素

 1 # 可以当做容器类的元素
 2 def select():
 3     print('---->>> select')
 4 def update():
 5     print('---->>> update')
 6 def delete():
 7     print('---->>> delete')
 8 def insert():
 9     print('---->>> insert')
10 
11 func_dict = {
12     'select':select,
13     'update':update,
14     'delete':delete,
15     'insert':insert
16 }
17 
18 def main():
19     while True:
20         sql = input(">>> ").strip()
21         if not sql:
22             continue
23         elif 'Q' == sql.upper():
24             print('Bye...')
25             exit(0)
26         else:
27             cmd_list = sql.split()
28             if cmd_list[0] in func_dict:
29                 func_dict[cmd_list[0]]()
30             else:
31                 print('Input invalid')
32 
33 main() 

六、函数的嵌套

  - 嵌套调用

 1 def max2(x,y):
 2     return x if x > y else y
 3 def max4(a,b,c,d):
 4     res1=max2(a,b)
 5     res2=max2(res1,c)
 6     res3=max2(res2,d)
 7     return res3
 8 
 9 print(max4(10,99,31,22))
10 
11 ---
12 99

  - 嵌套定义

 1 def f1():
 2     print('from f1')
 3     def f2():
 4         print('from f2')
 5         def f3():
 6             print('from f3')
 7         f3()
 8     f2()
 9 
10 f1()
11 
12 ---
13 from f1
14 from f2
15 from f3

七、命名空间和作用域

参考:Python命名空间的本质

  - 命名空间

    - 1.内置名称空间:随着Python解释器的启动而产生;会一直保留,直到Python解释器退出;

1 print(max([1,2,3]))
1 import builtins
2 for i in dir(builtins):
3     print(i)
  1 ArithmeticError
  2 AssertionError
  3 AttributeError
  4 BaseException
  5 BlockingIOError
  6 BrokenPipeError
  7 BufferError
  8 BytesWarning
  9 ChildProcessError
 10 ConnectionAbortedError
 11 ConnectionError
 12 ConnectionRefusedError
 13 ConnectionResetError
 14 DeprecationWarning
 15 EOFError
 16 Ellipsis
 17 EnvironmentError
 18 Exception
 19 False
 20 FileExistsError
 21 FileNotFoundError
 22 FloatingPointError
 23 FutureWarning
 24 GeneratorExit
 25 IOError
 26 ImportError
 27 ImportWarning
 28 IndentationError
 29 IndexError
 30 InterruptedError
 31 IsADirectoryError
 32 KeyError
 33 KeyboardInterrupt
 34 LookupError
 35 MemoryError
 36 NameError
 37 None
 38 NotADirectoryError
 39 NotImplemented
 40 NotImplementedError
 41 OSError
 42 OverflowError
 43 PendingDeprecationWarning
 44 PermissionError
 45 ProcessLookupError
 46 RecursionError
 47 ReferenceError
 48 ResourceWarning
 49 RuntimeError
 50 RuntimeWarning
 51 StopAsyncIteration
 52 StopIteration
 53 SyntaxError
 54 SyntaxWarning
 55 SystemError
 56 SystemExit
 57 TabError
 58 TimeoutError
 59 True
 60 TypeError
 61 UnboundLocalError
 62 UnicodeDecodeError
 63 UnicodeEncodeError
 64 UnicodeError
 65 UnicodeTranslateError
 66 UnicodeWarning
 67 UserWarning
 68 ValueError
 69 Warning
 70 WindowsError
 71 ZeroDivisionError
 72 __build_class__
 73 __debug__
 74 __doc__
 75 __import__
 76 __loader__
 77 __name__
 78 __package__
 79 __spec__
 80 abs
 81 all
 82 any
 83 ascii
 84 bin
 85 bool
 86 bytearray
 87 bytes
 88 callable
 89 chr
 90 classmethod
 91 compile
 92 complex
 93 copyright
 94 credits
 95 delattr
 96 dict
 97 dir
 98 divmod
 99 enumerate
100 eval
101 exec
102 exit
103 filter
104 float
105 format
106 frozenset
107 getattr
108 globals
109 hasattr
110 hash
111 help
112 hex
113 id
114 input
115 int
116 isinstance
117 issubclass
118 iter
119 len
120 license
121 list
122 locals
123 map
124 max
125 memoryview
126 min
127 next
128 object
129 oct
130 open
131 ord
132 pow
133 print
134 property
135 quit
136 range
137 repr
138 reversed
139 round
140 set
141 setattr
142 slice
143 sorted
144 staticmethod
145 str
146 sum
147 super
148 tuple
149 type
150 vars
151 zip
Python解释器启动后,加载的内置名称空间所包含的变量

    - 2.全局名称空间:文件的执行会产生全局名称空间,指的是文件级别定义的名字都会放入该空间;在文件/模块被Python解释器读入时创建,通常也会一直保存到解释器退出;

    - 3.局部名称空间:调用函数时会产生局部名称空间,只在函数调用时临时绑定,调用结束解绑定;

 1 info = "Adress : "
 2 def func_father(country):
 3     def func_son(area):
 4         city= "Shanghai "                       #此处的city变量,覆盖了父函数的city变量
 5         print(info + country + city + area)
 6     city = " Beijing "
 7     #调用内部函数
 8     func_son("ChaoYang ");
 9  
10 func_father("China ")
11 
12 ---
13 Adress : China Shanghai ChaoYang

  - 作用域

    - 1.全局作用域

      包含内置名称空间和全局名称空间;

      查看全局作用域内的名字:gloabls()

      生命周期:全局有效,在任何位置都能被访问到,除非del删掉,否则会一直存活到文件执行完毕;

    - 2.局部作用域

      只包含局部名称空间;

      查看局局作用域内的名字:locals()

      生命周期:局部有效,只能在局部范围调用,只在函数调用时才有效,调用结束就失效;

    - 3.名字的查找顺序

1 LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
2  
3 1.locals 是函数内的名字空间,包括局部变量和形参    # 局部名称空间
4 2.enclosing 外部嵌套函数的名字空间(闭包中常见)
5 3.globals 全局变量,函数定义所在模块的名字空间     # 全局名称空间
6 4.builtins 内置模块的名字空间                      # 内置名称空间

如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name 'aa' is not defined;

     示例:

 1 x=1000
 2 def func():
 3     x=2
 4     y=('a','b',)
 5     print(x,y)
 6     print(globals())
 7     print(locals())
 8 func()
 9 
10 ---
11 2 ('a', 'b')
12 {'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000565C88>, 'func': <function func at 0x00000000006EC268>, '__file__': 'D:/soft/work/Python_17/day04/functions.py', '__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__doc__': None, '__cached__': None, '__package__': None, 'x': 1000, '__spec__': None}
13 {'y': ('a', 'b'), 'x': 2}

八、闭包的概念

  - 闭包的定义

    1. 定义在函数内部的函数;

    2. 该内部函数包含对外部作用域而不是对全局作用域的引用;

    - 扩展:

      __closure__ 属性定义的是一个包含 cell 对象的元组,其中元组中的每一个 cell 对象用来保存作用域中变量的值;

 1 def f1():
 2     x = 1
 3     user_list = ['alex','egon']
 4     def f2():
 5         print(x)
 6         print(user_list)
 7     return f2
 8 
 9 f = f1()                          # 返回一个闭包函数对象:即f2;
10 print(f)
11 print(f.__closure__)              # 查看该闭包函数所包含的元素;
12 for item in f.__closure__:
13     print(item)
14     print(item.cell_contents)   # 打印出每一个元素的值
15 
16 ---
17 <function f1.<locals>.f2 at 0x00000000006CC400>
18 (<cell at 0x0000000000697D68: list object at 0x00000000006D0F48>, <cell at 0x0000000000697D98: int object at 0x0000000060BE01D0>)
19 <cell at 0x0000000000697D68: list object at 0x00000000006D0F48>
20 ['alex', 'egon']
21 <cell at 0x0000000000697D98: int object at 0x0000000060BE01D0>
22 1

  - 闭包的意义

    - 返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域;

    - 这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域;

  - 应用领域:延迟计算/惰性计算

    - f = index('http://www.baidu.com')  这样只是得到了一个函数对象,并没有执行具体操作,需要执行的时候直接使用 f() 即可;

 1 # 内部函数包含对外部作用域而非全局作用域的引用;
 2 # 返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域;
 3 # 闭包应用:惰性计算/延迟计算
 4 from urllib.request import urlopen
 5 
 6 def index(url):
 7     def get():
 8         return urlopen(url).read()
 9     return get
10 
11 f = index('http://www.baidu.com')
12 print(f.__closure__)
13 print(f.__closure__[0].cell_contents)
14 
15 # print(f())
16 print(f().decode('utf-8'))

九、装饰器

参考:Advanced Uses of Python Decorators

  - 什么是装饰器?

    - 修饰别人的工具,在原来基础上修饰添加功能;

    - 装饰器本身可以是任何可调用对象,被装饰的对象也可以是任意可调用对象;

  - 为什么要用装饰器?

    - 开放封闭原则:对修改是封闭的,对扩展是开放的;

    - 装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为其添加新功能;

  - 装饰器应用

    - 示例:

 1 # 开放封闭原则:对修改封闭,对扩展开放;
 2 # 装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象;
 3 # 装饰器就是闭包的一种实现,只不过参数是函数对象;
 4 
 5 import time
 6 
 7 def timer(func):
 8     def wrapper():
 9         start_time = time.time()
10         func()
11         end_time = time.time()
12         print('Run time is {}'.format(end_time-start_time))
13     return wrapper
14 
15 @timer # 等价于 index = timer(index)
16 def index():
17     print('Start to work...')
18     time.sleep(3)
19     print('End the work...')
20 
21 index()
22 
23 ---
24 Start to work...
25 End the work...
26 Run time is 3.0

    - 原理分析:

      1.在 index()函数定义的正上方加上 @timer,即表示用 timer()函数来装饰 index()函数;

      2.加上 @timer 后,再调用 index()的时候就相当于:timer(index);而 timer(index) 的返回值是 wrapper() 函数;

      3.所以装饰之后,调用index() 等价于 调用 wrapper(),而wrapper()是一种闭包,包含了timer函数里参数(即 index)的引用;

    - 装饰器扩展示例1:被装饰的对象(本例中就是函数),可以有参数返回值

 1 import time
 2 
 3 def timer(func):
 4     def wrapper(*args,**kwargs):
 5         start_time = time.time()
 6         res = func(*args,**kwargs)
 7         end_time = time.time()
 8         print('Run time is {}'.format(end_time-start_time))
 9         return res
10     return wrapper
11 
12 @timer # 等价于 index = timer(index)
13 def index():
14     time.sleep(3)
15     print('Welcome hahaha...')
16     return 123
17 
18 res = index()
19 print(res)
20 
21 print('---------------')
22 
23 @timer # 等价于 foo = timer(foo)
24 def foo(name):
25     time.sleep(1)
26     print('From foo func...')
27     return '--->>> ' + name
28 
29 res = foo('standby')
30 print(res)
31 
32 ---
33 Welcome hahaha...
34 Run time is 3.0
35 123
36 ---------------
37 From foo func...
38 Run time is 1.0
39 --->>> standby

     - 装饰器扩展示例2:要求用户输入用户名密码,验证通过才能进行相关操作;

 1 def auth_user(func):
 2     def wrapper(*args,**kwargs):
 3         u_name = input('Name is >>>	'.expandtabs(20))
 4         u_pwd = input('Password is >>>	'.expandtabs(20))
 5         if 'standby' == u_name and '123' == u_pwd:
 6             res = func(*args,**kwargs)
 7             return res
 8         else:
 9             print('Invalid input...')
10     return wrapper
11 
12 @auth_user
13 def index():
14     print('Welcome to index page...')
15 
16 res1 = index()
17 print(res1)
18 
19 print('--------------')
20 
21 @auth_user
22 def home(name):
23     print('{}, welcome to index page...'.format(name))
24     return '--->>> ' + name
25 res2 = home('standby')
26 print(res2)
27 
28 ---
29 Name is >>>         standby
30 Password is >>>     123
31 Welcome to index page...
32 None
33 --------------
34 Name is >>>         standby
35 Password is >>>     123
36 standby, welcome to index page...
37 --->>> standby

    - 装饰器扩展示例3:有参装饰器

 1 user_dict = {
 2     'name':None,
 3     'status':False
 4 }
 5 
 6 def auth_type(type='ldap'):
 7     def auth(func):
 8         def wrapper(*args,**kwargs):
 9             if user_dict['name'] and user_dict['status']:
10                 res = func(*args, **kwargs)
11                 return res
12             else:
13                 name = input('Name >>> ')
14                 pwd = input('Pass >>> ')
15                 if 'ldap' == type:
16                     print('ldap')
17                     if 'standby' == name and '123' == pwd:
18                         user_dict['name'] = name
19                         user_dict['status'] = True
20                         res = func(*args,**kwargs)
21                         return res
22                     else:
23                         print('auth failed...')
24                 elif 'mysql' == type:
25                     print('mysql')
26                     if 'standby' == name and '123' == pwd:
27                         user_dict['name'] = name
28                         user_dict['status'] = True
29                         res = func(*args,**kwargs)
30                         return res
31                     else:
32                         print('auth failed...')
33                 else:
34                     print('Invalid auth type.')
35         return wrapper
36     return auth
37 
38 @auth_type('ldap')
39 def index():
40     print('Welcome to index page.')
41 
42 @auth_type('mysql')
43 def home(name):
44     print('{}, welcome home.'.format(name))
45     return 'Hello ' + name
46 index()
47 res = home('standby')
48 print(res)

    - 装饰器扩展示例4:装饰器叠加

 1 def decorator1(func):
 2     def wrapper():
 3         print('hello python, from decorator1')
 4         func()
 5     return wrapper
 6 
 7 def decorator2(func):
 8     def wrapper():
 9         func()
10         print('hello python, from decorator2')
11     return wrapper
12 
13 @decorator1
14 @decorator2
15 def test():
16     print('hello python!')
17 
18 test()
19 
20 ---
21 hello python, from decorator1
22 hello python!
23 hello python, from decorator2

十、迭代器

  - 迭代的概念

1 迭代器
2 迭代的概念:重复+上一次迭代的结果为下一次迭代的初始值
3 重复的过程称为迭代,每次重复即一次迭代,
4 并且每次迭代的结果是下一次迭代的初始值

  - 为什么要有迭代器?

    为什么要有迭代器?

    对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式;

  - 可迭代对象 和 迭代器对象

    - 可迭代对象

      - 只要它定义了可以返回一个迭代器的__iter__方法(即:内置__iter__方法的),那么他就是可迭代对象;

      - 如下所示:list str tuple dict set 这些都是可迭代对象:

 1 iter1 = [1,2].__iter__()
 2 iter2 = 'hello'.__iter__()
 3 iter3 = (1,2).__iter__()
 4 iter4 = {'a':1,'b':2}.__iter__()
 5 iter5 = {1,2,3}.__iter__()
 6 print(type(iter1))
 7 print(type(iter2))
 8 print(type(iter3))
 9 print(type(iter4))
10 print(type(iter5))
11 
12 ---
13 <class 'list_iterator'>
14 <class 'str_iterator'>
15 <class 'tuple_iterator'>
16 <class 'dict_keyiterator'>
17 <class 'set_iterator'>

    - 迭代器对象

      - 执行__iter__方法,得到的结果就是迭代器,迭代器对象有__next__方法;

      - 迭代器是通过  next()  来实现的,每调用一次他就会返回下一个元素,当没有下一个元素的时候返回一个 StopIteration异常;

 1 i = {'k1':1,'k2':9,'k3':7}.__iter__()
 2 print(type(i))
 3 print(i)
 4 print(i.__next__())
 5 print(i.__next__())
 6 print(i.__next__())
 7 # print(i.__next__()) #抛出异常:StopIteration
 8 
 9 ---
10 <class 'dict_keyiterator'>
11 <dict_keyiterator object at 0x00000000006C3778>
12 k2
13 k3
14 k1

  示例1:

 1 dic = {
 2     'k1':'v1',
 3     'k2':'v2',
 4 }
 5 
 6 f = dic.__iter__()                  # 得到一个迭代器对象 f
 7 print(type(f))                      # 迭代器类型
 8 print(f)                     
 9 print(f.__iter__())                 # f.__iter__() 得到的还是 f 自身;(迭代器执行 __iter__() 得到的还是迭代器自身)
10 print(f.__next__())                 # 迭代dic,获取 key 
11 print(f.__next__())
12 
13 ---
14 <class 'dict_keyiterator'>
15 <dict_keyiterator object at 0x00000000006D2778>
16 <dict_keyiterator object at 0x00000000006D2778>
17 k1
18 k2

  示例二: 模拟字典的for循环

 1 dic={'a':1,'b':2,'c':3}
 2 i=dic.__iter__()
 3 while True:
 4     try:
 5         key=i.__next__()                    # 获取dic的key
 6         print(dic[key])                     # 根据key获取对应的value
 7     except StopIteration:
 8         break
 9 
10 ---
11 2
12 3
13 1

  示例三: 枚举类型也是迭代器

 1 l=[10000,2,3,4,5]
 2 
 3 i=enumerate(l)
 4 print(type(i),i)
 5 print(next(i))
 6 print(next(i))
 7 print(next(i))
 8 print(next(i))
 9 print(next(i))
10 
11 ---
12 <class 'enumerate'> <enumerate object at 0x0000000000A10AF8>
13 (0, 10000)
14 (1, 2)
15 (2, 3)
16 (3, 4)
17 (4, 5)

   - 扩展示例:  len()  等价于 __len__()

1 s='hello'
2 print(s.__len__())
3 print(len(s))
4 
5 ---
6 5
7 5

  - 哪些是可迭代的对象?

 1 #如何判断一个对象是可迭代的对象,还是迭代器对象
 2 from collections import Iterable,Iterator
 3 
 4 'abc'.__iter__()                              # 具有 __iter__() 方法的都是可迭代对象
 5 ().__iter__()
 6 [].__iter__()
 7 {'a':1}.__iter__()
 8 {1,2}.__iter__()
 9 f=open('a.txt',mode='r',encoding='utf-8')
10 f.__iter__()
11 
12 
13 # 判断是否是可迭代的对象
14 print(isinstance('abc',Iterable))
15 print(isinstance([],Iterable))
16 print(isinstance((),Iterable))
17 print(isinstance({'a':1},Iterable))
18 print(isinstance({1,2},Iterable))
19 print(isinstance(f,Iterable))
20 
21 print('--------------------')
22 # 判断是否是迭代器对象
23 print(isinstance('abc',Iterator))
24 print(isinstance([],Iterator))
25 print(isinstance((),Iterator))
26 print(isinstance({'a':1},Iterator))
27 print(isinstance({1,2},Iterator))
28 print(isinstance(f,Iterator))                # 只有文件对象是迭代器对象
29 
30 ---
31 True
32 True
33 True
34 True
35 True
36 True
37 --------------------
38 False
39 False
40 False
41 False
42 False
43 True

  - 迭代器优缺点

1 迭代器的优点和缺点
2 优点:
3     1.提供了一种不依赖下标的迭代方式;
4     2.就跌迭代器本身来说,更节省内存,每次只把一个元素放入内存中,依次迭代,而非全部放入内存中;
5 
6 缺点:
7     1. 无法获取迭代器对象的长度;
8     2. 不如序列类型取值灵活,是一次性的,只能往后取值,不能往前退;

  - 迭代器协议

    - 对象有__next__()方法,即 next();

    - 对象有__iter__()方法,对于迭代器对象来说,执行__iter__()方法,得到的结果仍然是它本身;

  - 迭代器应用

    - for循环遍历

 1 dic={'name':'egon','age':18,'height':'180'}
 2 print(dic.items())
 3 for k,v in dic.items():
 4     print(k,v)
 5 
 6 ---
 7 dict_items([('age', 18), ('name', 'egon'), ('height', '180')])
 8 age 18
 9 name egon
10 height 180

    - 迭代器内部实现

 1 dic={'name':'egon','age':18,'height':'180'}
 2 i=iter(dic)
 3 while True:
 4     try:
 5         k=next(i)
 6         print(k + " " + str(dic[k]))
 7     except StopIteration:
 8         break
 9 
10 ---
11 name egon
12 age 18
13 height 180

十一、生成器

  - 什么是生成器?

    - 只要函数体包含yield关键字,该函数就是生成器函数;

    - 生成器就是迭代器;生成器是构造迭代器的最简单有力的工具,与普通函数不同的只有在返回一个值的时候使用yield来替代return,然后yield会为函数封装好 next() 和 iter();

    - 外部通过调用 next() 来触发迭代器(函数)的执行;

    - yield 语句可以让普通函数变成一个生成器,并且相应的 __next__() 方法返回的是 yield 后面的值。

    - 一种更直观的解释是:程序执行到 yield 会返回值并暂停,再次调用 next() 时会从上次暂停的地方继续开始执行;

1 return只能返回一次值,函数就终止了;
2 yield能返回多次值,每次返回都会将函数暂停;
3 下一次next会从上一次暂停的位置继续执行

  - 示例:

    - 示例1:

 1 def foo():
 2     print('first')
 3     yield 1
 4     print('second')
 5     yield 2
 6     print('third')
 7     yield 3
 8     print('fourth')
 9     yield 4
10     print('fifth')
11 
12 g=foo()
13 print(type(g))
14 print(g)
15 print(next(g)) #触发迭代器g的执行,进而触发函数的执行
16 print(next(g))
17 print(next(g))
18 print(next(g))
19 # print(next(g)) # StopIteration 异常
20 
21 ---
22 <class 'generator'>
23 <generator object foo at 0x00000000007DB468>
24 first
25 1
26 second
27 2
28 third
29 3
30 fourth
31 4

    - 示例2:   

 1 def foo():
 2     print('first')
 3     yield 1
 4     print('second')
 5     yield 2
 6     print('third')
 7     yield 3
 8     print('fourth')
 9     yield 4
10     print('fifth')
11 
12 g=foo()                            # 得到一个生成器;
13 print(type(g))
14 print(g)
15 for i in g:                        # 遍历这个生成器;
16     print("返回值是 %s" % i)
17 
18 ---
19 <class 'generator'>
20 <generator object foo at 0x00000000010EB4C0>
21 first
22 返回值是 1
23 second
24 返回值是 2
25 third
26 返回值是 3
27 fourth
28 返回值是 4
29 fifth

  - 生成器应用

 1 # 模拟Linux中命令:tail -f a.txt | grep 'python'
 2 
 3 import time
 4 def tail(filepath):
 5     with open(filepath,encoding='utf-8') as f:
 6         f.seek(0,2)                      # 定位到文件末尾
 7         while True:
 8             line=f.readline().strip()    # 从最后一行开始读内容
 9             if line:                     # 如果读到内容则返回该行内容,然后进入下一轮 while循环,并暂停,等待下一次调用;
10                 yield line
11             else:
12                 time.sleep(0.2)
13 
14 def grep(pattern,lines):
15     for line in lines:                   # 循环tail函数中yield返回的文件新增内容;
16         if pattern in line:              # 如果包含关键字 patten,则返回该行内容,然后进入下一次for循环并暂停在这一步,等待下一次调用;
17             yield line
18 
19 g=grep('python',tail('a.txt'))           # patten 即 'python'
20 print(g)                                 # 得到一个生成器对象:<generator object grep at 0x0000000000A3B570>
21 for i in g:                              # 遍历该生成器(即 迭代器),循环调用 grep() 和 tail() 函数;
22     print(i)                             # 将grep()函数返回的内容打印出来;

十二、练习

要求:

  操作文件:haproxy.cfg,实现对这个文件配置的新增、删除以及查询的功能;

  - 新增的时候要考虑backend是否已存在,没有则新增backend;有则在进一步判断是否存在响应的record,有则不能重复插入,没有则可以插入;

  - 删除的时候要判断是否存在用户想要删除的record,不存在则反馈用户提示信息;存在的话就需要考虑backend含有几个record,大于1个,则删除record,如果只有1个record,则需要把record和backend都要删除;

  - 查询则是列出用户所输入的backend下的所有record信息;

代码实现:

  1 #!/usr/bin/python
  2 # -*- coding:utf-8 -*-
  3 
  4 
  5 def get_backend_dict():
  6     with open('haproxy.cfg',mode='r',encoding='utf-8') as rf:
  7         content_list = rf.readlines()
  8     backend_dict = {}
  9     for i in range(0, len(content_list)):
 10         if content_list[i].startswith('backend '):
 11             backend_dict[content_list[i].split()[1]] = i
 12     return content_list,backend_dict
 13 
 14 def select(data):
 15     content_list,backend_dict = get_backend_dict()
 16     if data not in backend_dict:
 17         print('No backend record found.')
 18     else:
 19         for i in range(backend_dict[data],len(content_list)-1):
 20             if content_list[i+1].startswith('backend '):
 21                 return
 22             print(content_list[i+1],end='')
 23 
 24 def add(data):
 25     content_list,backend_dict = get_backend_dict()
 26     sub_record_info = "        server " + data['record']['server'] + " " 
 27                       + data['record']['server'] + " weight " 
 28                       + str(data['record']['weight']) + " maxcon " 
 29                       + str(data['record']['maxcon']) + "
"
 30     backend_info = "
" + "backend " + data['backend'] + "
" + sub_record_info
 31     if data['backend'] in backend_dict:
 32         index = -1
 33         tmp_list = []
 34         print('The backend:{} already exist, now to append a record...'.format(data['backend']))
 35         for i in range(backend_dict[data['backend']],len(content_list)-1):
 36             if content_list[i+1].startswith('backend '):
 37                 index = i
 38                 break
 39             else:
 40                 tmp_list.append(content_list[i+1])
 41         if sub_record_info not in tmp_list:
 42             if -1 == index:
 43                 content_list.append(sub_record_info)
 44             else:
 45                 content_list.insert(index,sub_record_info)
 46             with open('haproxy.cfg', mode='w', encoding='utf-8') as wf:
 47                 wf.writelines(content_list)
 48             print("The backend:{} already exist, append a record successfully.".format(data['backend']))
 49         else:
 50             print("The record:{} already exist, add failed.".format(sub_record_info.strip()))
 51     else:
 52         with open('haproxy.cfg',mode='a+',encoding='utf-8') as wf:
 53             wf.write(backend_info)
 54         print("New backend added successfully.")
 55 
 56 def remove(data):
 57     content_list, backend_dict = get_backend_dict()
 58     if data['backend'] not in backend_dict:
 59         print("The backend:{} doesn't exist, remove failed.".format(data['backend']))
 60     else:
 61         tmp_list = []
 62         sub_record_info = "        server " + data['record']['server'] + " " 
 63                           + data['record']['server'] + " weight " 
 64                           + str(data['record']['weight']) + " maxcon " 
 65                           + str(data['record']['maxcon']) + "
"
 66         if sub_record_info not in content_list:
 67             print("The record:{} doesn't exist, remove failed.".format(sub_record_info.strip()))
 68             return
 69         else:
 70             content_list.remove(sub_record_info)
 71         for i in range(backend_dict[data['backend']],len(content_list)-1):
 72             if content_list[i+1].startswith('backend '):
 73                 break
 74             else:
 75                 tmp_list.append(content_list[i+1])
 76         if 0 == len(tmp_list):
 77             content_list.remove(data['backend'])
 78         with open('haproxy.cfg',mode='w',encoding='utf-8') as wf:
 79             wf.writelines(content_list)
 80         print("The record:{} removed successfully.".format(sub_record_info.strip()))
 81 
 82 if __name__ == '__main__':
 83     info = '''
 84     1:查询
 85     2:增加
 86     3:删除
 87     4:退出程序
 88     '''
 89     menu_dict = {
 90         '1':select,
 91         '2':add,
 92         '3':remove,
 93         '4':exit,
 94     }
 95     while True:
 96         print(info)
 97         chioce = input('请输入操作序号>>> ').strip()
 98         if '4' == chioce: break
 99         if 0 == len(chioce) or chioce not in menu_dict:continue
100         elif '1' == chioce:
101             backend = input('请输入要查询的backend>>> ').strip()
102             if 0 == len(backend):
103                 print('Invalid input...')
104             else:
105                 select(backend)
106         else:
107             notice = 'Input Fomat: backend domain|server(domain/ip)|weight value|maxcon value'
108             print(notice)
109             data = input('请输入数据>>> ').strip()
110             data_list = data.split('|')
111             if 4 == len(data_list) and data_list[2].isdigit() and data_list[3].isdigit():
112                 input_dict = {
113                     'backend':data_list[0],
114                     'record':{
115                         'server':data_list[1],
116                         'weight':int(data_list[2]),
117                         'maxcon':int(data_list[3]),
118                     },
119                 }
120                 menu_dict[chioce](input_dict)
121             else:
122                 print('Input invalid...')

 文件:

 1 frontend www
 2         bind *:80
 3         acl web hdr_beg(host)  10.207.252.45
 4         acl web1 hdr_beg(host)  10.207.252.46
 5         use_backend webserver if web
 6         use_backend web1server if web1
 7 
 8 backend www.oldboy1.org
 9         server 192.168.1.110 192.168.1.110 weight 10 maxcon 3007
10         server 192.168.1.120 192.168.1.120 weight 10 maxcon 3009
11         server 192.168.1.110 192.168.1.110 weight 10 maxcon 3009
12 
13 backend www.oldboy2.org
14         server 192.168.1.130 192.168.1.130 weight 20 maxcon 3000
15         server 192.168.1.131 192.168.1.131 weight 20 maxcon 3000
16 backend www.qq.com
17         server 3.3.3.3 3.3.3.3 weight 90 maxcon 100
18         server 3.3.3.100 3.3.3.100 weight 90 maxcon 700
19         server 8.8.8.9 8.8.8.9 weight 20 maxcon 200
作者:Standby一生热爱名山大川、草原沙漠,还有妹子
出处:http://www.cnblogs.com/standby/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文地址:https://www.cnblogs.com/standby/p/6910613.html