这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战
在Python中,有一个@property
(描述符)装饰器,是用来修饰方法的。
使用@property
装饰器装饰一个类的方法,可以把这个方法变成属性调用,起到既能检查属性,还能用属性的方式来访问该方法的作用。
@property的使用及功能
修饰方法,可以让方法像属性一样访问
简单说:就是把类里的实例方法当做属性去调用访问,使得封装性更好, 让开发者在使用的时候,就像是在调用属性一样简单。
看下面一段代码:
class Person:
def __init__(self):
self.name = "张三"
self.birthday = 1997
@property
def get_age_with_property(self):
age = datetime.now().year - self.birthday
return age
def get_age_without_property(self):
age = datetime.now().year - self.birthday
return age
per = Person()
print(per.name)
print(per.get_age_without_property())
print(per.get_age_with_property) # 调用get_age_with_property不用加()
执行结果为:张三 24 24
,这是因为,其中get_age_with_property()使用了@property
装饰器来修饰,可以直接使用per.get_age_with_property方式进行调用,与访问属性一样;而get_age_without_property()没有使用@property
装饰器,不能以这种方式访问。
给属性设置getter和setter方法
class Person:
def __init__(self):
self.name = "张三"
self.__birthday = 1997
@property
def birthday(self):
return self.__birthday
@birthday.setter
def birthday(self, value):
self.__birthday = value
per = Person()
print(per.birthday)
per.birthday = 2222
print(per.birthday)
以上代码就是@property
装饰器的写法。通过@property
装饰器和@get函数名.setter
装饰器分别装饰get函数和set函数,就可以给属性设置类似于getter和setter方法的功能。
设置只读属性,防止属性被修改
如果想要设置一个类的属性为只读的,不可修改,这时候怎么办呢?
@property
是最好的选择,它就可以创建只读属性。
class Person:
def __init__(self):
self.name = "张三"
self.__birthday = 1997
@property
def birthday(self):
return self.__birthday
per = Person()
print(per.birthday)
per.birthday = 2222
print(per.birthday)
执行结果如下:
异常信息提示我们"AttributeError: can't set attribute",不可设置该属性。
只要我们只使用
@property
装饰器, 不使用@birthday.setter
装饰器,就可以让用户只能使用属性,而无法修改属性。
@property实现原理
在开头说过,@property
是个描述符,实际上他本身也是类。
通过源码我们就可以知道
所谓描述符,其实很好解释,就是某个类,只要是内部定义了方法 __get__()
、__set__()
、__delete_()
方法中的一个或多个,同时给另一个类的属性赋值为实例,那么该类可以称之为描述符。
那么我们只要在定义类的时候,在类内实现__get__()
、__set__()
、__delete_()
方法,那么这个类就是一个描述符,就会拥有和@property
一样的功能。
class MyProperty:
def __init__(self, get=None, set=None):
self.__get = get
self.__set = set
def __get__(self, instance, type):
return self.__get(instance)
def __set__(self, instance, value):
if self.__set is None:
raise AttributeError("this attribute is read-only")
return self.__set(instance, value)
def setter(self, set):
self.__set = set
return self
这里解释下__get__(self, instance, type)中的三个参数:
- self:描述符对象自身
- instance:被代理类的实例对象
- type:将描述符对象附加到哪个类上,其实是instance所属的类
接下来我们使用上面自定义的MyProperty描述符,装饰Person类的方法,实现和@property
一样的功能:
class Person:
def __init__(self):
self.name = "张三"
self.__birthday = 1997
@MyProperty
def birthday(self):
return self.__birthday
@birthday.setter
def birthday(self, value):
self.__birthday = value
per = Person()
print(per.birthday)
per.birthday = 2222
print(per.birthday)
总结
关于@property
的介绍就到这,@property
装饰器就是一个语法糖,Python中的语法糖往往给我们提供了更实用的编码方式,有益于形成更好的编码风格,让代码高效又易读。
最后,感谢女朋友在工作和生活中的包容、理解与支持 !