北京个人制作网站有哪些内容网站管理工作是具体应该怎么做
web/
2025/10/8 3:55:27/
文章来源:
北京个人制作网站有哪些内容,网站管理工作是具体应该怎么做,工业设计网站有那些,钓鱼网站开发教程文章目录 普通装饰器decorator0. 万能公式#xff0c;非常重要1. 便于入门的decorator原理2. 理解函数3. 装饰器的作用:4. 装饰器的语法糖5. 装饰器顺序6. 极简的装饰器7. 装饰器的参数无参 函数装饰器有参 函数装饰器 类装饰器class decorator0. 万能公式#xff0c;非常重要… 文章目录 普通装饰器decorator0. 万能公式非常重要1. 便于入门的decorator原理2. 理解函数3. 装饰器的作用:4. 装饰器的语法糖5. 装饰器顺序6. 极简的装饰器7. 装饰器的参数无参 函数装饰器有参 函数装饰器 类装饰器class decorator0. 万能公式非常重要1. 可以成为装饰器的类无参 装饰器类有参 装饰器类 2. 可以装饰类的装饰器无参 可以装饰类的装饰器有参 可以装饰类的装饰器 如何把装饰器放到类里面warps详解装饰器中wraps(view_func)的作用不加wraps的例子加wraps后 普通装饰器decorator
0. 万能公式非常重要
在一个函数上添加装饰器就等价于这个函数名装饰器调用这个函数。
1. 便于入门的decorator原理
decorator是一个输入和输出都是函数的函数。请暂时这样理解以便入门。
2. 理解函数 在Python中定义一个函数其实就是新建了一个变量这个变量里保存了函数对象 def double(x):return x * 2
print(double) # function double at 0x10688a710函数对象有一个特点就是callable在Python中callable这个东西后面是可以跟一对小括号的从而调用它。 print(double(2)) # 4实际上在Python的语法层面任何的东西都可以调用只不过如果这个东西不是callable的话会在Python的runtime时会出错。 import dis
# 例如我们让1进行调用那么也是可以通过编译的只不过会给出syntax warning但是它的字节码表示一样可一LOAD完这个1之后再尝试call它
dis.dis(1())当我们理解了函数在Python中只是一个普通的对象后我们就可以理解函数可以被当作参数传进其他的函数里。 def double(x):return x * 2
def triple(x):return x * 3
def calc_number(func, x):print(func(x))calc_number(double, 3) # 6
calc_number(triple, 3) # 9函数不单单可以作为变量传进其他函数函数本身也可以成为一个返回值。 def get_multiple_func(n):def multiple(x):# multiple函数return什么值是由get_multiple_func的n决定的return n * xreturn multipledouble get_multiple_func(2)
triple get_multiple_func(3)print(double(2)) # 4
print(triple(3)) # 9此时我们就理解了“函数的返回值可以是一个函数”那么decorator相对来说就容易理解了。
3. 装饰器的作用:
在不改变原有功能代码的基础上添加额外的功能如用户验证等。
4. 装饰器的语法糖 符号是装饰器的语法糖。它放在一个函数开始定义的地方头顶和这个函数绑定在一起。
在我们调用这个函数的时候会先将这个函数做为参数传入它头顶即装饰器里。
5. 装饰器顺序
一个函数可以同时定义多个装饰器比如
a
b
c
def f ():pass它的执行顺序是从里到外最先调用最里层的装饰器最后调用最外层的装饰器它等效于
f a(b(c(f)))
6. 极简的装饰器
decorator本身就是一个callable它没有特殊的地方那么我们可以暂时理解为decorator本身就是一个函数后面跟的这个名字就是函数名我们定义了一个函数叫做dec
def dec(f):passdec
def double(x):return x * 2# 第4行到第6行的代码完全等价于double dec(double)decorator是一个输入和输出都是函数的函数。当然了输入一定是函数但是输出不一定是下面是极端的例子没有人会这样写代码。 def dec(f):return 1dec
def double(x):return x * 2print(double) # 1注意我们print的是double本身而不是对double进行调用。 double dec(double)
# dec(double)返回的是1
# 1被赋值给double变量
# print(double)就是1上面的例子只是极端情况我们还是要认为decorator就是输入和输出都是函数的函数。
7. 装饰器的参数
无参 函数装饰器
import timedef timeit(f):def wrapper(x):start time.time()ret f(x)print(time.time() - start)return retreturn wrapper# 等价于my_func timeit(my_func)
timeit
def my_func(x):time.sleep(x)my_func(1) # 等价于timeit(my_func)(1)# 这是通用写法用可变参数来适配各种被包装的函数
import timedef timer(func):def wrapper(*args, **kwargs):start_time time.time()func(*args, **kwargs) # 这是函数真正执行的地方stop_time time.time()cost_time stop_time - start_timeprint(花费时间{}秒.format(cost_time))return wrappertimer
def want_sleep(sleep_time):time.sleep(sleep_time)want_sleep(3)有参 函数装饰器
import timedef timeit(iteration): # iteration是运行多少次。返回的是inner函数相当于是之前没有参数的decoratordef inner(f):def wrapper(*args, **kwargs): # 里面运行了iteration次的函数fstart time.time()for _ in range(iteration):ret f(*args, **kwargs)print(time.time() - start)return retreturn wrapperreturn inner# 等价于double timeit(10)(double)
# 即
# inner timeit(10)
# double inner(double)
timeit(10)
def double(x):return x * 2double(1) # 等价于timeit(10)(double)(1)import timedef timer(sleep_time):def outter(func):def wrapper(*args, **kwargs):start_time time.strftime(%Y-%m-%d %H:%M:%S, time.localtime())print(start_time: str(start_time))result func(*args, **kwargs) # 这是函数真正执行的地方time.sleep(sleep_time)stop_time time.strftime(%Y-%m-%d %H:%M:%S, time.localtime())print(stop_time: str(stop_time))return resultreturn wrapperreturn outtertimer(sleep_time3)
def sleep_status(name):print(name is sleeping...)return name is wake...r sleep_status(zhangsan)
print(r)# 输出
# start_time:2022-09-23 23:53:16
# zhangsan is sleeping...
# stop_time:2022-09-23 23:53:19
# zhangsan is wake...
类装饰器class decorator 类装饰器class decorator这个名称实际上是有一定的歧义的。 在有些地方是指“可以成为装饰器的类”。这说的是装饰器本身。用中文的语境来描述应该是“装饰器类”而不是“类装饰器”。在有些地方是指“可以装饰类的装饰器”。这说的是装饰器要装饰的对象。用中文的语境来描述应该是“类 的 装饰器”。 装饰器本身既可以是函数还可以是类。而装饰的对象同样既可以是函数也可以是类。
0. 万能公式非常重要
万能公式非常重要只要你把它转换成等价形式decorator就比较好读而转换成等价形式之后我们就知道代码如何写它的输入应该是什么它的输出应该是什么。
1. 可以成为装饰器的类
装饰器类 的实现必须实现__call__和 __init__两个内置函数。
当使用 形式将装饰器附加到函数上时就会调用此方法。
无参 装饰器类
__init__接收被装饰函数 __call__实现装饰逻辑。
import timeclass Timer:def __init__(self, func):# 函数add被传入__init__方法里,所以add被作为参数保存在了self.func里self.func func# __call__魔术方法的意思让所有这个类的实例都变成一个callable# 简单理解就是这个类的实例都可以当作函数用你都可以调用它 即在后面用小括号def __call__(self, *args, **kwargs):start time.time()ret self.func(*args, **kwargs) # self.func就是原来的add函数print(fTime: {time.time() - start})return ret# 把Timer这个类本身当作一个装饰器装饰在了add函数上
# 等价于add Timer(add)
# Timer装饰器相当于把名为add的变量其值从一个函数变成了一个Timer类的实例
Timer
def add(a, b):return a bprint(type(add)) # class __main__.Timer
print(add(2, 3)) # 实际上是调用了Timer类的实例add的__call__方法此时2和3参数就被传到__call__的参数里。Time: 5.7220458984375e-06
5通过上面的例子我们可以看到和前面讲的函数decorator其实没有很大的区别。原来的函数装饰器是“函数调用一个函数返回一个函数”更确切的说是“函数调用一个函数返回一个callable”。现在是类调用一个函数返回一个实例这个实例依然是callable所以我们只需要把decorator在脑海中进行等价转换就非常容易理解了。
class Logger(object):def __init__(self, func):self.func funcdef __call__(self, *args, **kwargs):print([INFO]: the function {func}() is running... \.format(funcself.func.__name__))return self.func(*args, **kwargs)Logger
def say(something):print(say {}!.format(something))say(hello)# 输出
# [INFO]: the function say() is running...
# say hello!有参 装饰器类
带参数和不带参数的类装饰器有很大的不同。
__init__ 不再接收被装饰函数而是接收传入参数。 __call__接收被装饰函数实现装饰逻辑。
import timeclass Timer:def __init__(self, prefix):self.prefix prefixdef __call__(self, func):def wrapper(*args, **kwargs):start time.time()ret func(*args, **kwargs)print(f{self.prefix} {time.time() - start})return retreturn wrapper# 等价于add Timer(prefixcurr_time: )(add)
# 那么此时add就是wrapper了
Timer(prefixcurr_time: )
def add(a, b):return a bprint(add(2, 3))curr_time: 1.9073486328125e-06
5class Logger(object):def __init__(self, levelINFO):self.level leveldef __call__(self, func): # 接受函数def wrapper(*args, **kwargs):print([{level}]: the function {func}() is running... \.format(levelself.level, funcfunc.__name__))func(*args, **kwargs)return wrapper # 返回函数Logger(levelWARNING)
def say(something):print(say {}!.format(something))say(hello)# 输出
# [WARNING]: the function say() is running...
# say hello!2. 可以装饰类的装饰器
无参 可以装饰类的装饰器 在Python里自定义的class类的实例如果我们直接打印实例是不会打印出任何有价值的信息的只会打印出这个实例对象是一个什么类。 class MyObject:def __init__(self, a, b):self.a aself.b bo MyObject(1, 2)
print(o) # __main__.MyObject object at 0x109447fd0那么我们可以通过重载其__str__方法来改变print出来的结果 class MyObject:def __init__(self, a, b):self.a aself.b bdef __str__(self):return str(self.__dict__)o MyObject(1, 2)
print(o) # {a: 1, b: 2}当然如果每一个对象都改的话也挺麻烦的。此时“装饰类的装饰器”就派上了用场。
# add_str函数是一个参数是class返回值也是class 的函数
def add_str(cls):这个decorator本质就是重载了一下这个class的__str__方法def __str__(self): # 自定义了一个名为__str__的函数当然这个函数也可以叫任意名但是为了规范我们不要随意起名return str(self.__dict__)# 将接收的class对象的__str__方法给替代成了add_str自己定义的__str__函数cls.__str__ __str__return cls# 等价于MyObject add_str(MyObject)
add_str
class MyObject:def __init__(self, a, b):self.a aself.b bo MyObject(1, 2)
print(o) # {a: 1, b: 2}所以给类写装饰器也没有什么难的就是输入一个类返回一个类然后在这个装饰器里面对传入的类动动手脚完成我们需要的事情。
有参 可以装饰类的装饰器
# add_str函数是一个参数为greet的函数返回一个参数为cls的inner函数
def add_str(greet):def inner(cls):print(greet)def say(self):return str(self.__dict__)# 将inner接收的class对象的__str__方法给替代成了inner自己定义的say函数cls.__str__ sayreturn clsreturn inner# 等价于MyObject add_str(hello)(MyObject)
# 也就是
# inner add_str(hello)
# MyObject inner(MyObject)
add_str(hello)
class MyObject:def __init__(self, a, b):self.a aself.b b
什么都不写也会打印出hello,因为inner(MyObject)的时候会执行inner函数打印出helloo MyObject(1, 2)
print(o) # {a: 1, b: 2}
如何把装饰器放到类里面
# 简单的装饰器作用就是在函数运行之前打印start在函数运行结束打印end
def log_function(func):def wrapper(*args, **kwargs):print(function start!)print(fargs: {args})ret func(*args, **kwargs)print(ffunction end!)return retreturn wrapperlog_function
def fib(n):斐波那契函数if n 1:return 0return fib(n - 1) fib(n - 2)fib(3)function start!
args: (3,)
function start!
args: (2,)
function start!
args: (1,)
function end!
function start!
args: (0,)
function end!
function end!
function start!
args: (1,)
function end!
function end!可以看到装饰器虽然用处不大但是确实是工作了的勉强算是能帮助我们理解递归。
我们一般写装饰器的时候通常是把装饰器的代码定义在py文件的最外层然而并不是所有的函数都适合放到全局空间里的。
如果我们就是想把装饰器放到类里面或者说我们有若干个功能相似的装饰器就是希望将其归类到同一个类里面下面就让我们介绍一下如何把装饰器放到类里。
# 这是我们最容易想到的一种实现
class Decorators:def log_function(self, func):def wrapper(*args, **kwargs):print(function start!)print(fargs: {args})ret func(*args, **kwargs)print(ffunction end!)return retreturn wrapperd Decorators() # __main__.Decorators object at 0x10dab7fd0d.log_function
def fib(n):if n 1:return 0return fib(n - 1) fib(n - 2)fib(3)本质上就是把一个函数变成类的方法所以这里我们仅仅是把函数加上self argument然后扔到类里面。
当我们使用这种方式的时候你一定要意识到一个类的方法必须由这个类的实例对象所调用。所以当我们想使用这个装饰器的时候我们首先要建立一个Decorators这个class的object也就是12行代码创建实例对象。
后面我们就可以用d.log_function来装饰这个斐波那契函数。这种方式得到的结果和之前是一样的。
但是这样用是有问题的
非常明显的我们为了使用这个装饰器迫不得已的要建立一个新的对象。其次我们这个装饰器里面莫名其妙的出现了self argument然后我们这个装饰器压根就没有用到它。
为了解决问题 我们可以把类里面定义的方法变成class method也就是把这个方法从对象调用变成一个类就可以调用的方法我们使用classmethod装饰器那么在12行的时候就不需要新建一个对象才能使用这个装饰器了。我们可以直接在14行Decorators.log_function来调用这个装饰器。 但是这个class method的第一个argument依然是cls我们依旧没有用到这个变量。 如果我们这个装饰器会根据这个class或者这个object的变化而变化从而改变装饰器自己的功能那么在这种情况下我们当然是可以这样写的。 但是如果我们只是单纯的想把装饰器放到类里面做一个封装我们就可以考虑把class method变成一个static method。 所以当你仅仅需要把一个装饰器封装到一个类里面的时候下面的代码就是一个非常不错的方式: # 仅仅需要把一个装饰器封装到一个类里面的时候这里的代码就是一个非常不错的实现
class Decorators:staticmethoddef log_function(func):def wrapper(*args, **kwargs):print(function start!)print(fargs: {args})ret func(*args, **kwargs)print(ffunction end!)return retreturn wrapperDecorators.log_function
def fib(n):if n 1:return 0return fib(n - 1) fib(n - 2)fib(3)然而。。。还没完。如果我想用我类里面定义的属于这个类的装饰器去装饰我这个类里面的其他方法时我应该怎么做如下所示怎么做 # 把fib函数移到了Decorators类里
class Decorators:staticmethoddef log_function(func):def wrapper(*args, **kwargs):print(function start!)print(fargs: {args})ret func(*args, **kwargs)print(ffunction end!)return retreturn wrapperlog_functiondef fib(self, n): # 在Python3.9中会报错TypeError: staticmethod object is not callable而在Python3.10中不会if n 1:return 0return self.fib(n - 1) self.fib(n - 2)d Decorators()
d.fib(3)让我说的再明确点我有一个类类里面有很多的方法都希望用装饰器给它们装饰一下但是我这个装饰器只会在这个类里面用我又想把这个装饰器给封装到这个类里来。
正确的用法是直接把log_function这个函数原封不动的放到这个Decorators class里
# 正确的做法把fib函数原封不动的移到Decorators类里
class Decorators:def log_function(func):def wrapper(*args, **kwargs):print(function start!)print(fargs: {args})ret func(*args, **kwargs)print(ffunction end!)return retreturn wrapperlog_functiondef fib(self, n):if n 1:return 0return self.fib(n - 1) self.fib(n - 2)d Decorators()
d.fib(3)相信你已经懵了。。你“我从来没在Python里见到过长成这样的类的方法”。但是当你运行的时候你会神奇的发现这是可以运行的哦
原因很简单“当我们在定义一个新的class的时候本质上是把class里面的code block在另外一个命名空间里运行了一下”。只不过我们非常不习惯看到这种没有self的method但实际上这个log_function也不是一个method你可以认为它是在我们定义这个类的时候里面的一个辅助函数或者是一个辅助的decorator。
意思就是说当我们尝试在这个类的定义之外 使用d.log_function的时候会报错。因为Python会尝试把这个log_funciton当成一个正常的对象方法去解读。
# 在类的定义之外使用d.log_function时报错
class Decorators:def log_function(func):def wrapper(*args, **kwargs):print(function start!)print(fargs: {args})ret func(*args, **kwargs)print(ffunction end!)return retreturn wrapperlog_functiondef fib(self, n):if n 1:return 0return self.fib(n - 1) self.fib(n - 2)d Decorators()
d.log_function
def f(): # TypeError: Decorators.log_function() takes 1 positional argument but 2 were givenpass
f()当然我们我们直接使用Decorators.log_function则是可以工作的因为Decorators.log_function就是一个正常的函数。
# 直接使用 Decorators.log_function 则是可以正常运行
class Decorators:def log_function(func):def wrapper(*args, **kwargs):print(function start!)print(fargs: {args})ret func(*args, **kwargs)print(ffunction end!)return retreturn wrapperlog_functiondef fib(self, n):if n 1:return 0return self.fib(n - 1) self.fib(n - 2)Decorators.log_function
def f():pass
f()最后到底有没有一种方式可以把我这个装饰器封装到类里在这个类里可以正常使用而在这个类之外无论是通过实例对象调用、还是通过类调用都可以正常使用呢
答案是有的你只需要在类定义的最后加上log_function staticmethod(log_function)这行代码即可
# 最终、最正确的实现方式
class Decorators:def log_function(func):def wrapper(*args, **kwargs):print(function start!)print(fargs: {args})ret func(*args, **kwargs)print(ffunction end!)return retreturn wrapperlog_functiondef fib(self, n):if n 1:return 0return self.fib(n - 1) self.fib(n - 2)# 加上下面的代码这行代码的本质和在log_function的定义上加一个staticmethod这个装饰器是一样的这两种方式是等价的# 注意区别下面的方式在Python3.9中就不会报TypeError的错误了# 只不过用staticmethod装饰器的语法只能在定义log_function的同时去装饰它而当我们使用staticmethod装饰器的等价语法的时候我们可以先定义log_function成为一个正常的函数然后在class的definition里面把log_function当成一个正常的装饰器去使用最后再通过log_functionstaticmethod(log_function)把这个log_function变成一个static methodlog_function staticmethod(log_function)Decorators.log_function
def f1():pass
f1()d Decorators()
d.log_function
def f2():pass
f2()通过上面的代码现在无论我们是使用Decorators.log_function还是使用它的对象d.log_function都能正确的执行这个装饰器的功能了。
warps详解 __name__用来显示函数的名称__doc__用来显示文档字符串也就是(“”“文档字符串”“”)这里面的内容。 文档字符串用于解释文档程序帮助程序文档更加简单易懂。 可以在函数体的第一行使用一对三个单引号 ‘’’ 或者一对三个双引号 “” 来定义文档字符串。 使用__doc__注意双下划线调用函数中的文档字符串属性。 装饰器中wraps(view_func)的作用
不改变使用装饰器原有函数的结构(如name, doc)。
装饰器decorator在实现的时候被装饰后的函数其实已经是另外一个函数了函数名等函数属性会发生改变。
为了不影响Python的functools包中提供了一个叫**wraps**的装饰器来消除这样的副作用。
写一个装饰器的时候最好在实现之前加上functools中的wraps它能保留原有函数的名称和文档字符串(DocStrings)。
不加wraps的例子
def my_decorator(func):def wrapper(*args, **kwargs):decoratorprint(Decorated function...)return func(*args, **kwargs)return wrappermy_decorator
def t():TestWordprint(Test function)t()
# 输出
# Decorated function...
# Test functionprint(t.__name__) # wrapper
print(t.__doc__) # decorator执行的整个过程在调用t()函数时首先会调用装饰器(将t作为参数传入到装饰器中)执wrapper函数再执行t函数。
但我们可以看到t函数的名字__name__为wrapper__doc__为decorator已经不是原来的test函数了。
加wraps后
from functools import wrapsdef my_decorator(func):wraps(func)def wrapper(*args, **kwargs):decoratorprint(Decorated function...)return func(*args, **kwargs)return wrappermy_decorator
def t():TestWordprint(Test function)t()
# 输出
# Decorated function...
# Test functionprint(t.__name__) # t
print(t.__doc__) # TestWord
会发现test函数的__name__和__doc__还是原来的即函数名称和属性没有变换。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/88857.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!