Python装饰器

目录

 

一、装饰器概念

二、Python装饰器

三、Python内置装饰器 

property对象:

@property装饰器:


一、装饰器概念

  在设计模式中,有这样一条原则:开闭原则。开闭原则即对扩展开放,对修改关闭。我的理解装饰器实质上一种设计思想,它的作用简单的说就是在满足开闭原则的前提下,即在不修改原代码的前提下,为其增加新的功能。具体到Python上来说,装饰器就是一种可以不改变原函数实现为原函数添加新的功能的一种函数。简单的说,装饰器无关于特定的语言,它就是把一类功能封装起来,为其他函数提供调用接口,其他函数想增加功能时便可以直接调用该接口,实现代码复用,提高开发效率。

  例如:


def f1():  # f1目前只能打印出hello world
    print('hello world')

# 现在有了新的需求,要求打印出hello world的时候并输出该函数的名称


# 可以通过直接修改f1实现添加功能

# 在不修改f1代码的前提下,可以这样做

def decorator(func):
    def temp():
        print(func.__name__)
        return func()   # 函数名加(),表示返回的是一个函数的结果,不加括号表示的是对函数的调用,不执行。
    return temp


f1 = decorator(f1)

f1()

  这就是一个简单的装饰器,当然这种方式写的代码不那么易读,读着也不是很舒服,在Python引入了@语法糖之后,Python有更简洁的装饰器写法。

二、Python装饰器


def decorator(func):
    def temp():
        print(func.__name__)
        return func()   # 函数名加(),表示返回的是一个函数的结果,不加括号表示的是对函数的调用,不执行。
    return temp

# 使用装饰器


@decorator 
def f1():  # f1经过装饰器修饰,有了打印函数名功能
    print('hello world')



# 最方便的是,使用装饰器,不改变函数调用方式

f1()


上面的代码通过使用@语法糖,将decorator函数作为装饰器提供给f1函数,此时,f1有了新的打印函数名的功能。这就是装饰器的实现与示例。要注意的是,一个函数可被多个装饰器装饰,一个装饰器也可装饰多个函数。一种完整的可接受任意参数的装饰器如下:


def decorator(func):
    def temp(*args, **kw):  # *args可变参数 ,**kw关键字参数,被装饰函数使用任何参数
        print(func.__name__)
        return func(*args, **kw)   
    return temp


@decorator 
def f1():  
    print('hello world')

@decorator 
def f2(func1,func2):  
    print('hello world')

@decorator 
def f3(func1,func2,**kw):  
    print('hello world')

# 最方便的是,使用装饰器,不改变函数调用方式

f1()
f2(test_1,test_2)
f3(test1,test2,a=1,b=5,c='ads')


当然,目前写的装饰器都比较小,如果装饰器很复杂,那么使用装饰器的优势就更明显,比如Python内置的一些装饰器。

三、Python内置装饰器 

 提到Python内置的装饰器,学习过Python面向对象的应该会想到,Python在声明静态方法所使用的@staticmethod装饰器和声明类方法的@classmethod装饰器。这两个就不再说明了,这里我将详细说明一个更常用的@property 装饰器。

property对象:

首先,来看一下property类(Python中一切皆对象)的官方文档,可到官网查看,也可在Python控制台使用help(property)查看,

class property(fget=None, fset=None, fdel=None, doc=None)
    Return a property attribute.

通过文档对property类的简单描述,我们可以知道property的主要作用是以对象的方式创建一个属性并返回该属性值。为什么要用对象的方式来创建属性呢,直接声明按理说更方便,下面继续说:

  • fget是一个用于获取属性值的函数,
  • fset是设置属性值的函数,类似java的get和set方法。
  • fdel是删除属性值的方法,
  • doc则是创建属性的文档字符串,

官方文档提供的一个典型实例:

class C:
    def __init__(self):
        self._x = None  # _x表明不建议外部调用,即变量私有,但是调用也不会报错

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")

通过上面的了解,我们可以试着对上面的示例进行解释,C类定义了一个构造方法,三个实例方法(__init__也是实例方法),在构造方法中设置了属性x值为none,并且在类C的最后,创建了一个property对象进行赋值,那么经过

x = property(getx, setx, delx, "I'm the 'x' property.")

这一操作,x变成了什么呢?首先可以肯定的是,x也变成了property对象,因为property对象传入了类C定义的getx、setx、delx方法,现在x就变成了通过getx、setx、delx方法修改赋值的对象,这就成功实现了利用property对象对x进行托管,因为之后对x的属性值操作是通过property对象来实现的。如果你还不是很明白,我们来测试一下:

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    # x = property(getx, setx, delx, "I'm the 'x' property.")
    x = None
    
# 当我们不用property对象托管x的时候,我们怎么对x进行操作呢?
# 首先得在类C创建一个属性x

c = C()

print(c.x)  
c.setx('123')  # 设置x值
print(c.getx())
c.delx()  # 删除x值
print(c.x)

输出:

None
123
None

 

可以看到我们需要利用对象名.方法名对属性进行操作,这种方式有哪些不足呢?我们来对比一下用property托管属性x的操作就知道了。

class C:

    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value
        if self._x < 123:
            raise ValueError('error!')

    def delx(self):
        del self._x

    # 通过property产生属性x
    x = property(getx, setx, delx, "I'm the 'x' property.")


# 用property对象托管x

c = C()

print(c.x)

c.x = '123'  # 设置x值,调用了c.setx()方法

print(c.x)
del c.x  # 删除x值
print(c.x)

如上面的代码所示,当我们使用了property对象托管x时,以后我们在修改该属性值时,必须与属性名一致,这样做的优势是直接用属性操作非常直接简单。但是有人可能会说,谁会把属性名随便暴露出来让你修改啊,没错,property的另一个作用就是访问限制。

实际上,我们在用c.x = '123'赋值时,就是在调用c.setx()方法,而不是直接对x进行赋值,验证这一点可以在setx加入检查参数的代码,输入违法的参数,看是不是报错。到这里,总结一下,为什么使用property呢?

  1. 可以直接的使用属性名对属性进行操作,更简单
  2. 不会暴露属性名被任意修改,实际调用的还是类中的相应方法
  3. 可以在方法中增加参数检查代码,对参数进行检查
  4. 当然,我们还可以设置只读属性(不写setx方法)

 

看到这里,应该都了解了,其实使用property只是比直接使用set和get方法来访问属性简单在了访问方式,可是不要忽略这一点,不知道在java中你们是否遇到过写几十个set和get方法的时候,看着就不舒服。Python推崇简洁的代码,任何可以使代码更简洁的方法都比较受欢迎。

@property装饰器:

了解了上面的property对象,再来看@property装饰器就很简单了,因为使用@property装饰器或者property对象其原理都是一样的,只是Python的@语法糖工具使我们可以将代码写的更简单,@property作用就是将装饰的方法变成属性来调用,使用方法:

  • 用@property声明一个getter方法,该方法名会变成属性名
  • @属性名.setter or deleter声明setter或者deleter方法

注:为什么是setter或者getter,这里可以不要考虑那么多,像在java中叫set和get,在Python中叫getter和setter自然也可以。

这是源码中对setter的描述:

 def setter(self, *args, **kwargs): # real signature unknown
        """ Descriptor to change the setter on a property. """
        pass

 

来看一下与上面使用property对象功能相同的使用@property代码:

class C:

    def __init__(self):
        self._x = None

    @property  # 装饰的方法会变成属性名
    def x(self):
        return self._x

    @x.setter  # 将setx变成x.setter方法
    def setx(self, value):
        self._x = value

    @x.deleter # # 将setx变成x.deleter方法
    def delx(self):
        del self._x


c = C()

print(c.x)

c.x = '123'  
print(c.x)
del c.x  # 删除x值
print(c.x)

可以看到,使用@property使代码更加简洁,作用与使用property对象一样。

property源码:

https://blog.csdn.net/StrongbyTime/article/details/86538172

 

 

 

 

 

 

 

 

你可能感兴趣的:(Python,装饰器,Python,@property)