Python基础-装饰器

Python基础-装饰器

1.定义及基本格式

装饰器 decorator

或者称为包装器,是对函数的一种包装。

它能使函数的功能得到扩充,而同时不用修改函数本身的代码。

它能够增加函数执行前、执行后的行为,而不需对调用函数的代码做任何改变。

其形式如下:

def myDecorator(...):    #定义装饰器,可能带参数
     def decorator(func):    #装饰器核心,以被装饰的函数对象为参数,返回装饰后的函数对象
         def wrapper(*args, **kvargs):    #装饰的过程,参数列表适应不同参数的函数
             ...    #修改函数调用前的行为
             func(*args, **kvargs)    #调用函数
             ...    #修改函数调用后的行为
         return wrapper
     return decorator
 
@myDecorator(...):    #给函数加上装饰器
def myFunc(...):      #自己定义的功能函数
    ...

装饰器存在的意义

  • 不影响原有函数的功能
  • 可以添加新功能

一般常见的,比如拿到第三方的API接口,不允许修改这个接口.这个时候,装饰器就派上了用场

装饰器本身也是一个函数,作用是为现有存在的函数,在不改变函数的基础上,增加一些功能进行装饰

它是以闭包的形式去实现的

在使用装饰器函数时,在被装饰的函数前一行,使用@装饰器函数名形式来进行装饰

2.装饰器的几种形式

2.1.无参无返回值

def setFunc(func):
    def wrapper():
        print('start')
        func()
        print('end')
    return wrapper
@setFunc
def show():
    print('show')

show()    #在不改变原函数调用释放下 添加了功能
'''
start
show
end
'''

2.2.无参有返回值

def setFunc(func):
    def wrapper():
        print('start')
        func()      # 若改为 return func()   则输出 start show 因为遇到return终止end不输出
        print('end')
    return wrapper
@setFunc
def show():
    return 'show'
print(show())    # 返回值 show 在 装饰器中 没有输出     

'''
start
end
None    #print   wrapper为空 None
'''

2.3.有参无返回值

def setFunc(func):
    def wrapper(s):
        print('start')
        func(s)
        print('end')
    return wrapper

@setFunc
def show(s):
    print('hello %s' %s)

show('zucc')

'''
start
hello zucc
end
'''

2.4.有参有返回值

def setFunc(func):
    def wrapper(x, y):
        print('start')
        return func(x, y)    	#return后终止  
        print('end')     		#end 不会输出
    return wrapper

@setFunc
def myAdd(x, y):
    return x+y

print(myAdd(1, 2))

'''
start
3
'''

3.万能装饰器

根据被装饰函数定义的不同,分出了四种形式。

能不能实现一种,适用于任何形式函数定义的装饰器?

通过可变参数(*args/**kwargs)来接收不同的参数类型

def setFunc(func):
    def wrapper(*args, **kwargs):
        print('Wrapper context.')
        return func(*args, **kwargs)

    return wrapper
@setFunc
def func(name, age, job = 'it'):
    print(name, age,'student')

func('Tom',18,)

@setFunc
def funcc(x, y):
    return x*y
print(funcc(3, 4))

@setFunc
def demo(a, b, *c, **d):
    print((a, b))
    print(c)
    print(d)
demo('zu','cc', 1999, 1, 1,school = 'zucc')

Wrapper context.
Tom 18 student
Wrapper context.
12
Wrapper context.
('zu', 'cc')
(1999, 1, 1)
{'school': 'zucc'}

4.函数被多个装饰器所装饰

一个函数在使用时,通过一个装饰器来拓展,可用多个装饰器来装饰

#装饰器1
def setFunc1(func):

    def wrapper1(*args, **kwargs):
        print('Wrapper Context 1 Start'.center(40, '*'))
        func(*args, **kwargs)
        print('Wrapper Context 1 end'.center(40, '*'))
    return wrapper1
#装饰器2
def setFunc2(func):

    def wrapper2(*args, **kwargs):
        print('Wrapper Context 2 Start'.center(40, '*'))
        func(*args, **kwargs)
        print('Wrapper Context 2 end'.center(40, '*'))
    return wrapper2

@setFunc1
@setFunc2
def show(*args, **kwargs):
    print('show run.'.center(40, '*'))

show()          # F(g(f))  嵌套先装饰最近的 2 再装饰1

********Wrapper Context 1 Start*********
********Wrapper Context 2 Start*********
***************show run.****************
*********Wrapper Context 2 end**********
*********Wrapper Context 1 end**********

总结:

  1. 函数可以像普通变量一样,作为函数的参数或者返回值进行传递。

  2. 函数的内部可以定义另外一个函数。目的,隐藏函数功能的实现。

  3. 闭包实际上也是函数定义的一种形式。

  4. 闭包定义的规则,再外部函数内定义一个内部函数,内部函数使用外部函数的变量,并返回内部函数的引用。

  5. Python中,装饰器就是用闭包来实现的。

  6. 装饰器的作用,不改变现有函数的基础上,为函数增加功能。

  7. 装饰器的使用,通过@装饰器函数名的形式来给已有函数进行装饰,添加功能。

  8. 装饰器四种形式,根据参数的不同以及返回值的不同。

  9. 万能装饰器,通过可变参数(*args/**kwargs)来实现。

  10. 一个装饰器可以为多个函数提供装饰功能。

  11. 一个函数也可以被多个装饰器所装饰。

  12. 通过类实现装饰器,重写__init____call__函数。

  13. 类装饰器再装饰函数后,原来的引用不再是函数,儿视装饰类的对象。

5.特殊的装饰器

5.1静态方法是类中的函数不需要实例

通过装饰器@staticmethod来进行装饰

静态方法不需要传递类对象,也不需要传递实例对象

静态方法可以通过实例对象和类对象来访问

静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但和类本身没有关系,在静态方法中不会涉及类的属性以及方法的操作。可以理解为,静态方法是一个独立的单纯的函数,仅仅时托管于某个类的名称空间中,比那与维护和管理。

使用场景

  • 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法

  • 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗

  • 如果在类外面写一个同样的函数来做这些事,打乱了逻辑关系,导致代码维护困难,使用静态方法。

demo:

class Dog:
    type='狗'
    def __init__(self):
        name=None

    #静态方法
    @staticmethod
    def introduce():   #静态方法不会自动传递实例对象和类对象
        print('犬科哺乳动物,肉食目')

dog=Dog()
Dog.introduce()
dog.introduce()

'''
犬科哺乳动物,肉食目
犬科哺乳动物,肉食目
'''
5.2类方法

类对象所拥有的方法

需要用装饰器@classmethod来标识其为类方法

对于类方法第一个参数必须为类对象,一般以cla 作为第一个参数

class Dog:
    __type='狗'
    #类方法,用class来进行装饰
    @classmethod
    def get_type(cls):
        return cls.__type

dog=Dog()
print(dog.get_type())

'狗'

使用场景:

当方法中需要使用类对象(比如需要访问私有属性时)

类方法一般和类属性配合使用

注意:

注意类中定义了同名的对象方法,类方法以及静态方法时,调用

class Dog():
    def demo_method(self):
        print('method')

    @classmethod
    def demo_method(cls):
        print('classmethod')
    @staticmethod
    def demo_method():
        print('staticmethod')   #最后被定义,优先被调用
dog=Dog()
Dog.demo_method()
Dog.demo_method()

'staticmethod'
'staticmethod'
5.3property方法

用于实现将类中的私有方法变为公有属性,能在类外直接调用

概述:

在Python中主要为属性提供一个遍历的操作方式

如果我们现在需要设计一个银行账户类,这个类中包含账户人的姓名余额

第一版

class Account(object):
    def __init__(self,name,money):
        self.name=name
        self.money=money

虽然这样设计简单方便,但是其中的属性都可以直接被外部访问修改

改进想法:

  • 隐藏实现细节

    对于账户而言,金额不允许让用户直接修改,在使用对象时,实现不让外部访问属性

    可以通过私有属性来实现

第二版:

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money

将属性私有化后,我们发现无法查看修改内部的属性了

改进:

  • 添加新的方法用来查看修改内部属性

第三版:

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money

    def get_name(self):
        return self.__name

    def get_money(self):
        return self.__money

    def set_money(self,n):
        self.__money=n

经过修改,外部使用这个类的对象时,想使用对象中的属性,只能通过类中提供的 set/get 接口来操作,提高了程序的安全性。

这样,程序基本达到了设计需求,但是能不能更加完善呢?

如果在使用这个类的对象过程中,由于误操作,传入了不正常的数据,导致数据异常。该如何以避免这种情况发生呢?

比如:设置金额时出现了负数,或字符串,或其它类型的对象。
改进:

数据有效性判断,在chang_money方法中,对输入money进行判断

第三版

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money

    def get_name(self):
        return self.__name

    def get_money(self):
        return self.__money

    def set_money(self,n):
        if isinstance(n,int):
            if n>0:
                self.__money=n
            else:
                raise valueError('输入金额不正确')
        else:
            raise valueError('输入金额不是数字')

经过两次迭代之后,程序变得健壮,安全性也有所提高

但是看起来不精炼

改进:

使用属性方法

property 类

在Python中,提供了一个Property类,通过创建这个类的对象的设置,在使用对象的私有属性时,可以不再使用属性的函数调用方式,而是像普通的公有属性一样去使用属性,为开发者提供便利。

property(fget=None,Fset=None,ldel=None,doc=None)  #property buribute

property()

  • 参数一:属性的获取方法
  • 参数二:属性的设置方法
  • 参数三:属性的删除方法
  • 参数四:属性的描述

是一个对象 ,__init__方法由四个参数组成。时刻i

第四版:

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money

    def get_name(self):
        return self.__name

    def get_money(self):
        return self.__money

    def set_money(self,n):
        if isinstance(n,int):
            if n>0:
                self.__money=n
            else:
                raise ValueError('输入金额不正确')
        else:
            raise ValueError('输入金额不是数字')


    name=property(get_name)
    money=property(get_money,set_money)

ac = Account('Tom', 10000)
print(ac.name)
print(ac.money)
# ac.set_money(1000)  # 调用方法
ac.money = 1000   # property提供了便利访问方式
print(ac.money)

'''
Tom
10000
1000
'''

通过 property 类实例对象以后,在使用对象中的属性时,就可以像使用普通公有属性一样来调用,但是实际调用的还是 set/get 方法。 在实例 property 对象时,不是所有的参数都需要写,比如示例中的 name 只提供了 get 方法,并且是一个私有的方法。这样就完全隐藏了内部的实现细节

第五版:

使用装饰器

class Account(object):
    def __init__(self,name,money):
        self.__name=name
        self.__money=money
    @property
    def name(self):
        return self.__name
    @property
    def money(self):
        return self.__money
    @money.setter
    def money(self,n):
        if isinstance(n,int):
            if n>0:
                self.__money=n
            else:
                raise ValueError('输入金额不正确')
        else:
            raise ValueError('输入金额不是数字')


ac = Account('Tom', 10000)
print(ac.name)
print(ac.money)
# ac.set_money(1000)  # 调用方法
ac.money = 1000   # property提供了便利访问方式
print(ac.money)

'''
Tom
10000
1000
'''
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值