python——装饰器@property以及静态方法@staticmethod的作用与用法

本文记录了Python中@property的相关知识。@property可将方法变成静态属性,实现函数调用转化为属性访问。还介绍了@staticmethod静态方法,以及通过@property实现参数检查。同时指出使用@property可能存在的陷阱,如函数名与变量名同名导致的错误,建议避免这种情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     在看代码的过程中看到类中的函数前面加了@property,于是就上网查,印象中应该是第二次碰到了,于是这次把网上找到的资料记录下来,权当笔记。

以下资料来自  https://www.cnblogs.com/zhong2018/p/9016366.html

                         https://blog.51cto.com/10836356/2108790

若有侵权,麻烦告知下,立马删!!!                      

1.   @property :作用就是通过@property把一个方法变成一个静态属性,意思就是我们像调用函数的属性一样调用该方法。

实际使用时,我们可以传递参数和不传递参数:分别如下

class Dog(object):
    def __init__(self,name):
        self.name=name
    @property  #把一个方法变成一个静态属性
    def eat(self):
        print('%s is eating %s' %(self.name,'包子'))
d1=Dog('Jack')
d1.eat  #不加(),输出:Jack is eating 包子
#如果要传参数
class Dog(object):
    def __init__(self,name):
        self.name=name
        self.__food=None
    @property
    def eat(self):
        print('%s is eating %s' %(self.name,self.__food))

    @eat.setter
    def eat(self,food):
        print('要传的参数:',food)
        self.__food=food
d1=Dog('Jack')
d1.eat
d1.eat='包子'
d1.eat    #self.__food=food把参数传值了再执行属性方法

2. 如果要求:在类中定义一个函数,要求该函数中的位置函数与实例无关,与所在的类本身也无关,既可以通过实例调用,也可以直接通过类名调用,则可以在所定义的方法前加@staticmethom,即声明该方法为静态方法。

使用方法如下:

可以看出,虽然test方法与实例无关,与类本身也无关,但是却可以通过实例和类来调用它,却使用实例调用它是不会传入实例本身的位置参数(在正常类方法中,实例化类后,实例在调用它时,会自动默认首先传入实例本身即self)。
到了此处可能有人问,为什么不能直接在类中定义一个函数,不传self形参?按照上面的提议是否能满足实例可以调用,类本身也可以调用的要求呢?详见下图:

我们可以看出,如果直接在类中定义一个常规方法(不含self的形参),通过类本身访问它,但是通过实例来访问它时虽然也是传入2个参数,但是实际行python自动默认首位传入了self,这样就造成了上面的情况了(传的是2个参数,收到的是3个参数)

-------------------------------------------------手动分割线---------------------------------------------------------

第一个参考资料中的另一个补充知识点,个人觉得非常值得学习,关于代码规范化。

定义类Student,拥有变量名name和score

class Student(object):  
2     def __init__(self,name,score):  
3         self.name = name  
4         self.score = score

 

但是,上述这样定义score是不会进行参数检查的,也就意味着我们不能执行必要的参数以及错误处理。

我们可以定义相应的set和get成员函数来访问成员变量score,并且进行参数检查。如下所示

class Student(object):  
    def __init__(self,name,score):  
        self.name = name  
        self.score = score  
    def get_score(self):  
        return self.score  
    def set_score(self,score):  
        if not isinstance(score, int):  
            raise ValueError(”invalid score!!!”)  
        if score < 0 or score > 100:  
            raise ValueError(”score must be between [0,100]!!!”)  
        self._score = score

 

上述代码定义了score成员的set和get函数。(可能实际应用时,修改分数比较常见)

现在,我们改变参数的代码是这样的:

 s1 = Student()  
2 s1.set_score(9999) #这里会抛出异常 

 

上述的第二种方式实现了set函数的参数检查,但是修改score的代码从简单的 s1.score = 90 变成了 s1.set_score(90) .我们怎么样才能做到既检验输入的参数又使得修改score的代码不变呢?

@Property便是这个作用。

下面,我们讨论Python的高级特性 @Property。简单的说@Properyty就是将成员函数的调用变成属性赋值。

于是有了下面的代码:

class Student(object):  
    def __init__(self,name,score):  
        self._name = name  
        self._score = score  
    @property  
    def score(self):   
        return self._score  
    @score.setter   
    def score(self,score):    
        if not isinstance(score,int):  
            raise ValueError(”invalid score!!!”)  
        if score < 0 or score > 100:  
            raise ValueError(”score must be between [0,100]!!!”)   
        self._score = score  
    @property  
    def name(self):   
        return self._name  
  
s1 = Student(”Lily”, 90)  
s1.name = ”Luly”  
s1.score = 100

关于上述代码的说明:

  • 可能你已经发现了,我的成员变量改成了_name 与 _score,这里首先是为了增加可读性,这两个变量是私有的。其次的原因见下面的误区分析。
  • 上述代码中的 s1.name = “Luly” 行会出现编译错误 AttributeError: can’t set attribute ,也就是说这里不能直接这样改变,这是为什么呢?可以看到,在代码中,我并没有提供名称为name的set函数, 这里值得注意的是,s1._name = “Lucy” 是可以运行通过的。但是我们之前说过了,假设用户足够自觉,不会去操作 _xxx 或者 __xxx这样的变量名。
  • 按照上述代码的初衷,也就是说name是类的只读的属性。score是可修改的。
  • 关于@property 修饰的函数 score 就是个简单的get函数,该函数不需要任何参数(self不需要传入值),因此我们可以这样来调用这个函数 ,即 s1.score 即可。(这就是Property的用处,将函数调用转化为属性访问),相当于给score加了一层包裹。
    • 关于@score.setter 便是针对与 score函数包裹的成员变量的的set函数。当我们需要修改_score的值时,使用score函数,但是就像score是类的成员属性一样使用,例如上面的: s1.score = 100,实际上等价于 s1.score(100).

      注意,这里的函数名不一定要是score,可以是任何的字符串,这里只是为了方面说score函数是_score的包裹,例如下面的代码:我们将score改成了AA。

class Student(object):  
  
    def __init__(self,name,score):  
        self._name = name  
        self._score = score  
 
    @property  
    def AA(self):  
        return self._score  
    @AA.setter  
    def AA(self,score):  
        if not isinstance(score,int):  
            raise ValueError(“invalid score!!!”)  
        if score < 0 or score > 100:  
            raise ValueError(“score must be between [0,100]!!!”)  
        self._score = score  
    @property  
    def name(self):  
        return self._name  
            
s1 = Student(”Lily”, 90)  
s1.name = ”Luly”  
s1.AA = 100 # 这里相当于是 s1.AA(100)

好了,关于@Property的概念与用法就讲完了。本质上是定义了新的函数,该函数们执行set与get的功能,并且有@Property的包裹。并且将这些定义的函数当作属性一样来赋值。

可能存在的陷阱:

下面的代码是个大的陷阱,因为现在的函数已经不再是单纯的函数,而是可以直接用 = 来调用,例如上面的 score函数 的调用竟然是 s1.score = 100 .这样就会出现下面的问题:

class Student(object):   
    def __init__(self,name,score):         
        self.name = name      
        self.score = score     
    @property     
    def score(self):  
        return self.score   
    @score.setter   
    def score(self,score):     
        if not isinstance(score,int):   
            raise ValueError(”invalid score!!!”)    
        if score < 0 or score > 100:       
            raise ValueError(”score must be between [0,100]!!!”)      
        self.score = score   
    @property   
    def name(self):   
        return self.name   
    def func(self):      
        self.score = score   
  
s1 = Student(”Lily”, 90)  
s1.func()
  • 上面的代码有两个很大的错误,

  • 你会发现,你无法定义Student的任何实例,为什么呢? 首先@property把score和name两个成员函数可以当作成员变量来访问,那么在定义实例时,调用init函数的时候,self.name = name,这一句,Python会将左端的self.name当作函数调用,然而我们并未给name变量 定义set函数,于是错误信息为:AttributeError: can’t set attribute.
  • 好的,我们接下来注释掉
 @property  
2 def name(self):   
3     return self.name

这两行,那么接下来的运行还是错误的,为什么呢?是因为init函数代码的第二行 self.score = score, 很庆幸我们定义了score的set函数, 那么self.score调用score函数,当执行到score函数的最后一句self.score = score时, 我们回发现,式子的左端还是score函数调用, 如此往复,最终以函数递归深度达到上限退出程序。

这里其实是一个很好的代码习惯,那就是尽量不要让函数名与变量名同名,便可以避免这些错误。所以,比如说,这里的变量名self.score改为:self._score就可以避免递归错误。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值