微信公众号: 点击蓝色字体小白图像与视觉进行关注
关于技术、关注
yysilence00
。有问题或建议,请公众号留言
下面主要讲装饰器
- 整理知识,学习笔记
- 发布日记,杂文,所见所想
#1.Decorator是什么
Python的修饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西。在认识装饰器之前,我们先来点感性认识,看一个Python修饰器的Hello World的代码。
def hello(fn):
def wrapper():
print("hello, %s" % fn.__name__)
fn()
print("goodby, %s" % fn.__name__)
return wrapper
# @hello
# def foo(): #foo = hello(foo)
# print("i am foo")
def foo():
print("i am foo")
foo = hello(foo)#hello(foo)返回了wrapper()函数,所以,foo其实变成了wrapper的一个变量
foo() #foo()执行其实变成了wrapper()
输出:
hello, foo
i am foo
goodby, foo
你可以看到如下的东西:
- 函数foo前面有个@hello的“注解”,hello就是我们前面定义的函数hello;
- 在hello函数中,其需要一个fn的参数(这就用来做回调的函数);
- hello函数中返回了一个inner函数wrapper,这个wrapper函数回调了-传进来的fn,并在回调前后加了两条语句。
#2.Decorator 的本质
对于Python的这个@decorator,当你在用某个@decorator来修饰某个函数func时,如下所示:
@decorator
def func():
pass
其解释器会解释成下面这样的语句:
func = decorator(func)
了然,这不就是把一个函数当参数传到另一个函数中,然后再回调吗?是的,但是,我们需要注意,那里还有一个赋值语句,把decorator这个函数的返回值赋值回了原来的func。 根据《函数式编程》中的first class functions中的定义的,你可以把函数当成变量来使用,所以,decorator必需得返回了一个函数出来给func,这就是所谓的higher order function 高阶函数,不然,后面当func()调用的时候就会出错。 就我们上面那个hello.py里的例子来说,
@hello
def foo():
print "i am foo"
其解释器会解释成下面这样的语句:
foo = hello(foo)
再回到我们hello.py的那个例子,我们可以看到,hello(foo)返回了wrapper()函数,所以,foo其实变成了wrapper的一个变量,而后面的foo()执行其实变成了wrapper()。知道这点本质,当你看到有多个decorator或是带参数的decorator,你也就不会害怕了。
#3.不带参数的装饰器
"""
python中的装饰器分为两类:函数装饰器和类装饰器。
"""
# 不带参数的装饰器。即@decorator
"""
@decorator
"""
# a decorator receives the method it's wrapping as a variable 'f'
def increment(f):
# we use arbitrary args and keywords to
# ensure we grab all the input arguments.
def wrapped_f(*args, **kw):
# note we call f against the variables passed into the wrapper,
# and cast the result to an int and increment .
return int(f(*args, **kw)) + 1
return wrapped_f # the wrapped function gets returned.
@increment
def plus(a, b):
return a + b
#等价于
#def plus(a, b):
# return a + b
#plus = increment(plus)
result = plus(4, 6)
if result == 11:
print('good')
输出:
good
#4.带参数的装饰器
#带参数的decrorator
def makeHtmlTag(tag, *args, **kwds):
def real_decorator(fn):
css_class = " class='{0}'".format(kwds["css_class"]) if "css_class" in kwds else ""
def wrapped(*args, **kwds):
return "<" + tag + css_class + ">" + fn(*args, **kwds) + "</" + tag + ">"
return wrapped
return real_decorator
@makeHtmlTag(tag="b", css_class="bold_css")
@makeHtmlTag(tag="i", css_class="italic_css")
@makeHtmlTag(tag="c", css_class="usa_css")
def hello(): #hello = makeHtmlTag(tag="b", css_class="bold_css")((makeHtmlTag(tag="i", css_class="italic_css")(hello))
return "style"
print(hello())
输出:
<b class='bold_css'><i class='italic_css'><c class='usa_css'>style</c></i></b>
上面可以写的更加复杂来表示前端语言的一种显示。
#5.一个Logging的Decorator
from time import sleep
from functools import wraps
import logging
logging.basicConfig()
log = logging.getLogger("retry")
def retry(f):
@wraps(f)
def wrapper_function(*args, **kwargs):
#wrapper_function=wraps(wrapper_function)
MAX_ATTEMPTS = 5
for attempt in range(1, MAX_ATTEMPTS + 1):
try:
return f(*args, **kwargs)
except:
log.exception("Attempt %s/%s failed : %s",
attempt,
MAX_ATTEMPTS,
(args, kwargs))
sleep(10 * attempt)
log.critical("All %s attempts failed : %s",
MAX_ATTEMPTS,
(args, kwargs))
return wrapper_function
counter = 0
@retry
def save_to_database(arg):
# save_to_database=retry(save_to_database)
print("Write to a database or make a network call or etc.")
print("This will be automatically retried if exception is thrown.")
global counter
counter += 1
# 这将在第一次调用中抛出异常
# 并且在第二次调用中工作正常(即重试)
if counter < 2:
raise ValueError(arg)
if __name__ == '__main__':
save_to_database("Some bad value")
输出:
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.
ERROR:retry:Attempt 1/5 failed : (('Some bad value',), {})
Traceback (most recent call last):
File "D:/PycharmProjects/more_decorator.py", line 14, in wrapper_function
return f(*args, **kwargs)
File "D:/PycharmProjects/more_decorator.py", line 38, in save_to_database
raise ValueError(arg)
ValueError: Some bad value
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.
上述是怎么工作的:
- 经过一次raise抛出异常被try捕获后,第二次就正常工作了。因为第二次没有抛出异常
#6.一个MySQL的Decorator
import umysql
from functools import wraps
class Configuraion:
def __init__(self, env):
if env == "Prod":
self.host = "coolshell.cn"
self.port = 3306
self.db = "coolshell"
self.user = "coolshell"
self.passwd = "fuckgfw"
elif env == "Test":
self.host = 'localhost'
self.port = 3300
self.user = 'coolshell'
self.db = 'coolshell'
self.passwd = 'fuckgfw'
def mysql(sql):
_conf = Configuraion(env="Prod")
def on_sql_error(err):
print err
sys.exit(-1)
def handle_sql_result(rs):
if rs.rows > 0:
fieldnames = [f[0] for f in rs.fields]
return [dict(zip(fieldnames, r)) for r in rs.rows]
else:
return []
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
mysqlconn = umysql.Connection()
mysqlconn.settimeout(5)
mysqlconn.connect(_conf.host, _conf.port, _conf.user, \
_conf.passwd, _conf.db, True, 'utf8')
try:
rs = mysqlconn.query(sql, {})
except umysql.Error as e:
on_sql_error(e)
data = handle_sql_result(rs)
kwargs["data"] = data
result = fn(*args, **kwargs)
mysqlconn.close()
return result
return wrapper
return decorator
@mysql(sql = "select * from coolshell" )
def get_coolshell(data):
... ...
... ..
更多请扫码关注: