Python-04-基础

时间:2024-01-08 11:49:32

一、装饰器(decorator)

  • 装饰器本质上也是函数,目的是为其他函数添加附加功能(装饰其他函数)
  • Python通过使用装饰器来达到代码的开放与封闭。

原则:

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

我们现在有个需求:写一个计时器来统计每个函数执行的时间长度,满足上面的2个原则。

  • 实现一:
import time

def timer(func):
start_time = time.time()
func()
stop_time = time.time()
print("The function run time is %s" % (stop_time-start_time))
return func def f1():
print("This is function F1")
time.sleep(1) timer(f1)

上述方法虽然可以实现需求,但是它却改变了原函数f1的调用方式,所以不满足第2个原则。

  • 我们下面使用装饰器来实现:
import time

def timer(func):
def deco():
start_time = time.time()
func()
stop_time = time.time()
print("The func run time is %s" % (stop_time-start_time))
return deco @timer
def f1():
print("This is function F1")
time.sleep(1) f1()

装饰器使用“@+函数名”的语法,当代码运行到@timer的时候,会把下面被装饰的函数f1做为自己的参数,相当于:func = f1,当f1()执行时,实际上执行的是deco()。

  • 当被装饰的函数有参数时我们怎么实现呢?
  1. 参数固定时:
def login(func):
def deco(user):
print("[装饰器]正在验证登录:")
func(user)
return deco @login
def wel(user):
print("Welcome %s to login the page!" % user) wel("huyd")

2.参数不固定时:

def login(func):
def deco(*args,**kwargs):
print("[装饰器]正在验证登录:")
func(*args,**kwargs)
return deco @login
def wel(*args,**kwargs):
print("Welcome {} to login the page!".format(*args,**kwargs)) wel("huyd")
  • 当被装饰的函数有返回值时,又该怎么实现呢?
def login(func):
def deco(*args,**kwargs):
print("[装饰器]正在验证登录:")
res = func(*args,**kwargs)
return res # 此处应该将被装饰函数的返回值return
return deco @login
def wel(*args,**kwargs):
print("Welcome {} to login the page!".format(*args,**kwargs))
return "sucessful" # 被装饰的函数有返回值 wel = wel("huyd")
print(wel)
  • 装饰器也可以有自己的参数
def deco_fun1():
print("装饰器功能1") def deco_fun2():
print("装饰器功能2") def deco(deco_fun): # 第1层传入装饰器参数(这里只传入了1个参数,也可传入多个)
def outer(main_fun): # 第2层传入被装饰的函数名
def inner(): # 第3层传入被装饰函数的参数
deco_fun()
main_fun()
return inner
return outer @deco(deco_fun1) # 这样就使用了装饰器deco功能1
def index():
print("我是主程序") index()

需求:使用装饰器写一个简单的用户认证功能,当登录index页面时使用本地验证用户名,登录home页面时使用LDAP验证。

user,passwd = 'huyd','123'

def auth(auth_type):     # 判断装饰器的参数
def outer_wrapper(func):
def wrapper(*args,**kwargs):
if auth_type == "local":
username = input("Username:").strip()
password = input("Password:").strip()
if username == user and password == passwd:
print("\033[32;1mLogin successful!\033[0m")
res = func(*args,**kwargs)
return res
else:
print("\033[31;1mInvalid username or password!\033[0m")
elif auth_type == "ldap":
print("Home page must be LDAP type Login!")
return wrapper
return outer_wrapper @auth(auth_type="local")
def index():
print("Welcome to index page!")
return "Index" @auth(auth_type="ldap")
def home():
print("Welcome to home page!")
return "Home" # index() # 进入index页面时使用本地验证登录
home() # 进入home页面时使用LDAP验证登录

二、生成器(generator)& 迭代器(iterator)

  • 迭代器是访问集合中元素的一种方式,迭代器 object从集合中的第一个元素开始访问,知道所有的元素被访问完成.
  • 所以迭代器的特点是:只能往前,不能后退
  • 迭代器的优点:不需要提前准备整个迭代器中的所有元素,仅仅迭代到某个元素时才计算该元素,而之前或者之后,元素可以不存在或者销毁.因为这个特点,迭代器特别适合遍历文件比较大或者无限的集合.
  • 总结下迭代器 iter()的特点吧:
  1. 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
  2. 不能随机访问集合中的某个值 ,只能从头到尾依次访问
  3. 访问到一半时不能往回退
  4. 便于循环比较大的数据集合,节省内存

迭代器是用__next__()来取值的,来看个例子吧:

gen = iter([1,2,3])

# 取值方法一:
print(gen.__next__())
print(gen.__next__())
print(gen.__next__())
print(gen.__next__()) #再取值就会报错 # 取值方法二:
for i in gen:
print(i)

能被for直接循环的都是可迭代对象,如列表,字典等。判断一个对象是不是可迭代对象,可以使用isinstance(*, Iterable)来判断。

生成器在被调用的时候返回一个迭代器。生成器有个标志性的语法yield。

当生成器被调用时,yield返回一个值,并记住当前位置,下一次调用时,从yield下一句语句运行返回下一个值。

def catch_mony(amount):
while amount > 0:
amount -= 100
print("又来取钱了!")
yield "给你100吧,剩余:{}".format(amount) atm = catch_mony(500)
print(type(atm))
print(atm.__next__())
print(atm.__next__())
print("去看电影!。。。")
print(atm.__next__()) ======================================
运行结果: <class 'generator'>
又来取钱了!
给你100吧,剩余:400
又来取钱了!
给你100吧,剩余:300
去看电影!。。。
又来取钱了!
给你100吧,剩余:200

此外,yield还有实现在单线程模式下实现并发运算的效果。如下例:

import time

def consumer(name):
print("{}准备吃包子啦!".format(name))
while True:
baozi = yield # yield也可以用于接收值
print("第{}次的包子来了,被{}吃掉了!".format(baozi, name)) def producer(name):
c1 = consumer('A')
c2 = consumer('B')
c1.__next__()
c2.__next__()
print("{}准备开始做包子了!".format(name))
for i in range(1, 11):
time.sleep(1)
print("{}第{}次做了两个包子。".format(name, i))
c1.send(i) # 将i的值传给yield
c2.send(i) # 将i的值传给yield producer("老板")

三、Json & pickle 数据序列化

用于序列化的两个模块:

  • json,用于字符串 和 python数据类型间进行转换
  • pickle,用于python特有的类型 和 python的数据类型间进行转换
  • Json模块提供了四个功能:dumps、dump、loads、load
  • pickle模块提供了四个功能:dumps、dump、loads、load
import json
data = {'k1':123,'k2':'Hello'} # json.dumps 将数据通过特殊的形式转换为所有程序语言都认识的字符串
j_str = json.dumps(data)
print(type(j_str),j_str) # json.loads 将字典形式的字符串转换为字典格式
j_dict = json.loads(j_str)
print(type(j_dict),j_dict) # json.dump 将数据通过特殊的形式转换为所有程序都认识的字符串,并写入文件
with open('result.json','w') as f:
json.dump(data,f) ####################################################################### import pickle
# pickle.dumps 将数据通过特殊的形式转换为只有python语言认识的字符串
p_str = pickle.dumps(data)
print(type(p_str),p_str) # pickle.dump 将数据通过特殊的形式转换为只有Python语言认识的字符串,并写入文件
with open('result.pk','w') as f:
pickle.dump(data,f)