Python - 面向对象编程 - @property

前言

  • 前面讲到实例属性的时候,我们可以通过 实例对象.实例属性 来访问对应的实例属性
  • 但这种做法是不建议的,因为它破坏了类的封装原则
  • 正常情况下,实例属性应该是隐藏的,只允许通过类提供的方法来间接实现对实例属性的访问和操作
class PoloBlog:
    # 构造方法
    def __init__(self, name):
        self.name = name


blog = PoloBlog("小菠萝")
# 破坏了封装原则
print(blog.name)
blog.name = "啊?"
print(blog.name)


# 输出结果
小菠萝
啊?

 

getter、setter 方法

  • 不破坏类封装原则的基础上,操作实例属性
  • 写过 java 的话应该知道,java 的类可以自动生成对属性的操作方法,一个是 get,另一个是 set(一般称为 getter、setter 方法)
  • python 中虽然不能自动生成,但也可以自己写哦
class PoloBlog:
    # 构造方法
    def __init__(self, name):
        self.name = name

    # set 属性的方法【setter】
    def setName(self, name):
        self.name = name

    # get 属性的方法【getter】
    def getName(self):
        return self.name


blog = PoloBlog("小菠萝")
# 获取 blog 实例对象的 name 实例属性
print(blog.getName())

# 设置 name 实例属性
blog.setName("新的小菠萝")

print(blog.getName())



# 输出结果
小菠萝
新的小菠萝

这样跟 java 的写法就差不多了,但还是有点麻烦

 

property() 方法的诞生

可以实现在不破坏类封装原则的前提下,让开发者依旧使用  实例对象.属性  的方式操作类中的属性

 

基本使用格式

属性名 = property(fget=None, fset=None, fdel=None, doc=None)
  • fget:用于获取属性的方法
  • fset:用于设置属性的方法
  • fdel:用于删除属性的方法
  • doc:属性的说明文档字符串

 

代码栗子

# property() 函数
class PoloBlog:
    # 构造函数
    def __init__(self, name):
        self.__name = name

    # setter
    def setName(self, name):
        self.__name = name

    # getter
    def getName(self):
        return self.__name

    # del
    def delName(self):
        self.__name = "xxx"

    # property()
    name = property(getName, setName, delName, "小菠萝测试笔记")


# 调用说明文档
# help(PoloBlog.name)
print(PoloBlog.name.__doc__)

blog = PoloBlog("小菠萝")

# 自动调用 getName()
print(blog.name)

# 自动调用 setName()
blog.name = "新的小菠萝"
print(blog.name)

# 自动调用 delName()
del blog.name
print(blog.name)


# 输出结果
小菠萝测试笔记
小菠萝
新的小菠萝
xxx

getName return 的是私有属性 __name,注意不是 name,不然会陷入死循环

 

注意

property() 方法的四个参数都是默认参数,可以不传参

# property() 函数
class PoloBlog:
    # 构造函数
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    # setter name
    def setName(self, name):
        self.__name = name

    # getter name
    def getName(self):
        return self.__name

    # del name
    def delName(self):
        self.__name = "xxx"

    # setter age
    def setAge(self, age):
        self.__age = age

    # getter age
    def getAge(self):
        return self.__age

    # property()
    name = property(getName, setName, delName, "小菠萝测试笔记")
    # 没有 fdel、doc
    age = property(getAge, setAge)


blog = PoloBlog("小菠萝", 14)

print(blog.age)

blog.age = "24"
print(blog.age)

del blog.age
print(blog.age)


# 输出结果
14
24
    del blog.age
AttributeError: can't delete attribute 

因为 property() 没有传 fdel 方法,所以无法删除属性,它是一个可读写,不可删的属性

 

其他传参解析

name = property(getName)    # name 属性可读,不可写,也不能删除
name = property(getName, setName,delName)    #name属性可读、可写、也可删除,就是没有说明文档

  

@property

  • 是一个装饰器,相当于 getter 装饰器
  • 可以使用 @property 来创建只读属性,将一个实例方法变成一个相同名称的只读实例属性,这样可以防止属性被修改

 

代码栗子

# @property
class PoloBlog:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name


blog = PoloBlog("小菠萝")
print(blog.name)

blog.name = "test"


# 输出结果
小菠萝

    blog.name = "test"
AttributeError: can't set attribute

name 是一个只读属性,不可写,相当于 __name 私有属性只有 getter 方法,没有 setter 方法

 

等价写法

class PoloBlog:
    def __init__(self, name):
        self.__name = name

    def getName(self):
        return self.__name

    name = property(getName)


blog = PoloBlog("小菠萝")
print(blog.name)

 

那想给 __name 设置值怎么办呢?

 

setter 装饰器

语法格式

@方法名.setter
def 方法名(self, value):
    self.__value = value
     ...

 

代码栗子

# @setter
class PoloBlog:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name


blog = PoloBlog("小菠萝")
# 打印属性值
print(blog.name)
# 修改属性
blog.name = "新的小菠萝"
print(blog.name)


# 输出结果
小菠萝
新的小菠萝

 

deleter 装饰器

和 setter 装饰器差不多写法

 

语法格式

@方法名.deleter
def 方法名(self):
     ...

 

代码栗子

class PoloBlog:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    @name.deleter
    def name(self):
        print("删除 __name")


blog = PoloBlog("小菠萝")
# 打印属性值
print(blog.name)
# 修改属性
blog.name = "新的小菠萝"
# 删除属性
del blog.name


# 输出结果
小菠萝
删除 __name

 


@property 踩坑

加了 @property 的方法相当于一个实例属性,所以不能和其他实例属性重名

 

错误代码栗子

class A:
    def __init__(self):
        # 已经定义了 name 实例属性
        self.name = 2

    # 这里相当于也定义了一个 name 实例属性
    @property
    def name(self):
        return self.name

    @name.setter
    def name(self, name):
        self.name = name


a = A()
print(a.name)



# 输出结果
Traceback (most recent call last):
  File "/Users/polo/Documents/pylearn/第四章:面向对象/17_实战5.py", line 26, in <module>
    a = A()
  File "/Users/polo/Documents/pylearn/第四章:面向对象/17_实战5.py", line 14, in __init__
    self.name = 2
  File "/Users/polo/Documents/pylearn/第四章:面向对象/17_实战5.py", line 23, in name
    self.name = name
  File "/Users/polo/Documents/pylearn/第四章:面向对象/17_实战5.py", line 23, in name
    self.name = name
  File "/Users/polo/Documents/pylearn/第四章:面向对象/17_实战5.py", line 23, in name
    self.name = name
  [Previous line repeated 994 more times]
RecursionError: maximum recursion depth exceeded

报错翻译:递归错误 超过最大递归深度

其实就是因为命名冲突导致了死循环

改下命名就好了

class A:
    def __init__(self):
        # 已经定义了 name 实例属性
        self.name = 2

    # 这里相当于也定义了一个 name 实例属性
    @property
    def name_func(self):
        return self.name

    @name_func.setter
    def name_func(self, name):
        self.name = name


a = A()
print(a.name)


# 输出结果
2

 

posted @ 2021-08-30 22:44  小菠萝测试笔记  阅读(1898)  评论(0编辑  收藏  举报