让我们从Python装饰器开始。
Python装饰器是一种函数,它帮助在已定义的函数中添加一些附加功能。
在Python中,一切皆为对象。Python中的函数是一等公民对象,这意味着它们可以由变量引用,在列表中添加,作为参数传递给另一个函数等。
考虑以下代码片段。
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
def say_bye():
print("bye!!")
say_bye = decorator_func(say_bye)
say_bye()
# Output:
# Wrapper function started
# bye!!
# Given function decorated
在这里,我们可以说装饰器函数修改了我们的say_bye函数,并向其中添加了一些额外的代码行。
Python中的装饰器语法
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
@decorator_func
def say_bye():
print("bye!!")
say_bye()
让我们通过一个案例来说明一切。但在此之前,让我们谈谈一些面向对象编程的原则。
Getter和Setter在许多面向对象编程语言中被用来确保数据封装的原则(这被视为将数据与操作这些数据的方法捆绑在一起的做法)。
当然,这些方法就是获取数据的getter和更改数据的setter。
根据这个原则,类的属性被设为私有来隐藏并保护它们免受其他代码的侵犯。
没错,@property基本上是使用getter和setter的“Python风格”。
Python有一个很棒的概念,称为property,使得面向对象程序员的生活变得更加简单。
假设您决定创建一个可以存储摄氏度温度的类。
class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
def get_temperature(self):
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value
重构代码,以下是我们如何使用“property”实现它的方法。
在Python中,property()是一个内置函数,用于创建并返回一个属性对象。
属性对象有三个方法:getter()、setter()和delete()。
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Getting value")
return self.temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
temperature = property(get_temperature,set_temperature)
在这里,
temperature = property(get_temperature,set_temperature)
可以拆分为以下内容:
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
注意事项:
- get_temperature仍然是一个属性而不是方法。
现在,您可以通过编写代码来访问温度的值。
C = Celsius()
C.temperature
# instead of writing C.get_temperature()
我们可以进一步简化代码,不定义名称get_temperature和set_temperature,因为它们是不必要的,会污染类命名空间。
处理上述问题的Pythonic方式是使用@property。
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
@property
def temperature(self):
print("Getting value")
return self.temperature
@temperature.setter
def temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
注意事项 -
- 用于获取值的方法使用"@property"进行装饰。
- 必须使用"@temperature.setter"来装饰作为setter的方法,如果函数名为"x",则必须使用"@x.setter"进行装饰。
- 我们编写了两个名称相同但参数数量不同的方法,"def temperature(self)"和"def temperature(self,x)"。
可以看出,代码显然不够优雅。
现在,让我们谈谈一个真实的实际场景。
假设您设计了一个如下所示的类:
class OurClass:
def __init__(self, a):
self.x = a
y = OurClass(10)
print(y.x)
现在,让我们进一步假设我们的类在客户中间很受欢迎,并且他们开始在他们的程序中使用它,对对象进行各种任务分配。
有一天,一个值得信赖的客户来找我们建议,“x”必须是0到1000之间的值;这真是一个可怕的情况!
由于属性的存在,这很容易:我们创建一个“x”的属性版本。
class OurClass:
def __init__(self,x):
self.x = x
@property
def x(self):
return self.__x
@x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
这太棒了,不是吗:你可以从最简单的实现开始,而且以后可以自由地迁移到属性版本,而无需更改接口!因此,属性不仅仅是getter和setter的替代品!
您可以在此处检查此实现。
property
实际上是一个类(不是函数),当你创建一个对象时,它可能会调用__init__()
方法。从终端使用help(property)
可以获得深入了解。令人惊讶的是,help
也是一个类。 - Brōtsyorfuzthrāx