浅谈Python中的setter、getter、property
面向对象三个重要的特性:封装、继承、多态;
封装就意味着一个类或对象内部的属性或方法,不能随意被外部访问或者修改;java
等语言中,可以在定义变量前面加上private
来声明变量是私有变量,而python
中没有关键字用于声明私有变量,但是约定俗成地使用双下划开头的变量作为私有变量;
比如下面的代码:
class Person(object):
def __init__(self, age, name):
self.name = name
self.__age = age
if __name__ == '__main__':
p = Person(18, "张三")
print("p.name: ", p.name)
p.name = "李四"
print("p.name: ", p.name) # 可以被随意修改
print("p.__age: ", p.__age)
运行代码,报错信息如下:
p.name: 张三
Traceback (most recent call last):
print("p.__age: ", p.__age)
AttributeError: 'Person' object has no attribute '__age'
但是如果有需要访问__age
属性的情况要怎么办?如何保证其内部数据的安全性呢?
此时就需要在类的内部实现获取数据和修改数据的方法, 同时可以增加传入参数的判断,以防止类似于年龄这样的数据会出现负数或者几百上千的值:
class Person(object):
def __init__(self, age, name):
self.name = name
self.__age = age
def get_age(self):
"""获取age"""
return self.__age
def set_age(self, age):
"""设置age"""
# 可以在这里面做一些判断,比如年龄的范围可以设置在0~120
self.__age = age if age > 0 and age <= 120 else "年龄有误"
if __name__ == '__main__':
p = Person(18, "张三")
print("p.name: ", p.name)
p.name = "李四"
print("p.name: ", p.name) # 可以被随意修改
# print("p.__age: ", p.__age)
print("p.get_age(): ", p.get_age())
p.set_age(100)
print("p.get_age(): ", p.get_age())
p.set_age(150)
print("p.get_age(): ", p.get_age())
每次都要get_age()
,set_age(age)
这样做会显得很繁琐,不够pythonic
,此时,就可以用到setter
、getter
、property
的思想了:
class Person(object):
def __init__(self, age, name):
self.name = name
self.__age = age
@property
def age(self):
"""获取age"""
return self.__age
@age.setter
def age(self, age):
"""设置age"""
# 可以在这里面做一些判断,比如年龄的范围可以设置在0~120
self.__age = age if age > 0 and age <= 120 else "年龄有误"
if __name__ == '__main__':
p = Person(18, "张三")
print("p.name: ", p.name)
p.name = "李四"
print("p.name: ", p.name) # 可以被随意修改
print("p.age: ", p.age)
p.age = 32
print("p.age: ", p.age)
p.age = 999
print("p.age: ", p.age)
p.age = -18
print("p.age: ", p.age)
"""
p.name: 张三
p.name: 李四
p.age: 18
p.age: 32
p.age: 年龄有误
p.age: 年龄有误
"""
这样的话,在外部看来,age
属性的获取就和普通的实例属性差不多了,但是修改age
属性的值就不是那么随意了。
另外,如果不使用setter
、getter
这种方式,或者之前定义的set_age(age)
、get_age()
方法已经投入使用又不想改变,就可以在类中添加:age = property(get_age, set_age)
,切记这里的参数是函数名,不是函数调用,所以不能加小括号;代码如下:
class Person(object):
def __init__(self, age, name):
self.name = name
self.__age = age
def get_age(self):
"""获取age"""
return self.__age
def set_age(self, age):
"""设置age"""
# 可以在这里面做一些判断,比如年龄的范围可以设置在0~120
self.__age = age if age > 0 and age <= 120 else "年龄有误"
age = property(get_age, set_age) # 这里函数名顺序不能错,当然也可以按关键字传参
# 如: age = property(fset=set_age, fget=get_age)
if __name__ == '__main__':
p = Person(18, "张三")
print("p.name: ", p.name)
p.name = "李四"
print("p.name: ", p.name) # 可以被随意修改
print("p.age: ", p.age)
p.age = 32
print("p.age: ", p.age)
p.age = 999
print("p.age: ", p.age)
p.age = -18
print("p.age: ", p.age)
"""
p.name: 张三
p.name: 李四
p.age: 18
p.age: 32
p.age: 年龄有误
p.age: 年龄有误
"""
顺便贴上property
部分源码:
class property(object):
"""
Property attribute.
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:
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
"""
def deleter(self, *args, **kwargs): # real signature unknown
""" Descriptor to change the deleter on a property. """
pass
def getter(self, *args, **kwargs): # real signature unknown
""" Descriptor to change the getter on a property. """
pass
def setter(self, *args, **kwargs): # real signature unknown
""" Descriptor to change the setter on a property. """
pass
def __delete__(self, *args, **kwargs): # real signature unknown
""" Delete an attribute of instance. """
pass
def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass
def __get__(self, *args, **kwargs): # real signature unknown
""" Return an attribute of instance, which is of type owner. """
pass
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
"""
Property attribute.
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:
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
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature. """
pass
def __set__(self, *args, **kwargs): # real signature unknown
""" Set an attribute of instance to value. """
pass
fdel = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
fget = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
fset = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
__isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default