Python基础(函数部分)-day04

时间:2023-02-05 23:42:05

写在前面



    加勒比海盗今天上映!



一、函数的基本概念

- 函数是什么?

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

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

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

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

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

  2.代码冗余;

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

- 函数的分类

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

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

- 函数的两个阶段

  1.定义阶段;

  2.使用阶段;

二、函数的定义

- 为何要定义函数?

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

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

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

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

- 如何定义函数?

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

- 定义函数的三种形式

  1.无参函数;

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

  2.有参函数;

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

  3.空函数;

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

- 三元表达式

 x = 1
y = 7
res = x if x > y else y
print(res) ---
7

三、函数的调用

- 函数的调用

  1.先找到函数名字;

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

 def foo():
print("From foo func")
print(foo) # 打印foo函数代码的内存地址
foo() ---
<function foo at 0x0000000000B5C268>
From foo func

- 函数调用的三种形式

  1.语句形式:foo()

 def my_max(x,y):
res=x if x >y else y
return res res = my_max(1,9)
print(res) ---
9

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

 def my_max(x,y):
res=x if x >y else y
return res res = my_max(1,9)*100
print(res) ---
900

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

 def my_max(x,y):
res=x if x >y else y
return res res=my_max(my_max(10,20),30)
print(res) ---
30

四、函数的参数

- 函数参数的分类

  - 形参

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

   - 实参

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

   - 意义

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

- 具体应用

  - 1.位置参数

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

 def foo(name,age):
print(name,age) foo('alex',20) ---
alex 20

   - 2.关键字参数

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

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

 def foo(name,age):
print(name,age) foo('alex',age=19) # ok foo(age=19,'alex') # SyntaxError: positional argument follows keyword argument foo('alex',name='egon') # TypeError: foo() got multiple values for argument 'name'

   - 3.默认参数

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

    注意:

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

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

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

 def foo(name,gender='male'):
print(name,gender) foo('alex')
foo('kelly','female') ---
alex male
kelly female

   - 4.可变长参数

    参考:egon函数草稿

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

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

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

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

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

 def foo(x, y, *args):
print(x, y)
print(args)
print(*args) foo(1, 2, *[3, 4, 5]) ---
1 2
(3, 4, 5)
3 4 5

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

 def foo(x, y, z):
print(x, y, z) foo(100,*(2, 3,)) ---
100 2 3

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

 def foo(x, y, z):
print(x, y, z) foo(*(1,2, 3,))
foo(*[1,2, 3,]) ---
1 2 3
1 2 3

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

 def foo(x,y,*args):
print("x -> {}, y -> {}, args -> {}".format(x,y,args))
for item in args:
print(type(item),item)
print('------------------------') foo(1,2,[1,2],{'k1':'v1'},10,'AAAAA') ---
x -> 1, y -> 2, args -> ([1, 2], {'k1': 'v1'}, 10, 'AAAAA')
<class 'list'> [1, 2]
<class 'dict'> {'k1': 'v1'}
<class 'int'> 10
<class 'str'> AAAAA
------------------------

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

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

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

 def foo(x, y, **kwargs):
print(x, y)
print(kwargs)
print(*kwargs) foo(1, y=2, **{'a': 1, 'b': 2, 'c': 3}) ---
1 2
{'a': 1, 'c': 3, 'b': 2}
a c b

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

 def foo(x, y, z):
print("x is %s\ny is %s\nz is %s" % (x, y, z)) foo(**{'z': 1, 'x': 2, 'y': 3}) ---
x is 2
y is 3
z is 1

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

 def foo(x, y, z):
print("x is %s\ny is %s\nz is %s" % (x, y, z)) foo(x=99,**{'z': 1, 'y': 3}) ---
x is 99
y is 3
z is 1

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

 # ** 会把溢出的按关键字定义的实参都接收,以字典的形式赋值给kwargs;可以通过 *kwargs 得到溢出的参数的key值;但不能用 **kwargs 获取其中的值;
def foo(x,y,**kwargs):
print("x -> {}, y -> {}, kwargs -> {}".format(x, y, kwargs))
print(*kwargs)
for key,value in kwargs.items():
print(key,value)
print('------------------------') foo(1,y=9)
foo(x=1,y=9)
foo(1,y=9,m=88,n=99)
foo(1,y=9,m=[3,4],n={'k1':'v1'},l=(1,2,)) ---
x -> 1, y -> 9, kwargs -> {} ------------------------
x -> 1, y -> 9, kwargs -> {} ------------------------
x -> 1, y -> 9, kwargs -> {'n': 99, 'm': 88}
n m
n 99
m 88
------------------------
x -> 1, y -> 9, kwargs -> {'n': {'k1': 'v1'}, 'm': [3, 4], 'l': (1, 2)}
n m l
n {'k1': 'v1'}
m [3, 4]
l (1, 2)
------------------------

   - 5.命名关键字参数

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

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

 def foo(name,age,*,sex='male',height):
print(name,age)
print(sex)
print(height) foo('egon',17,height='')
print('---------')
foo('egon',17,sex='female',height='') ---
egon 17
male
185
---------
egon 17
female
185

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

 def foo(x, y, *args, a=1, b, **kwargs):
print(x, y)
print(args)
print(a)
print(b)
print(kwargs) foo(1, 2, 3, 4, 5, b=3, c=4, d=5) ---
1 2
(3, 4, 5)
1
3
{'d': 5, 'c': 4}

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

 def foo(name,age=10,*args,sex='male',height,**kwargs):
print(name)
print(age)
print(args)
print(sex)
print(height)
print(kwargs) foo('alex',1,2,3,4,5,sex='female',height='',a=1,b=2,c=3) ---
alex
1
(2, 3, 4, 5)
female
150
{'c': 3, 'a': 1, 'b': 2}

  - 扩展和思考

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

五、函数对象

  - 1 可以被引用

 def foo():
print('from foo') func=foo
print(foo)
print(func)
func() ---
<function foo at 0x0000000000B6C268>
<function foo at 0x0000000000B6C268>
from foo

  - 2 可以当作参数传递

 def foo():
print('from foo') def bar(func):
print(func)
func() bar(foo) ---
<function foo at 0x000000000072C268>
from foo

  - 3 返回值可以是函数

 def foo():
print('from foo')
def bar(func):
return func f=bar(foo)
print(f)
f() ---
<function foo at 0x00000000010DC268>
from foo

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

 # 可以当做容器类的元素
def select():
print('---->>> select')
def update():
print('---->>> update')
def delete():
print('---->>> delete')
def insert():
print('---->>> insert') func_dict = {
'select':select,
'update':update,
'delete':delete,
'insert':insert
} def main():
while True:
sql = input(">>> ").strip()
if not sql:
continue
elif 'Q' == sql.upper():
print('Bye...')
exit(0)
else:
cmd_list = sql.split()
if cmd_list[0] in func_dict:
func_dict[cmd_list[0]]()
else:
print('Input invalid') main() 

六、函数的嵌套

  - 嵌套调用

 def max2(x,y):
return x if x > y else y
def max4(a,b,c,d):
res1=max2(a,b)
res2=max2(res1,c)
res3=max2(res2,d)
return res3 print(max4(10,99,31,22)) ---
99

  - 嵌套定义

 def f1():
print('from f1')
def f2():
print('from f2')
def f3():
print('from f3')
f3()
f2() f1() ---
from f1
from f2
from f3

七、命名空间和作用域

参考:Python命名空间的本质

  - 命名空间

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

 print(max([1,2,3]))
 import builtins
for i in dir(builtins):
print(i)
 ArithmeticError
AssertionError
AttributeError
BaseException
BlockingIOError
BrokenPipeError
BufferError
BytesWarning
ChildProcessError
ConnectionAbortedError
ConnectionError
ConnectionRefusedError
ConnectionResetError
DeprecationWarning
EOFError
Ellipsis
EnvironmentError
Exception
False
FileExistsError
FileNotFoundError
FloatingPointError
FutureWarning
GeneratorExit
IOError
ImportError
ImportWarning
IndentationError
IndexError
InterruptedError
IsADirectoryError
KeyError
KeyboardInterrupt
LookupError
MemoryError
NameError
None
NotADirectoryError
NotImplemented
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
PermissionError
ProcessLookupError
RecursionError
ReferenceError
ResourceWarning
RuntimeError
RuntimeWarning
StopAsyncIteration
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TimeoutError
True
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
WindowsError
ZeroDivisionError
__build_class__
__debug__
__doc__
__import__
__loader__
__name__
__package__
__spec__
abs
all
any
ascii
bin
bool
bytearray
bytes
callable
chr
classmethod
compile
complex
copyright
credits
delattr
dict
dir
divmod
enumerate
eval
exec
exit
filter
float
format
frozenset
getattr
globals
hasattr
hash
help
hex
id
input
int
isinstance
issubclass
iter
len
license
list
locals
map
max
memoryview
min
next
object
oct
open
ord
pow
print
property
quit
range
repr
reversed
round
set
setattr
slice
sorted
staticmethod
str
sum
super
tuple
type
vars
zip

Python解释器启动后,加载的内置名称空间所包含的变量

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

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

 info = "Adress : "
def func_father(country):
def func_son(area):
city= "Shanghai " #此处的city变量,覆盖了父函数的city变量
print(info + country + city + area)
city = " Beijing "
#调用内部函数
func_son("ChaoYang "); func_father("China ") ---
Adress : China Shanghai ChaoYang

  - 作用域

    - 1.全局作用域

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

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

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

    - 2.局部作用域

      只包含局部名称空间;

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

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

    - 3.名字的查找顺序

 LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__

 1.locals 是函数内的名字空间,包括局部变量和形参    # 局部名称空间
2.enclosing 外部嵌套函数的名字空间(闭包中常见)
3.globals 全局变量,函数定义所在模块的名字空间     # 全局名称空间
4.builtins 内置模块的名字空间 # 内置名称空间 如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name 'aa' is not defined;

    示例:

 x=1000
def func():
x=2
y=('a','b',)
print(x,y)
print(globals())
print(locals())
func() ---
2 ('a', 'b')
{'__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}
{'y': ('a', 'b'), 'x': 2}

八、闭包的概念

  - 闭包的定义

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

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

    - 扩展:

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

 def f1():
x = 1
user_list = ['alex','egon']
def f2():
print(x)
print(user_list)
return f2 f = f1() # 返回一个闭包函数对象:即f2;
print(f)
print(f.__closure__) # 查看该闭包函数所包含的元素;
for item in f.__closure__:
print(item)
print(item.cell_contents)   # 打印出每一个元素的值 ---
<function f1.<locals>.f2 at 0x00000000006CC400>
(<cell at 0x0000000000697D68: list object at 0x00000000006D0F48>, <cell at 0x0000000000697D98: int object at 0x0000000060BE01D0>)
<cell at 0x0000000000697D68: list object at 0x00000000006D0F48>
['alex', 'egon']
<cell at 0x0000000000697D98: int object at 0x0000000060BE01D0>
1

  - 闭包的意义

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

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

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

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

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

九、装饰器

参考:Advanced Uses of Python Decorators

  - 什么是装饰器?

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

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

  - 为什么要用装饰器?

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

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

  - 装饰器应用

    - 示例:

 # 开放封闭原则:对修改封闭,对扩展开放;
# 装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象;
# 装饰器就是闭包的一种实现,只不过参数是函数对象; import time def timer(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('Run time is {}'.format(end_time-start_time))
return wrapper @timer # 等价于 index = timer(index)
def index():
print('Start to work...')
time.sleep(3)
print('End the work...') index() ---
Start to work...
End the work...
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:被装饰的对象(本例中就是函数),可以有参数返回值

 import time

 def timer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
res = func(*args,**kwargs)
end_time = time.time()
print('Run time is {}'.format(end_time-start_time))
return res
return wrapper @timer # 等价于 index = timer(index)
def index():
time.sleep(3)
print('Welcome hahaha...')
return 123 res = index()
print(res) print('---------------') @timer # 等价于 foo = timer(foo)
def foo(name):
time.sleep(1)
print('From foo func...')
return '--->>> ' + name res = foo('standby')
print(res) ---
Welcome hahaha...
Run time is 3.0
123
---------------
From foo func...
Run time is 1.0
--->>> standby

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

 def auth_user(func):
def wrapper(*args,**kwargs):
u_name = input('Name is >>>\t'.expandtabs(20))
u_pwd = input('Password is >>>\t'.expandtabs(20))
if 'standby' == u_name and '' == u_pwd:
res = func(*args,**kwargs)
return res
else:
print('Invalid input...')
return wrapper @auth_user
def index():
print('Welcome to index page...') res1 = index()
print(res1) print('--------------') @auth_user
def home(name):
print('{}, welcome to index page...'.format(name))
return '--->>> ' + name
res2 = home('standby')
print(res2) ---
Name is >>> standby
Password is >>> 123
Welcome to index page...
None
--------------
Name is >>> standby
Password is >>> 123
standby, welcome to index page...
--->>> standby

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

 user_dict = {
'name':None,
'status':False
} def auth_type(type='ldap'):
def auth(func):
def wrapper(*args,**kwargs):
if user_dict['name'] and user_dict['status']:
res = func(*args, **kwargs)
return res
else:
name = input('Name >>> ')
pwd = input('Pass >>> ')
if 'ldap' == type:
print('ldap')
if 'standby' == name and '' == pwd:
user_dict['name'] = name
user_dict['status'] = True
res = func(*args,**kwargs)
return res
else:
print('auth failed...')
elif 'mysql' == type:
print('mysql')
if 'standby' == name and '' == pwd:
user_dict['name'] = name
user_dict['status'] = True
res = func(*args,**kwargs)
return res
else:
print('auth failed...')
else:
print('Invalid auth type.')
return wrapper
return auth @auth_type('ldap')
def index():
print('Welcome to index page.') @auth_type('mysql')
def home(name):
print('{}, welcome home.'.format(name))
return 'Hello ' + name
index()
res = home('standby')
print(res)

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

 def decorator1(func):
def wrapper():
print('hello python, from decorator1')
func()
return wrapper def decorator2(func):
def wrapper():
func()
print('hello python, from decorator2')
return wrapper @decorator1
@decorator2
def test():
print('hello python!') test() ---
hello python, from decorator1
hello python!
hello python, from decorator2

十、迭代器

  - 迭代的概念

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

  - 为什么要有迭代器?

    为什么要有迭代器?

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

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

    - 可迭代对象

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

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

 iter1 = [1,2].__iter__()
iter2 = 'hello'.__iter__()
iter3 = (1,2).__iter__()
iter4 = {'a':1,'b':2}.__iter__()
iter5 = {1,2,3}.__iter__()
print(type(iter1))
print(type(iter2))
print(type(iter3))
print(type(iter4))
print(type(iter5)) ---
<class 'list_iterator'>
<class 'str_iterator'>
<class 'tuple_iterator'>
<class 'dict_keyiterator'>
<class 'set_iterator'>

    - 迭代器对象

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

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

 i = {'k1':1,'k2':9,'k3':7}.__iter__()
print(type(i))
print(i)
print(i.__next__())
print(i.__next__())
print(i.__next__())
# print(i.__next__()) #抛出异常:StopIteration ---
<class 'dict_keyiterator'>
<dict_keyiterator object at 0x00000000006C3778>
k2
k3
k1

  示例1:

 dic = {
'k1':'v1',
'k2':'v2',
} f = dic.__iter__() # 得到一个迭代器对象 f
print(type(f)) # 迭代器类型
print(f)
print(f.__iter__()) # f.__iter__() 得到的还是 f 自身;(迭代器执行 __iter__() 得到的还是迭代器自身)
print(f.__next__()) # 迭代dic,获取 key
print(f.__next__()) ---
<class 'dict_keyiterator'>
<dict_keyiterator object at 0x00000000006D2778>
<dict_keyiterator object at 0x00000000006D2778>
k1
k2

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

 dic={'a':1,'b':2,'c':3}
i=dic.__iter__()
while True:
try:
key=i.__next__() # 获取dic的key
print(dic[key]) # 根据key获取对应的value
except StopIteration:
break ---
2
3
1

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

 l=[10000,2,3,4,5]

 i=enumerate(l)
print(type(i),i)
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i)) ---
<class 'enumerate'> <enumerate object at 0x0000000000A10AF8>
(0, 10000)
(1, 2)
(2, 3)
(3, 4)
(4, 5)

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

 s='hello'
print(s.__len__())
print(len(s)) ---
5
5

  - 哪些是可迭代的对象?

 #如何判断一个对象是可迭代的对象,还是迭代器对象
from collections import Iterable,Iterator 'abc'.__iter__() # 具有 __iter__() 方法的都是可迭代对象
().__iter__()
[].__iter__()
{'a':1}.__iter__()
{1,2}.__iter__()
f=open('a.txt',mode='r',encoding='utf-8')
f.__iter__() # 判断是否是可迭代的对象
print(isinstance('abc',Iterable))
print(isinstance([],Iterable))
print(isinstance((),Iterable))
print(isinstance({'a':1},Iterable))
print(isinstance({1,2},Iterable))
print(isinstance(f,Iterable)) print('--------------------')
# 判断是否是迭代器对象
print(isinstance('abc',Iterator))
print(isinstance([],Iterator))
print(isinstance((),Iterator))
print(isinstance({'a':1},Iterator))
print(isinstance({1,2},Iterator))
print(isinstance(f,Iterator)) # 只有文件对象是迭代器对象 ---
True
True
True
True
True
True
--------------------
False
False
False
False
False
True

  - 迭代器优缺点

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

  - 迭代器协议

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

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

  - 迭代器应用

    - for循环遍历

 dic={'name':'egon','age':18,'height':''}
print(dic.items())
for k,v in dic.items():
print(k,v) ---
dict_items([('age', 18), ('name', 'egon'), ('height', '')])
age 18
name egon
height 180

    - 迭代器内部实现

 dic={'name':'egon','age':18,'height':''}
i=iter(dic)
while True:
try:
k=next(i)
print(k + " " + str(dic[k]))
except StopIteration:
break ---
name egon
age 18
height 180

十一、生成器

  - 什么是生成器?

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

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

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

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

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

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

  - 示例:

    - 示例1:

 def foo():
print('first')
yield 1
print('second')
yield 2
print('third')
yield 3
print('fourth')
yield 4
print('fifth') g=foo()
print(type(g))
print(g)
print(next(g)) #触发迭代器g的执行,进而触发函数的执行
print(next(g))
print(next(g))
print(next(g))
# print(next(g)) # StopIteration 异常 ---
<class 'generator'>
<generator object foo at 0x00000000007DB468>
first
1
second
2
third
3
fourth
4

    - 示例2:   

 def foo():
print('first')
yield 1
print('second')
yield 2
print('third')
yield 3
print('fourth')
yield 4
print('fifth') g=foo() # 得到一个生成器;
print(type(g))
print(g)
for i in g: # 遍历这个生成器;
print("返回值是 %s" % i) ---
<class 'generator'>
<generator object foo at 0x00000000010EB4C0>
first
返回值是 1
second
返回值是 2
third
返回值是 3
fourth
返回值是 4
fifth

  - 生成器应用

 # 模拟Linux中命令:tail -f a.txt | grep 'python'

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

十二、练习

要求:

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

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

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

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

代码实现:

 #!/usr/bin/python
# -*- coding:utf-8 -*- def get_backend_dict():
with open('haproxy.cfg',mode='r',encoding='utf-8') as rf:
content_list = rf.readlines()
backend_dict = {}
for i in range(0, len(content_list)):
if content_list[i].startswith('backend '):
backend_dict[content_list[i].split()[1]] = i
return content_list,backend_dict def select(data):
content_list,backend_dict = get_backend_dict()
if data not in backend_dict:
print('No backend record found.')
else:
for i in range(backend_dict[data],len(content_list)-1):
if content_list[i+1].startswith('backend '):
return
print(content_list[i+1],end='') def add(data):
content_list,backend_dict = get_backend_dict()
sub_record_info = " server " + data['record']['server'] + " " \
+ data['record']['server'] + " weight " \
+ str(data['record']['weight']) + " maxcon " \
+ str(data['record']['maxcon']) + "\n"
backend_info = "\n" + "backend " + data['backend'] + "\n" + sub_record_info
if data['backend'] in backend_dict:
index = -1
tmp_list = []
print('The backend:{} already exist, now to append a record...'.format(data['backend']))
for i in range(backend_dict[data['backend']],len(content_list)-1):
if content_list[i+1].startswith('backend '):
index = i
break
else:
tmp_list.append(content_list[i+1])
if sub_record_info not in tmp_list:
if -1 == index:
content_list.append(sub_record_info)
else:
content_list.insert(index,sub_record_info)
with open('haproxy.cfg', mode='w', encoding='utf-8') as wf:
wf.writelines(content_list)
print("The backend:{} already exist, append a record successfully.".format(data['backend']))
else:
print("The record:{} already exist, add failed.".format(sub_record_info.strip()))
else:
with open('haproxy.cfg',mode='a+',encoding='utf-8') as wf:
wf.write(backend_info)
print("New backend added successfully.") def remove(data):
content_list, backend_dict = get_backend_dict()
if data['backend'] not in backend_dict:
print("The backend:{} doesn't exist, remove failed.".format(data['backend']))
else:
tmp_list = []
sub_record_info = " server " + data['record']['server'] + " " \
+ data['record']['server'] + " weight " \
+ str(data['record']['weight']) + " maxcon " \
+ str(data['record']['maxcon']) + "\n"
if sub_record_info not in content_list:
print("The record:{} doesn't exist, remove failed.".format(sub_record_info.strip()))
return
else:
content_list.remove(sub_record_info)
for i in range(backend_dict[data['backend']],len(content_list)-1):
if content_list[i+1].startswith('backend '):
break
else:
tmp_list.append(content_list[i+1])
if 0 == len(tmp_list):
content_list.remove(data['backend'])
with open('haproxy.cfg',mode='w',encoding='utf-8') as wf:
wf.writelines(content_list)
print("The record:{} removed successfully.".format(sub_record_info.strip())) if __name__ == '__main__':
info = '''
1:查询
2:增加
3:删除
4:退出程序
'''
menu_dict = {
'':select,
'':add,
'':remove,
'':exit,
}
while True:
print(info)
chioce = input('请输入操作序号>>> ').strip()
if '' == chioce: break
if 0 == len(chioce) or chioce not in menu_dict:continue
elif '' == chioce:
backend = input('请输入要查询的backend>>> ').strip()
if 0 == len(backend):
print('Invalid input...')
else:
select(backend)
else:
notice = 'Input Fomat: backend domain|server(domain/ip)|weight value|maxcon value'
print(notice)
data = input('请输入数据>>> ').strip()
data_list = data.split('|')
if 4 == len(data_list) and data_list[2].isdigit() and data_list[3].isdigit():
input_dict = {
'backend':data_list[0],
'record':{
'server':data_list[1],
'weight':int(data_list[2]),
'maxcon':int(data_list[3]),
},
}
menu_dict[chioce](input_dict)
else:
print('Input invalid...')

文件:

 frontend www
bind *:80
acl web hdr_beg(host) 10.207.252.45
acl web1 hdr_beg(host) 10.207.252.46
use_backend webserver if web
use_backend web1server if web1 backend www.oldboy1.org
server 192.168.1.110 192.168.1.110 weight 10 maxcon 3007
server 192.168.1.120 192.168.1.120 weight 10 maxcon 3009
server 192.168.1.110 192.168.1.110 weight 10 maxcon 3009 backend www.oldboy2.org
server 192.168.1.130 192.168.1.130 weight 20 maxcon 3000
server 192.168.1.131 192.168.1.131 weight 20 maxcon 3000
backend www.qq.com
server 3.3.3.3 3.3.3.3 weight 90 maxcon 100
server 3.3.3.100 3.3.3.100 weight 90 maxcon 700
server 8.8.8.9 8.8.8.9 weight 20 maxcon 200