Python中@property的使用及原理

2,038 阅读3分钟

这是我参与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)

执行结果如下: image.png 异常信息提示我们"AttributeError: can't set attribute",不可设置该属性。 只要我们只使用@property装饰器, 不使用@birthday.setter装饰器,就可以让用户只能使用属性,而无法修改属性。

@property实现原理

在开头说过,@property是个描述符,实际上他本身也是类。 通过源码我们就可以知道 image.png

所谓描述符,其实很好解释,就是某个类,只要是内部定义了方法 __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中的语法糖往往给我们提供了更实用的编码方式,有益于形成更好的编码风格,让代码高效又易读。

最后,感谢女朋友在工作和生活中的包容、理解与支持 !