• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
66zhyh99
博客园    首页    新随笔    联系   管理    订阅  订阅

笔记1:python 装饰器@

python 装饰器@:

1 函数修饰符@的作用 首先介绍函数装饰器。通过装饰器函数,在不修改原函数的前提下,对函数的功能进行合理扩充。 例如:有一个函数func(a, b),它的功能是求a,b的差值,我现在有一个需求,就是想对函数功能再装饰下,求完差值后再取绝对值,但是不能在func函数内部实现,这时候就需要装饰器函数了,比如func = decorate(func)函数,将func函数作为参数传递给decorate函数,由decorate来丰富func函数,丰富完成后再返回给func,此时func的功能就丰富

用@装饰器的格式来写的目的就是为了书写简单方便

2 函数修饰符@的工作原理

假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示:

#funA 作为装饰器函数
def funA(fn):
   #...
   fn() # 执行传入的fn参数
   #...
   return '...'
@funA
def funB():
   #...
其等价于:
def funA(fn):
   #...
   fn() # 执行传入的fn参数
   #...
   return '...'
def funB():
   #...
funB = funA(funB)
​

通过比对以上 2 段程序不难发现,使用函数装饰器 A() 去装饰另一个函数 B(),其底层执行了如下 2 步操作:

  • 将 B 作为参数传给 A() 函数;

  • 将 A() 函数执行完成的返回值反馈回 B。

    示例如下:

    #funA 作为装饰器函数
    def funA(fn):
       print("C语言中文网")
       fn() # 执行传入的fn参数
       print("http://c.biancheng.net")
       return "装饰器函数的返回值"
    ​
    @funA
    def funB():
       print("学习 Python")

    得到的是:

    C语言中文网
    学习 Python
    http://c.biancheng.net

    在此基础上,如果在程序末尾添加如下语句:

    print(funB)

    其输出结果为:

    装饰器函数的返回值

    显然,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名;同样,如果装饰器返回的是一个函数的名称,那么被修饰的函数名依然表示一个函数。

    实际上,所谓函数装饰器,就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。

带参数的函数装饰器

在分析 funA() 函数装饰器和 funB() 函数的关系时,细心的读者可能会发现一个问题,即当 funB() 函数无参数时,可以直接将 funB 作为 funA() 的参数传入。但是,如果被修饰的函数本身带有参数,那应该如何传值呢?

比较简单的解决方法就是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同。例如:

def funA(fn):
   # 定义一个嵌套函数
   def say(arc):
       print("Python教程:",arc)
   return say
​
@funA
def funB(arc):
   print("funB():", a)
funB("http://c.biancheng.net/python")

程序执行结果为:

Python教程: http://c.biancheng.net/python

其实和下面的函数相同

def funA(fn):
   # 定义一个嵌套函数
   def say(arc):
       print("Python教程:",arc)
   return say
​
def funB(arc):
   print("funB():", a)
 
funB = funA(funB)
funB("http://c.biancheng.net/python")

显然,通过 funB() 函数被装饰器 funA() 修饰,funB 就被赋值为 say。这意味着,虽然我们在程序显式调用的是 funB() 函数,但其实执行的是装饰器嵌套的 say() 函数。

但还有一个问题需要解决,即如果当前程序中,有多个(≥ 2)函数被同一个装饰器函数修饰,这些函数带有的参数个数并不相等,怎么办呢?

最简单的解决方式是用 args 和 **kwargs 作为装饰器内部嵌套函数的参数,args 和 **kwargs 表示接受任意数量和类型的参数。举个例子:

def funA(fn):
   # 定义一个嵌套函数
   def say(*args,**kwargs):
       fn(*args,**kwargs)
   return say
​
@funA
def funB(arc):
   print("C语言中文网:",arc)
​
@funA
def other_funB(name,arc):
   print(name,arc)
funB("http://c.biancheng.net")
other_funB("Python教程:","http://c.biancheng.net/python")

返回:

C语言中文网: http://c.biancheng.net Python教程: http://c.biancheng.net/python

函数装饰器可以嵌套

上面示例中,都是使用一个装饰器的情况,但实际上,Python 也支持多个装饰器,比如:

@funA
@funB
@funC
def fun():    #...

上面程序的执行顺序是里到外,所以它等效于下面这行代码:

fun = funA( funB ( funC (fun) ) )

 

 

 

带参数:

def funA(c):                       # 多一层包装 c=2
def bbb(fn):
def aaa(n): #n=1
n = n + 3 + c
print("a:",n)
fn(n)
# print("Python教程:",n)
return aaa
return bbb
​
@funA(2)
def funB(a):
b = a + 100
print("funB():", b)
funB(1)
​
输出:
a: 6
funB(): 106
​

python内置装饰符函数:

  • @staticmethod:

使用场景:当某个方法不需要用到对象中的任何资源时,将这个方法改为一个静态方法

 

也就是没有用到任何外面的资源,此时加上@staticmethod,方法变为静态方法,同事不需要常常要加的self。

比如:

@staticmethod
def get_name():
   return 'what?'

·

  • classmethod :

使用场景:修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。

# 初始类:
class Data_test(object):
   day=0
   month=0
   year=0
   def __init__(self,year=0,month=0,day=0):
       self.day=day
       self.month=month
       self.year=year
​
   def out_date(self):
       print "year :"
       print self.year
       print "month :"
       print self.month
       print "day :"
       print self.day
​
# 新增功能:
class Str2IntParam(Data_test):
   @classmethod
   def get_date(cls, string_date):
       #这里第一个参数是cls, 表示调用当前的类名
       year,month,day=map(int,string_date.split('-'))
       date1=cls(year,month,day)
       #返回的是一个初始化后的类
       return date1
​
# 使用:
r = Str2IntParam.get_date("2016-8-1")
r.out_date()
​
# 输出:
year :
2016
month :
8
day :
1

可以扩充类的方法。易于维护。

 

@property

  • 1.只读属性

  • 2.可以做方法来修改属性,也就是变成属性了,而不是方法,最直观没有括号

  • 3.@方法名.setter,可以做到修改不用参数,或者参数的预处理。

  • 4.因为property是类,所以可以用其内部的 age = property(get_age, set_age,del_age)来修改,顺序不能改变顺序。 (property函数内置方法:属性=property(get,set,del))

其功能1是可定义只读属性,也就是真正意义上的私有属性(属性前双下划线的私有属性也是可以访问的)。

class Person(object):
   def __init__(self, name, age=18):
       self.name = name
       self.__age = 18
​
   @property
   def age(self):
       return self.__age
       
xm = Person('xiaoming')  #定义一个人名小明
print(xm.age) #结果为18
xm.age = -4 #报错无法给年龄赋值
print(xm.age)
@property真正强大的是可以限制属性的定义。往往我们定义类,希望其中的属性必须符合实际,但因为在__init__里定义的属性可以随意的修改,导致很难实现。如我想实现Person类,规定每个人(即创建的实例)的年龄必须大于18岁,正常实现的话,则必须将属性age设为只读属性,然后通过方法来赋值,代码如下:
class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.__age = 18
​
   @property
   def age(self):
       return self.__age
​
   def set_age(self, age): #定义函数来给self.__age赋值
       if age < 18:
           print('年龄必须大于18岁')
           return
       self.__age = age
       return self.__age
   
xm = Person('xiaoming', 20)
   
print(xm.age)
print('----------')
xm.set_age(10)
print(xm.age)
print('----------')
xm.set_age(20)
print(xm.age)
​
​
返回:
18
----------
年龄必须大于18岁
18
----------
20

@property方法

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.__age = 18
​
   @property
   def age(self):
       return self.__age
​
   @age.setter
   def age(self, age):
       if age < 18:
           print('年龄必须大于18岁')
           return
       self.__age = age
       return self.__age
​
xm = Person('xiaoming', 20)
print(xm.age)
print('----------')
xm.age = 10
print(xm.age)
print('----------')
xm.age = 20
print(xm.age)
​

结果和上图一致。两段代码变化的内容:将set_age修改为age,并且在上方加入装饰器@age.setter。这就是@property定义可访问属性的语法,即仍旧以属性名为方法名,并在方法名上增加@属性.setter就行了。

@property是个描述符(decorator),实际上他本身是类,

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.__age = 18
​
   def get_age(self): #恢复用方法名来获取以及定义
       return self.__age
​
   def set_age(self, age):
       if age < 18:
           print('年龄必须大于18岁')
           return
       self.__age = age
       return self.__age
​
   age = property(get_age, set_age)  #增加property类
​

上述代码的运行结果和前面一致,将@property装饰的属性方法再次修改回定义方法名,然后再类的最下方,定义:属性=property(get,set,del),这个格式是固定的,是由property源码决定的。

class C(object):
   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.")
​
​
#如果 c 是 C 的实例化, c.x 将触发 getter,c.x = value 将触发 setter , del c.x 触发 deleter。

 

posted @ 2022-03-09 17:44  66zhyh99  阅读(49)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3