如何使用装饰器
当我们之前写好了一个程序,等过了几天发现这个函数不完整,又因为函数已经发布出去,无法更改的时候,我们是否会想这世上是否有后悔药,上帝能不能再给我重来一个的机会?别急,你是否经历过打开过套娃,分分钟逼死强迫症有没有。
那么,既然现实中有套娃,Python是否也会有类似套娃的函数呢,当通过外部函数(也就是大盒子)访问内部函数(也就是小盒子)并且将后面需要添加的内容放在大盒子内、小盒子外,这就是Python中的装饰器了。
那么,装饰器(本质上就是返回函数的函数)又是怎么具体使用的呢?让我们一起来看一看
例一:
import functools#0
def metric(func):#1
@functools.wraps(func) #3
def wrapper(*args,**kw):#4
print('hi'); #5
return func(*args,**kw);#6
return wrapper ; #2
@metric #7
def Hello():
print('大家好,我是渣渣辉')
a = Hello()
从上面的例子我们可以看出对于Hello()函数这个小箱子,需要做的几个步骤。
首先需要做一个准备工作,也就需要导入functools模块,在这个模块中包含了装饰器。
需要先定义外部函数(也就是大箱子),然后在大箱子中返回一个函数A(也就是大箱子和小箱子中间的空间),然后在函数A中定义要添加的语句(也就是要放的东西)。
注意:*args是可变参数也就是参数的数目是可变的(可变参数在调用时自动组装为一个tuple),**kw是关键字参数,其允许你为函数添加新的参数名,并将其组装为一个dict。一个是参数名(相当于在定义()里面添加了新的参数名),一个是参数(也就是在‘:’后面的已经语句当中的变量)
例二
import functools#0
def log(text):#1
def decorator(func):#3
@functools.wraps(func) #4
def wrapper(*args, **kw):#6
print('%s %s():' % (text, func.__name__))#7
return func(*args, **kw)#8
return wrapper#5
return decorator#2
@log('qwer') #9
def Hello():
print('大家好,我是渣渣辉')
a = Hello();
类:
__name__这种前后都带有两个下划线的称之为特殊方法。这种特殊方法要求用户在创建实例的时候,必须把__name__中强制把类型的参数输入进去,否则会报错。
class person(object):
def __init__( self , age , gender ):
self.age = age ;
self.gender = gender ;
Liming = person( 16 , 'male' );
print( Liming.age )
print( Liming.gender )
输出为
说明,对于类,都有着继承的关系,而(object)的意思就是从哪里继承来的,如果没有继承的话,其就代表默认值。
对于__init__方法的self,其表示产生的实例本身,在调用参数的时候Python会自动将其读入,而且对于必备的参数,self都会将其传入进去,调用的时候只需使用Liming.age就可以了。另外需要说明的一点是,str类型必须加双引号""或单引号''。
对于方法和普通的函数,其仅有的区别就是类中的方法的第一个参数永远是实例变量self。
私有变量:
私有变量即这个变量对于实例对象私有的,不能被随意的改变,这样就避免了变量可以被肆意修改的恶果。
__age 这种前面有两个下划线的是‘私有变量’,即不能直接通过 实例变量.__age 来访问,但可以通过 实例对象._类名__age 来访问(最好不要这么做)
_year这种前面只有一个下划线的默认为是‘私有变量’,虽然还可以直接通过._year来访问,但是最好不要这么做。
class person(object):
def __init__( self , age , gender ):
self.__age = age ;
self._gender = gender ;
Liming = person( 16 , 'male' );
print( Liming._person__age )
print( Liming._gender )
print( Liming.__age )
输出为
那么,当我们想修改实例变量的时候,发现其不能修改,这不是失去了其应有的效果了么?别急,Python在为我们关上一扇门的同时,又为我们打开了一扇门。get_变量 和 set_变量 就为我们提供了得到和修改变量的机会,让我们来一起看一看吧。
class person(object):
def __init__( self , age , gender ):
self.__age = age ;
self.__gender = gender ;
def get_age( self ):
return self.__age ;
def get_gender( self ):
return self.__gender ;
def set_age( self , age ):
self.__age = age ;
def set_gender( self , gender ):
if not isinstance( gender , str ):
raise TypeError( '您输入的不是字符串,请重新输入' );
elif gender == 'male' or gender == 'female':
self.__gender = gender ;
Liming = person( 16 , 'male' );
print( Liming.get_age() )
print( Liming.get_gender() )
Liming.set_gender( 'female' );
print( Liming.get_gender() )
通过set_变量 方法,我们可以完成在一定条件下的更改实例变量值的操作,从而在一定程度上限制了用户的错误操作。
注意,输出的时候,Liming.get_gender()若是省去了括号,就会输出这个方法的内存地址。
类中的装饰器@property:
上面的类的调用是不是略显复杂呢?对于用户来说,要输入的东西太多并不是一件好事,还记得前面学过的装饰器么?那么装饰器这个大箱子是否又能放到类中去呢?答案是肯定的,那么现在就让我们一起来学习@property装饰器吧
class person(object):
def __init__( self , age , gender ):
self.__age = age ;
self.__gender = gender ;
def get_age( self ):
return self.__age ;
@property
def gender( self ):
return self.__gender ;
def set_age( self , age ):
self.__age = age ;
@gender.setter
def gender( self , val ):
if not isinstance( val , str ):
raise TypeError( '您输入的不是字符串,请重新输入' );
elif val == 'male' or val == 'female':
self.__gender = val ;
Liming = person( 16 , 'male' );
print( Liming.get_age() )
print( Liming.gender )
Liming.gender ='female';
print( Liming.gender )
输出为
怎么样,是不是很神奇?仅仅通过两行短代码就实现了用户端的极短调用,现在就让我们来看一看@property装饰器做了什么吧。
首先@property把gender变成了一个类的属性,也就是使得用户可以通过调用属性的方式来调用gender函数,但是这个属性仍然是只可读的;然后@gender.setter相当于把其下面的gender函数变成了属性赋值语句,也就是说这个属性可以被赋值了。
注意 属性的赋值需要借用‘=’而方法、函数的传递参数需要借用()!!!
类中的方法与函数与属性
class person(object):
identity = 'student' ;
pass;
在上述例子中,我们定义了person这个类的identity属性,在实例化类的过程中,这个类的属性也会传递给实例化对象,但是当通过重新赋值之后,实例属性的优先级将会高于类属性的优先级,只有当实例属性被删除后,类属性才会被显露出来。
class person(object):
identity = 'student' ;
pass;
Liming = person();
print( Liming.identity );
Liming.identity = 'lawyer';
print( Liming.identity );
print( person.identity );
del Liming.identity;
print( Liming.identity );
输出示意:
牢记 实例属性>类属性,即实例会覆盖类的属性