我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈
入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈
虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈
PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈
Oracle数据库教程:👉👉 Oracle数据库文章合集 👈👈
优 质 资 源 下 载 :👉👉 资源下载合集 👈👈
类&补充_只读属性
只读属性
概念
- 一个属性(一般指实例属性),只能读取,不能写入
应用场景
- 有些属性,只限在内部根据不同场景进行修改,对外界来说,不能修改,只能读取
实现方法
方式一:使用property装饰器
- 步骤
- 1、通过私有化全部隐藏(既不能读,也不能写)
- 2、通过方法部分公开(公开读的操作)
- 示例
class Person: def __init__(self): self.__age = 18 def getAge(self): return self.__age p = Person() # print(p.__age) # 报错,通过属性私有化,隐藏了读操作 # p.__age = 22 # 并不是修改的私有属性,而是新增了__age属性。通过属性私有化,隐藏了写操作 print(p.getAge()) # 通过指定方法,公开读操作
- 存在问题
- 1、获取属性的时候需要通过调用方法获取
p.getAge()
,而不是通过属性获取方式获取p.age
- 2、在外部直接使用赋值
p.age = 22
并不会报错(虽然没有修改内部的私有属性,而是给实例添加了一个age属性,但是给人的感觉就是修改了)
- 1、获取属性的时候需要通过调用方法获取
- 优化
class Person: def __init__(self): self.__age = 18 # @property的作用:装饰器,可以使用属性的方式调用这个方法 @property def age(self): return self.__age p = Person() print(p.age) # 18 # p.age = 22 # 报错,AttributeError: can't set attribute
- 通过优化之后,就可以通过调用属性的方式调用读属性方法了。并且,外部也不能对这个属性方法进行赋值
- 缺陷
- 通过装饰器的方式设置的只读属性,其实还是可以通过
_Person__age
这种改名之后的方式进行修改 - 也可以通过
实例.__dict__['_Person__age'] = 新值
的方式修改这个只读属性的值
- 通过装饰器的方式设置的只读属性,其实还是可以通过
方式二:使用__setattr__方法进行判断
- 步骤
- 当通过 实例.属性=值 的方式给实例添加一个属性或修改属性的值时候,会自动调用该方法
- 在这个方法内部,增加判定方法,满足条件后才把这个属性添加到
__dict__
这个字典中
- 代码说明
class Person(object): # 当通过 实例.属性=值 的方式给实例添加一个属性或修改属性的值时候,会自动调用该方法 # 在这个方法内部,才会真正的把这个属性添加到 __dict__ 这个字典中 def __setattr__(self, key, value): # key:属性 value:属性值 print(key, value) p = Person() p.age = 12 # age 12
- 使用
__setattr__
设置只读属性class Person(object): def __init__(self): self.__age = 18 pass @property def age(self): return self.__age # 当通过 实例.属性=值 的方式给实例添加一个属性或修改属性的值时候,会自动调用该方法 # 在这个方法内部,才会真正的把这个属性添加到 __dict__ 这个字典中 def __setattr__(self, key, value): # key:属性 value:属性值 # print(key, value) if key == 'age': print('隐藏属性,不可修改', key) return # self.key = value # 不能这么写,这样写也是通过 实例.属性=值 的方式给实例添加一个属性或修改属性的值,会循环调用__setattr__方法,出现死循环 # 只能通过下面的方法给实例添加/修改属性 self.__dict__[key] = value p = Person() print(p.age) # 18 p.age = 22 # 隐藏属性,不可修改 age print(p.age) # 18
补充_装饰器property详解
property作用
- 将一些“属性的操作方法(删、改、查)”关联到某一个属性中
class Person(object):
def __init__(self):
self.__age = 18
@property
def age(self, num):
"""属性的操作方法:获取属性值"""
return self.__age
property源码
- preoperty源码说明
class property(object): """ Property attribute. property(fget=None, fset=None, fdel=None, doc=None) -> property ottribute # 调用property函数,传入四个参数,返回一个属性 # 作用:将一个属性的几个操作方法(删、改、查)关联到返回的这个属性里面 # 读取这个属性的时候会自动调用第一个参数:fget方法 # 设置这个属性的值会自动调用第二个参数:fset方法 # 删除合格属性的时候会自动调用第三个参数:fdel方法 fget function to be used for getting an attribute value # 用于获取属性值的函数 fset function to be used for setting an attribute value # 用于设置属性值的函数 fdel function to be used for del'ing an attribute # 用于删除属性值的函数 doc docstring Typical use is to define a managed attribute x: # 典型的用法是定义一个定义和管理一个属性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 # 必须先开启上面的@property装饰器 @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x """
补充:经典类和新式类
- 经典类
- 概念:没有继承自object的类
- 新式类
- 概念:继承自object的类和其派生类
- 示例
# 在Python2.x 版本中 class Person: pass class Animal(object): pass # 查看基类(父类) print(Person.__base__) # () print(Animal.__base__) # <class 'object'> # =================================================== # 在Python3.x 版本中 class Person: pass class Animal(object): pass # 查看基类(父类) print(Person.__base__) # <class 'object'> print(Animal.__base__) # <class 'object'>
- 在Python2.x版本中
- 定义一个类的时候,没有显示的继承自object类,那么这个类就是一个经典类
- 必须显示的继承自object类,才是一个新式类
- 在Python3.x版本中
- 定义一个类的时候,没有显示的继承自pbject类时,会隐式的(默认)继承自object类,这个类就是一个新式类
- 在通常情况下,我们一般都写成显示继承(即:定义的类继承自object时,也用
class Person(object)
的方式定义)
property在两种类中的使用方式
property在新式类中的使用方式
- 使用方式一
class Person(object): def __init__(self): # 定义一个属性__age self.__age = 18 def get_age(self): """定义一个__age属性的操作方法(查)""" print('执行get_age方法') return self.__age def set_age(self, num): """定义一个__age属性的操作方法(改)""" print('执行set_age方法') self.__age = num # 调用property方法,使age属性与get_age、set_age方法进行关联 # 在获取age属性的值时,自动调用property函数的第一个参数:get_age方法 # 在设置age属性的值时,自动调用property函数的第二个参数:seg_age方法 age = property(get_age, set_age) p = Person() print(p.age) # 执行get_age方法 18 p.age = 36 # 执行set_age方法 print(p.age) # 执行get_age方法 36 print(p.__dict__) # {'_Person__age': 36},可以看出来,赋值方式并没有给对象增加新的属性,而是直接修改了原本的属性值
- 使用方式二
class Person(object): def __init__(self): self.__age = 18 @property def age(self): print("执行了@property装饰的age方法") return self.__age @age.setter def age(self, num): print("执行了@age.setter装饰的age方法") self.__age = num @age.deleter def age(self): print("执行了@age.deleter装饰的age方法") del self.__age p = Person() print(p.age) # 执行了@property装饰的age方法 18 p.age = 36 # 执行了@age.setter装饰的age方法 print(p.__dict__) # {'_Person__age': 36} del p.age # 执行了@age.deleter装饰的age方法
property在经典类中的使用方式(在Python2.x版本中运行)
- 使用方式一
# Python2.x版本运行 # 定义一个经典类,不继承自object类 class Person: def __init__(self): self.__age = 18 def get_age(self): print '执行了get_age方法' return self.__age def set_age(self, num): print '执行了set_age方法' self.__age = num age =property(get_age, set_age) p = Person() print p.age # 执行了get_age方法 p.age = 36 # print(p.__dict__) # {'age': 36, '_Person__age': 18} # 可以看出来,在经典类中,属性的赋值操作并不能关联到property函数的第二个参数
- 使用方式二
# Python2.x版本运行 # 定义一个经典类,不继承自object类 class Person: def __init__(self): self.__age = 18 @property def age(self): print '执行了get_age方法' return self.__age @age.setter def age(self, num): print '执行了set_age方法' self.__age = num p = Person() print p.age # 执行了get_age方法 18 p.age = 36 # print(p.__dict__) # {'age': 36, '_Person__age': 18}
- 通过示例可以看出来,在经典类中,虽然我们可以定义装饰属性赋值操作的方法,但是并不能真正的将这个操作方法与赋值操作进行关联