• Python


    前言

    • 前面讲到实例属性的时候,我们可以通过 实例对象.实例属性 来访问对应的实例属性
    • 但这种做法是不建议的,因为它破坏了类的封装原则
    • 正常情况下,实例属性应该是隐藏的,只允许通过类提供的方法来间接实现对实例属性的访问和操作
    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
  • 相关阅读:
    codeforces 1140D(区间dp/思维题)
    区间dp之四边形不等式优化详解及证明
    poj1947
    hdu4044
    poj1155
    poj3162(树形dp+优先队列)
    hdu4003详解(树形dp+多组背包)
    poj1947(树形背包)
    hdu1011(树形背包)(提供一个特殊样例)
    CopyOnWriteArrayList总结
  • 原文地址:https://www.cnblogs.com/poloyy/p/15202761.html
Copyright © 2020-2023  润新知