小甲鱼 发表于 2014-8-19 17:04:42

Python 函数修饰符(装饰器)的使用

转自:关于Python修饰符学习

注意:由于很多童鞋反馈原文一点都不好理解,小甲鱼负有连带责任……所以,我又重新把内容整理了一遍,增加了一些例子,希望能帮助到大家。

1.修饰符的来源

借用一个博客上的一段叙述:修饰符是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。

修饰符是解决这类问题的绝佳设计,有了修饰符,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。

概括的讲,修饰符的作用就是为已经存在的对象添加额外的功能。

如下:

import time

def timeslong(func):
    start = time.time()
    print("It's time starting ! ")
    func()
    print("It's time ending ! ")
    end = time.time()
    return "It's used : %s." % (end - start)

def myfunc():
    print("Hello FishC.")

t = timeslong(myfunc)
print(t)
实现结果:

It's time starting !
Hello FishC.
It's time ending !
It's used : 0.02497720718383789.
上面的程序中,定义了一个函数(timeslong()),对一个对象(代码中是 myfunc())的运行时间进行计算。

通常情况下,如果我们需要计算另外一个函数的运算时间,那么我们就需要修改 timeslong() 函数在调用时候的实参。

比如我需要统计 myfunc2 这个函数的运行时间,就需要调用 timeslong(myfunc2) 酱紫。

那么为了优化这种操作,Python 便提出了修饰符这个概念。

我们看下它是怎么实现的:

import time

def timeslong(func):
    def call():
      start = time.time()
      print("It's time starting ! ")
      func()
      print("It's time ending ! ")
      end = time.time()
      return "It's used : %s." % (end - start)
    return call

@timeslong
def myfunc():
    print("Hello FishC.")

print(myfunc())
实现的结果是一样的:

It's time starting !
Hello FishC.
It's time ending !
It's used : 0.022337913513183594.
但是大家有没有发现,这一次我们不需要再去调用 timeslong() 函数了。

如果我有多个函数需要统计,那么使用起修饰符来就更优雅了:

import time

def timeslong(func):
    def call():
      start = time.time()
      print("It's time starting ! ")
      func()
      print("It's time ending ! ")
      end = time.time()
      return "It's used : %s." % (end - start)
    return call

@timeslong
def myfuna():
    print("Hello World.")

@timeslong
def myfunb():
    print("Hello Python.")

@timeslong
def myfunc():
    print("Hello FishC.")

print(myfuna())
print("========================================")
print(myfunb())
print("========================================")
print(myfunc())
实现结果:

It's time starting !
Hello World.
It's time ending !
It's used : 0.023639202117919922.
========================================
It's time starting !
Hello Python.
It's time ending !
It's used : 0.01770305633544922.
========================================
It's time starting !
Hello FishC.
It's time ending !
It's used : 0.014674186706542969.
通过修饰符主要达到的目标是使得整个代码看起来更加美观,仅此而已。

另外,我们还可以进一步“优雅”,

那就是把它封装成类:

import time

class timeslong(object):
    def __init__(self, func):
      self.func = func

    def __call__(self):
      start = time.time()
      print("It's time starting ! ")
      self.func()
      print("It's time ending ! ")
      end = time.time()
      return "It's used : %s." % (end - start)

@timeslong
def myfuna():
    print("Hello World.")

@timeslong
def myfunb():
    print("Hello Python.")

@timeslong
def myfunc():
    print("Hello FishC.")

print(myfuna())
print("========================================")
print(myfunb())
print("========================================")
print(myfunc())
实现的结果是一样的:

It's time starting !
Hello World.
It's time ending !
It's used : 0.03470897674560547.
========================================
It's time starting !
Hello Python.
It's time ending !
It's used : 0.01266622543334961.
========================================
It's time starting !
Hello FishC.
It's time ending !
It's used : 0.014008522033691406.
其实呀,Python 也有内置的修饰符,它们分别是 staticmethod、classmethod 和 property,作用分别是把类中定义的实例方法变成静态方法、类方法和类属性。

简单地举个例子,如果我们将类这么写:

>>> class Hello(object):
...   def print_hello(cls):
...         print("Hello FishC")
那么直接使用 类名.函数() 的方式调用就会报错:

>>> Hello.print_hello()
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
    Hello.print_hello()
TypeError: print_hello() missing 1 required positional argument: 'cls'
但是,只需要在类里面的函数名上方加上一个 @classmethod 修饰符:

>>> class Hello(object):
...   @classmethod
...   def print_hello(cls):
...         print("Hello FishC")
那么问题就迎刃而解了:

>>> Hello.print_hello()
Hello FishC
因为给 @classmethod 修饰过后,print_hello() 就变成了类方法,可以直接通过 Hello.print_hello() 调用,而无需绑定实例对象了。

wei_Y 发表于 2014-9-9 10:21:10

没大看懂- -。。

猴子请来的救兵 发表于 2014-12-26 11:31:12

本帖最后由 猴子请来的救兵 于 2014-12-26 14:23 编辑

看不懂。。。

我是桃川人 发表于 2014-12-31 16:24:34

这方法太好了!

nirvana436 发表于 2015-1-1 01:18:19

那三个内置修饰符没看懂怎么用法,得研究一下了。

vito7 发表于 2015-1-20 17:06:47

没看懂,比如使用修饰符,为什么不需要将func()重新在timeslong中重新写一遍,没有体现出来,只说了不需要重写一遍,所以没看明白。。。:sad

vito7 发表于 2015-1-20 20:34:03

现在明白了一点点了,智商是硬伤

delphi369 发表于 2015-1-22 20:34:42

我写了一个简单的装饰器:
def fun1(fx):
    fx(2,5)
@fun1
def fun2(a,b):
    s=a+b
    print(s)

BinAn 发表于 2015-4-8 11:06:40

有点晕!!!!

awakeljw 发表于 2015-4-14 13:25:05

有点像matlab里的那个函数修饰符@

课件制作 发表于 2015-7-12 16:52:59

有点懂了,可还是迷糊

zwysw456 发表于 2015-7-15 19:42:17

def deco(func):
    print('有点蛋疼。。。')
    func()
    return func
@deco
def myfunc():
    print('真心的。。。')
myfunc()

墨白_找 发表于 2015-7-26 09:28:57

半懂~

tomopad 发表于 2015-9-14 15:01:04

迷迷糊糊的

翅膀团 发表于 2015-9-16 17:22:47

这是2.X版本的?3.X运行不了

无名似名 发表于 2015-11-7 10:19:25

这是给会的人看的吧

零距离2 发表于 2015-11-11 13:38:27

不太懂啊

斯盖丸 发表于 2015-11-11 15:23:31

楼主能再说清楚点吗?

江南孤雁 发表于 2015-11-19 14:52:53

看蒙了,还是不明白:sad

younasd 发表于 2015-12-6 12:21:37

悄悄哈
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: Python 函数修饰符(装饰器)的使用