知识点

背景

装饰器是在原有函数不做修改的基础上对原函数添加新的功能

实现原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def set_func(func):
print("装饰器装饰的时候就会执行")
def call_func(*args, **kwargs):
print("被装饰程序执行时会执行的语句")
return func(*args, **kwargs)
return call_func

# 函数本身也是一个对象,直接写函数名相当于传递了该函数的引用
# 相当于执行fun = set_func(fun),该句执行完以后将fun的引用传递给call_func,然后将变量名fun的引用指向call_func
@set_func # 运行到此处时,就已经开始装饰了
def fun(num):
print("函数本身的功能")
return num

fun(1)
# 程序运行的时候,执行到@set_func,此时开始给fun(num)函数添加了装饰,且将fun函数的引用方式传递给func,装饰完以后就将原fun对象的变量名的引用指向了call_func,此时程序继续运行,执行到fun(1)时就相当云运行call_func(1),然后依次执行print("被装饰..."),运行完以后执行到return func(...),由于此时fun的引用方式是原fun的引用方式,故开始执行真实的fun(num)函数,然后将1返回到call_func函数中,call_func函数又将值返回到主程序中。

分类

  • 多装饰装饰

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    def set_func1(func):
    print("装饰器1")
    def call_func1(*args, **kwargs):
    print("装饰器1执行的程序")
    return func(*args, **kwargs)
    return call_func1

    def set_func2(func):
    print("装饰器2")
    def call_func2(*args, **kwargs):
    print("装饰器2执行的程序")
    return func(*args, **kwargs)
    return call_func2

    @set_func1 # 运行到此处时,发现下一代码行不是函数,因此暂停装饰
    @set_func2 # 运行到此处时,检测下一代码行是函数,因此开始装饰
    def fun(num):
    print("函数本身的功能")
    return num

    fun(1)

    # 程序运行到@set_func1时,发现下一代码行不是函数,因此暂停装饰
    # 运行到@set_func2时,发现下一代码行是函数,因此开始装饰,此时输出【装饰器2】,fun变量名的引用修改为call_func2
    # 然后@set_fun1检测底部装饰器装饰完以后,开始进行装饰,此时输出【装饰器1】,fun的引用方式(call_fun2)指向了call_fun1
    # 此时执行fun(1),先从装饰完的代码的最外层开始执行,因此先输出【装饰器1执行的程序】,然后输出【装饰器2执行的程序】
  • 类装饰器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Test(object):

    def __init__(self, func):
    self.func = func

    def __call__(self, *args, **kwargs):
    print("这是直接调用【类名(参数)】时执行的语句")
    return self.func(*args, **kwargs)

    @Test # 相当于 fun = Test(fun)
    def fun(num):
    print("函数本身的功能")
    return num

    fun(1)
    # 程序运行到@Test时,会先装饰,即执行Test(fun),然后将fun的引用方式当作一个实例属性赋值给self.func,
    # 由于直接执行Test(fun)会生成一个实例对象,此时再执行执行【对象(参数)】会直接调用__call__方法,此时就会输出【这是....】
    # 然后执行self.func(*args, **kwargs),由于self.func指向原来的fun,故其会返回1到__call__方法中,call又会将该值返回到主程序中
  • 带参数装饰器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    # 定义一个全局变量
    page_info = dict()

    def func(num):
    def func_x(func):
    # 如果不需要去修改全局变量的引用方式,则不需要重新声明
    page_info[num] = func
    def call_funx(*args, **kwargs):
    # 下方的判断语句是可以对修饰器传递的内容进行修饰的
    if num == "1.py":
    print(1)
    elif num == '2.py':
    print(2)
    else:
    print(3)
    return func(*args, **kwargs)
    return call_funx
    return func_x

    # 带参数的装饰器,则是先执行func函数,然后用返回的结果去装饰其下方的函数
    @func("1.py")
    def page():
    print("执行到内部函数了")
    return "ok"

    page()

    # 程序由上向下执行,当执行到【@func("1.py")】时,开始先执行装饰器【func函数】,将【1.py】变量引用方式传递给【num】,然后返回装饰器【内部func_x对象】。
    # 然后funx_x对象开始对【page()】函数开始装饰,先将【page】方法的引用传递给形参【func】,然后将装饰器传递的参数【1.py】与形参【func】构建成键值对填充到字典中,之后将【page】变量名的引用方式修改为【call_funx】
    # 之后开始先执行条件判断语句,然后返回【原page】引用方式执行的函数的引用

其它

  • 判断键是否存在某一个字典中

    1
    2
    3
    4
    5
    dict_name = {"name":"wuxiang"}
    key = "name"
    # 通过in语句判断某个键是否存在在字典中
    if key in dict_name:
    print(key)
  • 静态、动态、伪静态

    • 静态

      案例:就是写好的静态页面,通过服务器进行访问

      优点:网站打开速度块,不用运行计算,网址结构友好,利于记忆

      缺点:对中大型网站而言,页面多,不利于管理。相比较其它网站实现方式,其占用磁盘空间较大,读取频率高,因此理论上说对硬盘有损

      总结:静态URL对SEO非常友好,因为打开速度快

    • 动态

      案例:用户请求的页面不是一个静态的页面,而是根据功能需要动态生成的页面

      优点:修改页面很方便,因为是逻辑地址,所以占用硬盘空间比纯静态网站小

      缺点:打开速度稍慢,但目前有服务器缓存技术,可提交加载速度。其次URL结构复杂,不利于记忆

      总结:相比较而言,动态网站对SEO较差,但目前很多搜索引擎已经可以很好的理解动态网站了,所以基本可以忽略

    • 伪静态

      案例:利用动态技术实现的伪装为静态的网址

      优点:URL比较友好,而且便于记忆

      缺点:设置较为麻烦,服务器需要支持重写规则。而且伪静态并没有提高网站的访问速度,反而需要服务器提供额外的运算解释,增加了服务器的负担,速度反而变慢。其次由于静态和伪动态的同时存在还可能导致两种URL都被搜索引擎收录,单此时可通过robots文件禁止掉静态网站的收录

      总结:伪静态更动态一样,对seo没什么影响