前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >神奇的Python property装饰器:1行代码让Python方法秒变属性

神奇的Python property装饰器:1行代码让Python方法秒变属性

作者头像
蒙娜丽宁
发布2021-04-19 15:29:03
9920
发布2021-04-19 15:29:03
举报
文章被收录于专栏:极客起源极客起源

1. property装饰器的基本用法

在讲property装饰器之前,先来看一个例子:

代码语言:javascript
复制
class MyClass:
    def __init__(self, word):
        self._word = word
    def word(self):
        return self._word

my = MyClass('Hello')
print(my.word())
print(my.word)

执行这段代码,会输出如下的结果:

代码语言:javascript
复制
Hello
<bound method MyClass.word of <__main__.MyClass object at 0x7fee500b61f0>>

这段代码的主要功能就是通过word方法返回一个字符串。而最后一行直接访问了word方法。在Python语言中,任何东西都可视为对象,方法也不例外。所以直接输出了word方法的对象形式。

不过my.word这种调用形式,其实也是访问属性的方式,所以这段代码也可以看做将word方法作为属性使用,而不是获取word对象本身。因此,如果要想将word方法作为属性使用,就要使用property装饰器。下面看一下改进的代码:

代码语言:javascript
复制
class MyClass:
    def __init__(self, word):
        self._word = word
    # 将word方法变成属性    
    @property
    def word(self):
        return self._word

my = MyClass('Hello')
# 输出Hello
print(my.word)

这段代码使用@property修饰了word方法,这时就会将word方法直接变成了属性,所以可以使用my.word形式调用word方法,运行这段代码,会输出Hello。

我们可以看到,只需要1行代码,就可以将任何一个普通的Python方法变成属性。

如果用@property修饰方法,那么就不能再当做方法调用了,例如,不能再使用my.word()形式调用word方法了,否则会抛出如下异常:

2. property装饰器的原理

可能有很多小伙伴感到很神奇,为何直接用@property修饰方法,就可以将Python方法变成属性呢?本节就来详细描述property装饰器的原理。

首先要了解property到底是什么,使用下面的代码输出property:

代码语言:javascript
复制
print(property)

输出结果如下:

代码语言:javascript
复制
<class 'property'>

很明显,property是一个类。而Python装饰器其实就是一个语法糖,本质上是将Python装饰器作为函数使用,并将被修饰器修饰的方法/函数作为参数值传入装饰器函数。例如,使用@property装饰word方法,那么就相当于使用下面的代码包装word方法:

代码语言:javascript
复制
property(word)

也就是说,word方法被@property修饰后,就会变成property类的实例。

可以用下面的代码来演示property装饰器的原理。在这段代码中,使用@property修饰了word方法,而new_word方法直接通过创建property实例的方式修饰。

代码语言:javascript
复制
class MyClass:
    def __init__(self, word):
        self._word = word
    @property
    def word(self):
        return self._word
    # 输出被修饰的word方法的类型    
    print('word:', type(word))

    def new_word(self):
        return self._word
    # 输出未被修饰的new_word方法的类型     
    print('new_word:', type(new_word))
    new_word = property(new_word)
    print(type(new_word))
my = MyClass("android")

print(my.word)
print(my.new_word)

执行这段代码,会输出如下内容:

从输出结果可以看出,被@property修饰的word方法的类型是property类,而未被@property修饰的new_word方法的类型是function类。而且通过创建property实例的方式包装的new_word方法也可以当做属性使用,与下面的代码等效:

代码语言:javascript
复制
@property
def new_word(self):
    return self._word

3. 让属性可写、可删除

用@property修饰的方法是只读属性,既不可以写,也不可以删除,否则会抛出异常。

如果使用my.word = 'new'设置word属性,会抛出如下异常。

如果使用del my.word删除word属性,会抛出如下异常:

其实property类还有setter方法和deleter方法,可以将属性变成可写和可删除的,先看下面的代码:

代码语言:javascript
复制
class MyClass:
    def __init__(self, word):
        self._word = word
    @property
    def word(self):
        return self._word
    # 设置可写属性    
    @word.setter
    def word(self, value):
        self._word = value
    # 设置可删除属性,删除word属性时会调用该方法    
    @word.deleter
    def word(self):
        print('delete word')
        self._word = ''
    # 通过创建property实例的方式将new_word方法变成可读写和可删除的
    def new_word(self):
        return self._word
    # 将new_word变成只读的属性,并且需要将property实例赋给一个新的变量,否则会被后面的new_word方法覆盖
    new_word1 = property(new_word)
    def new_word(self, value):
        self._word = value
    # 将new_word变成可写的属性
    new_word1 = new_word1.setter(new_word)

    def new_word(self):
        print('delete new word')
    # 将new_word变成可删除的属性
    new_word = new_word1.deleter(new_word)


my = MyClass('hello')

print(my.word)
my.word = 'world'   #  def word(self, value):
print(my.word)
del my.word
print(my.word)

print('---------')
my = MyClass('ios')

print(my.new_word)
my.new_word = 'harmony'
print(my.new_word)
del my.new_word
print(my.new_word)

执行这段代码啊,会输出如下内容:

4. 获得原始方法

用@property修饰的方法,就会被property实例取代。那么如何获取原始的方法呢?这就要通过property类的如下3个方法:

(1)fget:获取被@property或@property.getter修饰的方法

(2)fset:获取被@property.setter修饰的方法

(3)fdel:获取被@property.deleter修饰的方法

在下面的例子中,分别获取了word属性的3个原始方法,并且调用了这3个原始方法

代码语言:javascript
复制
class MyClass:
    def __init__(self, word):
        self._word = word
    @property
    def word(self):
        return self._word
    @word.setter
    def word(self, value):
        self._word = value
    @word.deleter
    def word(self):
        print('delete word')
        self._word = ''
    fget_word = word.fget
    fset_word = word.fset
    fdel_word = word.fdel

my = MyClass('android')
print(my.fget_word())
my.fset_word('harmony')
print(my.fget_word())
print(my.fdel_word())

执行这段代码,会输出如下的结果。

- EOF -

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客起源 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档