文章目录
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**********
总结:
-
函数可以像普通变量一样,作为函数的参数或者返回值进行传递。
-
函数的内部可以定义另外一个函数。目的,隐藏函数功能的实现。
-
闭包实际上也是函数定义的一种形式。
-
闭包定义的规则,再外部函数内定义一个内部函数,内部函数使用外部函数的变量,并返回内部函数的引用。
-
Python中,装饰器就是用闭包来实现的。
-
装饰器的作用,不改变现有函数的基础上,为函数增加功能。
-
装饰器的使用,通过
@装饰器函数名
的形式来给已有函数进行装饰,添加功能。 -
装饰器四种形式,根据参数的不同以及返回值的不同。
-
万能装饰器,通过可变参数(*args/**kwargs)来实现。
-
一个装饰器可以为多个函数提供装饰功能。
-
一个函数也可以被多个装饰器所装饰。
-
通过类实现装饰器,重写
__init__
和__call__
函数。 -
类装饰器再装饰函数后,原来的引用不再是函数,儿视装饰类的对象。
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
'''