特性(property)

特性是对类的一个特定属性进行拦截,在操作这个属性时,执行特定的函数,对属性的操作进行拦截。

特性的实现

特性使用property类来实现,也可以使用property装饰器实现,二者本质是一样的。

property类的__init__函数接收4个参数,来实现属性的获取、赋值、删除及文档。

    def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
"""
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting, and fdel a function for del'ing, an
attribute. Typical use is to define a managed attribute x: class C(object):
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.") Decorators make defining new properties or modifying existing ones easy: class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x # (copied from class doc)
"""
pass

从代码上看,4个参数都不是必须的,如果没有传入对应的操作函数,则取默认值None,则对应的操作不受支持,试图调用默认值None时,会引发异常。

测试代码:

class Person(object):

    def __init__(self, age):
self._age = age # @property 装饰器等同于 age = property(fget=age)
@property
def age(self):
return self._age if __name__ == '__main__': jack = Person(22)
jack.age = 32

因为缺少setter的函数方法,所以试图给age赋值时,会引发异常。

AttributeError: can't set attribute

所以我们把setter方法补充完整,setter装饰器的写法是刚刚被property的装饰器所装饰的函数的名称,再加上setter属性

比如下面例子中,装饰的是 def age(self): .... 这个方法,那么对应的setter装饰器,就应该是 @age.setter

class Person(object):

    def __init__(self, age):
self._age = age
self._name = 'lilei' # @property 装饰器等同于 age = property(fget=age)
@property
def age(self):
return self._age @age.setter
def age(self, value):
print('person property age setter')
self._age = value * 2

一开始可能不太容易理解,age明明是一个实例方法,内部也没有setter这个属性,为什么就变成了一个装饰器,还有setter这个属性了?

实际上,age()方法上增加@property装饰器,等同于age = property(fget=age),将age赋值为property的实例。

所以,被装饰后的age,已经不是这个实例方法age了,而是property的实例age。

可以将age的type打印出来看看,会得到<class 'property'>,说明age已经不是当初那个age了,他们只是同名而已。

可以再做个测试,把装饰器的写法修改下:

@property
def age(self):
pass

修改为

age = property(fget=age)

不影响代码的执行,所以@age.setter装饰器就很好理解了,因为age是property类的实例,property类有setter的方法,age拥有property类的setter方法,所以可以使用@age.setter装饰器。其他的setter、getter、deleter也是同理。

特性的继承

类的实例和子类都会继承类的特性,测试代码:

class Person(object):
  def __init__(self, age):
self._age = age @property
def age(self):
return self._age @age.setter
def age(self, value):
self._age = value * 2 class Man(Person):
pass if __name__ == '__main__': tom = Man(22)
print(tom.age)
tom.age = 23
print(tom.age)

在age的setter方法中,将age的值乘以2,从运行结果上看,子类及其实例都是继承property的

22
46

特性只对实例方法有效,对于静态方法、类方法都无法使用

在子类中重写父类的property

如果想在子类中,重写父类的property,实际上要分为两种情况:一种是完全重写父类的property,一种是只想重写父类property的某些方法,比如说setter。

完全重写父类的property最为简单,在子类中重新定义一个同名的getter函数,再加上@propety装饰器即可。

class Student(Person):

    # 如果在子类重新定义一个property,会完全覆盖掉父类的同名的property,包括里面的方法
# 所以没有定义setter方法,age变为只读,无法赋值
# 实际上,这种方式是在子类重新创建了一个名为age的property对象,覆盖掉了父类名为age的property
# age = property(fget=..., fset=None)
@property
def age(self):
print('student property age getter')
return self._age

可以这么理解,上面相当于在子类创建了一个property的同名实例:age,根据类继承的原则,子类的属性会覆盖掉父类的属性,所以这个时候调用子类实例的age,只会执行子类的getter方法,同时,因为setter和deleter方法没有定义,无法进行对应的操作。

如果只想修改父类propery的某个方法,会稍微麻烦点。

在使用propery装饰器的时候,需要先指定父类的名称,property实例的名称,最后指定需要修改的方法。

如下面示例代码的 @Person.age.getter。因为age本身是property的类实例,所以@Person.age.getter 这个装饰器,相当于找到父类Person的属性age,在找到age的getter方法,并用被装饰的函数,覆盖掉父类的getter方法函数。

同样,setter方法和deleter的方法也是同理。

class Teacher(Person):

    # 只有这种方式才能正确的覆盖掉父类的getter方法
@Person.age.getter
def age(self):
print('teacher property age getter')
return self._age # 同样, 这样才能正确的覆盖掉父类特性的setter方法
# property是个类,@Person.age.setter,这个操作相当于找到Person下,这个age(property实例)的方法fset
# 并用我们在子类的方法将其覆盖。
@Person.age.setter
def age(self, value):
print('teacher property age setter')
self._age = value

上面的方法,其实有个缺陷:我们必须清楚的知道,需要重写的propery所属的父类。

这对于单继承通常的是没有问题的,但是对于多继承就会存在问题:比如继承树中存在多个同名的property那么到底应该继承哪个property就会产生疑惑。

比较合理的解决办法是,如student这个类一样,彻底重写property的实例,再使用super()方法去调用父类的方法。

class Girl(Person):

    @property
def age(self):
print('girl property age getter')
return super().age @age.setter
def age(self, value):
print('girl property age setter')
super(Girl, Girl).age.__set__(self, value)

Python的特性(property)的更多相关文章

  1. python基础——特性(property)、静态方法(staticmethod)和类方法(classmethod)

    python基础--特性(property) 1 什么是特性property property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 import math class Circl ...

  2. python 3全栈开发-面向对象之绑定方法(classmethod与staticmethod的区别)、多态、封装的特性property

    一.面向对象绑定方法 一.类中定义的函数分成两大类 1.绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入): 1. 绑定到类的方法:用classmethod装饰器装饰的方法. 为类量身定制 ...

  3. python特性--property

    在定义一个类的时候,有时我们需要获取一个类的属性值,而这个属性值需要经过类中的其他属性运算来获得的.那么很容易,只要我们在类中定义一个方法,并且通过调用方法可以获取到那个需要运算的属性值.那么,问题来 ...

  4. Python学习之property

    Python中使用Property函数可以将类中的函数当作属性来调用. 案例 __metaclass__=type class Rectangle: def __init__(self): self. ...

  5. python基础——使用@property

    python基础——使用@property 在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改: s = Student() s.score = 9 ...

  6. 三、python高级特性(切片、迭代、列表生成器、生成器)

    1.python高级特性 1.1切片 list列表 L=['Mli','add','sal','saoo','Lkkl'] L[0:3]  #即为['Mli','add','sal']  从索引0开始 ...

  7. python高级特性:切片/迭代/列表生成式/生成器

    廖雪峰老师的教程上学来的,地址:python高级特性 下面以几个具体示例演示用法: 一.切片 1.1 利用切片实现trim def trim(s): while s[:1] == " &qu ...

  8. python高级特性和高阶函数

    python高级特性 1.集合的推导式 列表推导式,使用一句表达式构造一个新列表,可包含过滤.转换等操作. 语法:[exp for item in collection if codition] if ...

  9. python中的property

    提示:这篇博文参考了两个博客,第一篇博文地址为:https://www.cnblogs.com/Lambda721/p/6132206.html,另一篇博文地址如下:关于python的property ...

随机推荐

  1. spring aop 动态代理批量调用方法实例

    今天项目经理发下任务,需要测试 20 个接口,看看推送和接收数据是否正常.因为对接传输的数据是 xml 格式的字符串,所以我拿现成的数据,先生成推送过去的数据并存储到文本,以便验证数据是否正确,这时候 ...

  2. 轻谈BFC

    BFC 定义 CSS2.1的定义 Block formatting contexts 9.4.1 Block formatting contexts Floats, absolutely positi ...

  3. LeetCode :My solution N-Queens

    N-Queens Total Accepted: 15603 Total Submissions: 60198My Submissions The n-queens puzzle is the pro ...

  4. C#调用RESTful API

    如今非常多的网络服务都用RESTful API来实现. 比方百度的搜索推广API介绍使用Rest原因:REST+JSON风格的API相比SOAP+XML,优点是:调用更加灵活.也更easy扩展:JSO ...

  5. Spring Cache简单介绍和使用

    Spring Cache 缓存是实际工作中非经常常使用的一种提高性能的方法, 我们会在很多场景下来使用缓存. 本文通过一个简单的样例进行展开,通过对照我们原来的自己定义缓存和 spring 的基于凝视 ...

  6. POJ 3928 &amp; hdu 2492 &amp; Uva1428 PingPong 【树状数组】

    Ping pong                                                   Time Limit: 2000/1000 MS (Java/Others)   ...

  7. 趋势科技PC-cillin2015,你来公測我发奖!

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXF1c2hp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/d ...

  8. jsp的标签库和自定义标签

    1.jstl标签库 JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能. JSTL支持通用的.结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签. ...

  9. 【JavaScript】 JS面向对象的模式与实践 (重点整治原型这个熊孩子 (/= _ =)/~┴┴ )

    参考书籍 <JavaScript高级语言程序设计>—— Nicholas C.Zakas <你不知道的JavaScript>  —— KYLE SIMPSON   在JS的面向 ...

  10. 关于IntelliJ IDEA有时候快捷键无效的说明

    1.这个原因最大的因素可能就是 搜狗输入法了, 关闭搜狗输入法,ok, 2.也可能是qq快捷键冲突,关闭它. 3.也可能是搜狗输入法快捷键冲突,关闭它.