• python3与django中@property详解


    django提供了内置装饰器

    @staticmethod@classmethodproperty

    在OSQA中,@property的使用频率是非常高的。下面就是它的使用方法:

    @property 可以将python定义的函数“当做”属性访问,从而提供更加友好访问方式,和java中的setter和getter类似。

    models.py中如下:

    from django.db import models
    
    class Person(models.Model):
        G=(('chen','jian'),('hong','yi'),('rt','ju'))
        gender=models.CharField(max_length=20,choices=G)
    
        @property
        def Gender(self):
            return self.gender
    
        @Gender.setter
        def Gender(self,new_value):
            self.gender=new_value

    在views.py中使用:

    from django.http import HttpResponse
    from mytest.models import *
    def index(request):
        print Person.objects.all()[0].Gender
        b=Person.objects.all()[0]
        b.Gender='adfasfasd'
        print b.Gender
        b.save()
        return HttpResponse(Person.objects.all()[0].Gender)
    

    @property提供的是一个只读的属性,如果需要对属性进行修改,那么就需要定义它的setter。

    #########python3中

    @staticmethod:静态方法,静态方法是不可以访问实例变量或类变量的,这个类方法实际上跟类没有什么关系,它只是类下面的一个函数,跟类本身没关系,只是名义上归类管。
    它与类唯一的关联就是需要通过类名来调用这个方法
    如果非要传参数,只有传自己
    @classmethod:类方法只能访问类变量,不能访问实例变量

    @property :属性方法,属性方法的作用就是通过@property把一个方法变成一个静态属性
    例如:
    复制代码
    1 class Dog(object):
    2     def __init__(self,name):
    3         self.name=name
    4     @property  #把一个方法变成一个静态属性
    5     def eat(self):
    6         print('%s is eating %s' %(self.name,'包子'))
    7 d1=Dog('Jack')
    8 d1.eat  #不加(),输出:Jack is eating 包子复制代码
    复制代码
     1 #如果要传参数
     2 class Dog(object):
     3     def __init__(self,name):
     4         self.name=name
     5         self.__food=None
     6     @property
     7     def eat(self):
     8         print('%s is eating %s' %(self.name,self.__food))
     9 
    10     @eat.setter
    11     def eat(self,food):
    12         print('要传的参数:',food)
    13         self.__food=food
    14 d1=Dog('Jack')
    15 d1.eat
    16 d1.eat='包子'
    17 d1.eat    #self.__food=food把参数传值了再执行属性方法复制代码

    摘自其他人笔记

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

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

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

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

    复制代码
     1 class Student(object):  
     2     def __init__(self,name,score):  
     3         self.name = name  
     4         self.score = score  
     5     def get_score(self):  
     6         return self.score  
     7     def set_score(self,score):  
     8         if not isinstance(score, int):  
     9             raise ValueError(”invalid score!!!”)  
    10         if score < 0 or score > 100:  
    11             raise ValueError(”score must be between [0,100]!!!”)  
    12         self._score = score  复制代码

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

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

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

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

    @Property便是这个作用。

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

    于是有了下面的代码:

     1 class Student(object):  
     2     def __init__(self,name,score):  
     3         self._name = name  
     4         self._score = score  
     5     @property  
     6     def score(self):   
     7         return self._score  
     8     @score.setter   
     9     def score(self,score):    
    10         if not isinstance(score,int):  
    11             raise ValueError(”invalid score!!!”)  
    12         if score < 0 or score > 100:  
    13             raise ValueError(”score must be between [0,100]!!!”)   
    14         self._score = score  
    15     @property  
    16     def name(self):   
    17         return self._name  
    18   
    19 s1 = Student(”Lily”, 90)  
    20 s1.name = ”Luly”  
    21 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,但是这样在:

    • 复制代码
       1 class Student(object):  
       2   
       3     def __init__(self,name,score):  
       4         self._name = name  
       5         self._score = score  
       6  
       7     @property  
       8     def AA(self):  
       9         return self._score  
      10     @AA.setter  
      11     def AA(self,score):  
      12         if not isinstance(score,int):  
      13             raise ValueError(“invalid score!!!”)  
      14         if score < 0 or score > 100:  
      15             raise ValueError(“score must be between [0,100]!!!”)  
      16         self._score = score  
      17     @property  
      18     def name(self):  
      19         return self._name  
      20             
      21 s1 = Student(”Lily”, 90)  
      22 s1.name = ”Luly”  
      23 s1.AA = 100 # 这里相当于是 s1.AA(100)  
       

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

      可能存在的陷阱:

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

    • 复制代码
       1 class Student(object):   
       2     def __init__(self,name,score):         
       3         self.name = name      
       4         self.score = score     
       5     @property     
       6     def score(self):  
       7         return self.score   
       8     @score.setter   
       9     def score(self,score):     
      10         if not isinstance(score,int):   
      11             raise ValueError(”invalid score!!!”)    
      12         if score < 0 or score > 100:       
      13             raise ValueError(”score must be between [0,100]!!!”)      
      14         self.score = score   
      15     @property   
      16     def name(self):   
      17         return self.name   
      18     def func(self):      
      19         self.score = score   
      20   
      21 s1 = Student(”Lily”, 90)  
      22 s1.func()  
       

      上面的代码有两个很大的错误,

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

     
  • 相关阅读:
    高效代码审查的十个经验
    记事本
    cocos2d-x游戏引擎核心之十一——并发编程(消息通知中心)
    DE1-SOC开发板使用学习
    反思的话-180929
    反思的话-企业制度的作用180927
    XDS100V3连接Pandaboard ES OMAP4460开发板
    《手把手教你学DSP-基于TMS320F28335》书中的错误
    DSP28335做FFT傅里叶变换
    itop4412学习-上层应用多任务开发
  • 原文地址:https://www.cnblogs.com/Brin-guo/p/10962558.html
Copyright © 2020-2023  润新知