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 条评论
登录 后参与评论

相关文章

来自专栏用户画像

正则表达式匹配 整数和正整数

?匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?

722
来自专栏wym

HDU 6109 数据分割(并查集+set维护)

 题目:http://acm.hdu.edu.cn/showproblem.php?pid=6109

961
来自专栏烂笔头

Python标准库笔记(4) — collections模块

目录[-] 这个模块提供几个非常有用的Python容器类型 1.容器 名称 功能描述 OrderedDict 保持了key插入顺序的di...

3397
来自专栏小小挖掘机

哇,原来python字符串是这样的!

python中的字符串一直是困扰小编的一大难题,相信大家伙也曾体验过被各种编码支配的恐惧吧。不过没关系,相信你读了这篇文章,一定会对python字符串豁然开朗!...

3725
来自专栏marsggbo

malloc函数及用法

动态存储分配 在数组一章中,曾介绍过数组的长度是预先定义好的,在整个程序中固定不变。C语言中不允许动态数组类型。 例如: int n; scanf("%d",&...

1968
来自专栏数据结构笔记

数据结构(四):栈的应用之表达式求值

用户从控制台输入一个数学表达式(所有输入均合法),数学表达式只包含四则运算,程序需输出表达式对应的结果,如:

692
来自专栏程序生活

Python中defaultdict用法

1926
来自专栏有趣的Python

慕课网-c++远征离港篇-学习笔记const 与引用

c++远征离港篇 离港总动员 引用VS指针、 #define VS const 函数默认值&函数重载 内存管理(头疼) 封装 继承 多态 c++语言引用: 引用...

35710
来自专栏Deep learning进阶路

C++随记(六)---函数处理数组的一些问题

C++随机(六)---函数处理数组的一些问题 本篇讨论数组做函数形参的情况。 通常,我们按照以往设置形参的习惯,可能会对数组形参做如下的书写: int exa...

1770
来自专栏Vamei实验室

Python基础04 运算

Python的运算符和其他语言类似 (我们暂时只了解这些运算符的基本用法,方便我们展开后面的内容,高级应用暂时不介绍) 数学运算 >>>print 1+9   ...

1898

扫码关注云+社区