让我们从 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()
让我们通过案例场景来了解所有内容。但在此之前,让我们先谈谈一些 OOP 原则。
Getter 和 setter 在许多面向对象的编程语言中使用,以确保数据封装的原则(这被视为将数据与操作这些数据的方法捆绑在一起。)
当然,这些方法是检索数据的 getter 和更改数据的 setter。
根据这一原则,将类的属性设为私有,以隐藏和保护它们免受其他代码的影响。
是的,@property基本上是一种使用 getter 和 setter 的 Python 方式。
Python 有一个很棒的概念,称为属性,它使面向对象程序员的生活变得更加简单。
让我们假设您决定创建一个可以以摄氏度为单位存储温度的类。
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
重构代码,以下是我们如何使用“属性”来实现它。
在 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”修饰。
- 必须充当 setter 的方法用“@temperature.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 的替代品!
你可以在这里查看这个实现