python学习笔记6.3-类的属性函数(@property)

属性函数(@property) 在对象中两个很重要的元素就是属性和方法,在调用的时候两者是有区别的。

class People:
    def __init__(self,first_name,last_name):
        self.first_name = first_name
        self.last_name  = last_name

    def get_first_name(self):
        return self.first_name

a = People('lala','ouyang')
print(a.get_first_name())
print(a.first_name)

从例子中我们可以发现,一样的结果,但是调用的过程不一样(虽然其实也就是多一个括号而已),那么有没有一种办法,使得我们调用属性的时候就会自动调用相应的方法,也就是增加一些额外的处理过程(例如类型检查或者验证)。这时候属性函数(@property)就能给我们提供很好的解决方案。 首先是最简单的例子,自动调用get,set,del函数对属性的处理。

class People:
    def __init__(self,name):
        self.name = name
    #getter function
    @property #属性函数
    def name(self):
        return self._name

    #setter function
    @name.setter
    def name(self,name):
        self._name = name

    #deleter function
    @name.deleter
    def name(self):
        raise AttributeError('Can not delete the name')

a = People('leida')
print(a.name) #calling the getter function automatic when we get the attribute
a.name = 'libai'  #Calling the setter function automatic when we set the attribute
print(a.name)
del a.name  #Calling the deleter function automatic when we delete the attribute

leida
Traceback (most recent call last):
libai
  File "D:/home/WX/test_clsaa.py", line 65, in <module>
    del a.name  #Calling the deleter function automatic when we delete the attribute
  File "D:/home/WX/test_clsaa.py", line 59, in name
    raise AttributeError('Can not delete the name')
AttributeError: Can not delete the name

正如例子中这样。要定义对属性的访问,一种最简单的方法就是将其定义为property。比如说,增加对属性的类型检查:

class People:
    def __init__(self,name):
        self.name = name
    #getter function
    @property #属性函数
    def name(self):
        return self._name

    #setter function
    @name.setter
    def name(self,name):
        if not isinstance(name,str):
            raise TypeError('name must is string type')
        self._name = name

    #deleter function
    @name.deleter
    def name(self):
        raise AttributeError('Can not delete the name')

a = People(12) #calling the setter function automatic when create the instance

Traceback (most recent call last):
  File "D:/home/WX/test_clsaa.py", line 63, in <module>
    a = People(12) #calling the setter function automatic when create the instance
  File "D:/home/WX/test_clsaa.py", line 45, in __init__
    self.name = name
  File "D:/home/WX/test_clsaa.py", line 55, in name
    raise TypeError('name must is string type')
TypeError: name must is string type

当我们实现一个property时,底层数据仍然需要保存在某个地方,因而在get和set的方法中,可以看到直接对_name操作的,这就是数据实际保存的地方。但是,也发现在init()方法中任然是对self.name操作的。但是实际情况是我初始化的时候程序仍旧是对self._name操作的。(这点我也还不理解,应该不是这样的啊.但是必须这么写,不然会报错)。 对于已经存在的get,set,del方法,同样也可以定义为property:

class People:
    def __init__(self,name):
        self.name = name
    def get_name(self):
        print('calling the get function')
        return self._name
    def set_name(self,name):
        print('calling the set function')
        self._name = name
    def del_name(self):
        del self._name
    name = property(get_name,set_name,del_name)

a = People('libai') #calling the setter function automatic when create the instance

同时,在set_name 函数中做了打印标记,发现在init()方法中确实调用了set_name()函数。

Property属性实际上就是把一系列的方法绑定到一起。如果检查类的property属性,就会发现property自身所持有的属性fget,fset,fdel所代表的原始方法。

print(People.name.fget)
print(People.name.fset)
print(People.name.fdel)

一般来说,我们不会直接去调用fset或者fget,但是当我们调用property属性时会自动触发对这些方法的调用。 上面例子中的两种写法,一般倾向于第二种写法,特别是如果需要对某个普通的属性额外增加处理步骤时,可以在不修改已有代码的情况下将这个属性提升为一个property。 Property也可以用来定义需要计算的属性,这类属性并不会实际被保存起来,而是根据需要完成计算。

import math
class Circle:
    def __init__(self,radius):
        self.radius = radius

    @property
    def area(self):
        return math.pi * self.radius**2
    @property
    def perimeter(self):
        return 2*math.pi*self.radius

c = Circle(5)
print(c.area)      #do not need (): c.area instead of c.area()
print(c.perimeter)

78.53981633974483
31.41592653589793

这样的写法就会使得实例的接口变得统一,本来用方法实现的计算调用的时候用属性就可以,很好的避免了方法、属性傻傻分不清的情况了。 个人建议,不要在代码中不断重复使用@property,这样会使得代码变得臃肿,而且难以阅读,容易出错。同样的任务,利用描述符或者闭包也能够很好的完成(以后会详细解释)。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏码洞

看完Java的动态代理技术——Pythoner笑了

Java的动态代理常用来包装原始方法调用,用于增强或改写现有方法的逻辑,它在Java技术领域被广为使用,在阿里的Sofa RPC框架序列化中你能看到它的身影,H...

693
来自专栏高性能服务器开发

你一定要搞明白的C函数调用方式与栈原理

这绝对不是标题党。而是C/C++开发中你必须要掌握的基础知识,也是高级技术岗位面试中高频题。我真的真的真的希望无论是学生还是广大C/C++开发者,都该掌握此文中...

972
来自专栏菩提树下的杨过

scala 学习笔记(04) OOP(上)主从构造器/私有属性/伴生对象(单例静态类)/apply方法/嵌套类

一、主从构造器 java中构造函数没有主、从之分,只有构造器重载,但在scala中,每个类都有一个主构造器,在定义class时,如果啥也没写,默认有一个xxx(...

1748
来自专栏偏前端工程师的驿站

JS魔法堂:函数重载 之 获取变量的数据类型

Brief                                 有时我们需要根据入参的数据类型来决定调用哪个函数实现,就是说所谓的函数重载(func...

1838
来自专栏我叫刘半仙

原荐你知道么?static关键字有5种用法。

     说到static,静态变量和静态方法大家随口就来,因为他们在实际开发中应用很广泛,但他们真正在使用的时候会存在很多问题,而且它的使用不只那两种:   ...

3486
来自专栏程序员的SOD蜜

条件表达式的短路求值与函数的延迟求值

延迟求值是 .NET的一个很重要的特性,在LISP语言,这个特性是依靠宏来完成的,在C,C++,可以通过函数指针来完成,而在.NET,它是靠委托来完成的。如果不...

1936
来自专栏java一日一条

Spring-涉及到的设计模式汇总

又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。

683
来自专栏向治洪

Kotlin基础之委托

委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。 类委托 委托模式是替换继承的较...

1746
来自专栏决胜机器学习

设计模式专题(十五) ——组合模式

设计模式专题(十五)——组合模式 (原创内容,转载请注明来源,谢谢) 一、概述 组合模式(Composite)将对象组合成树形结构,以表示部分-整体的层次结构...

37411
来自专栏编程

使用dict和set

Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。-...

18510

扫码关注云+社区