Python3通过装饰器来创建property

Python中,属性(property)是使用装饰器语法来创建的,使得可在不改变类接口的情况下,将类的方法用作属性。如此可以对数据进行封装,实现数据的获取和设置时加入逻辑控制。@property装饰器可以将一个方法转换成属性,使得可像访问属性那样访问方法。同时,还可以使用@property_name.setter装饰器来定义设置属性值时需要执行的方法。

1、装饰器简介

Python 的装饰器是一种非常有用的功能,可以使用户在不修改原有函数或方法定义的情况下,给这些函数或方法增加额外的功能。装饰器本质上是一个函数,它接受一个函数作为参数并返回一个新的函数。装饰器可以使代码更简洁。

import time
def log_calls(func):
def wrapper(*args,**kwargs):
now = time.time()
print("Calling {0} with {1} and {2}".format(func.__name__,args,kwargs))
return_value = func(*args, **kwargs)
print("Executed {0} in {1}ms".format(func.__name__,time.time()-now))
return return_value
return wrapper
def test1(a,b,c):
print("test1 called")
def test2(a,b):
print("test2 called")
def test3(a,b):
print("test3 called")
time.sleep(1)
test1 = log_calls(test1)
test2 = log_calls(test2)
test3 = log_calls(test3)

>>> test1(1,2,3)
Calling test1 with (1, 2, 3) and {}
test1 called
Executed test1 in 0.00017595291137695312ms
>>> test2(4,b=5)
Calling test2 with (4,) and {'b': 5}
test2 called
Executed test2 in 9.918212890625e-05ms
>>> test3(6,7)
Calling test3 with (6, 7) and {}
test3 called
Executed test3 in 1.0013248920440674ms

通过装饰器实现上述代码,

import time
def log_calls(func):
def wrapper(*args,**kwargs):
now = time.time()
print("Calling {0} with {1} and {2}".format(func.__name__,args,kwargs))
return_value = func(*args, **kwargs)
print("Executed {0} in {1}ms".format(func.__name__,time.time()-now))
return return_value
return wrapper
@log_calls
def test1(a,b,c):
print("test1 called")
@log_calls
def test2(a,b):
print("test2 called")
@log_calls
def test3(a,b):
print("test3 called")
time.sleep(1)

>>> test1(1,2,3)
Calling test1 with (1, 2, 3) and {}
test1 called
Executed test1 in 0.00017595291137695312ms
>>> test2(4,b=5)
Calling test2 with (4,) and {'b': 5}
test2 called
Executed test2 in 9.918212890625e-05ms
>>> test3(6,7)
Calling test3 with (6, 7) and {}
test3 called
Executed test3 in 1.0013248920440674ms

@log_calls这样的写法就是使用装饰器

2、通过装饰器创建property

使用@property可以创建出既简洁又易于使用的类接口,同时还能保持对数据的严格控制。通过使用property装饰器,可以创建出既安全又易于使用的类接口,同时还能保持代码的简洁性和灵活性。

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        """获取圆的半径"""
        return self._radius

    @radius.setter
    def radius(self, value):
        """设置圆的半径,并进行简单的校验"""
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

    @property
    def diameter(self):
        """计算并返回圆的直径"""
        return self._radius * 2

# 使用Circle类
circle = Circle(5)
print(circle.radius)   # 获取半径,输出: 5
print(circle.diameter) # 获取直径,输出: 10

circle.radius = 10     # 设置半径
print(circle.radius)   # 再次获取半径,输出: 10
print(circle.diameter) # 再次获取直径,输出: 20

# 尝试设置半径为负数,将抛出ValueError
# circle.radius = -2

推荐阅读
cjavapy编程之路首页