Zacznijmy od dekoratorów Python.
Dekorator Python to funkcja, która pomaga dodać dodatkowe funkcje do już zdefiniowanej funkcji.
W Pythonie wszystko jest przedmiotem. Funkcje w Pythonie są pierwszorzędnymi obiektami, co oznacza, że można do nich odwoływać się zmienną, dodawać do list, przekazywać jako argumenty do innej funkcji itp.
Rozważ następujący fragment kodu.
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
Tutaj możemy powiedzieć, że funkcja dekoratora zmodyfikowała naszą funkcję say_hello i dodała w niej kilka dodatkowych wierszy kodu.
Składnia Pythona dla dekoratora
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()
Podsumujmy wszystko niż scenariusz przypadku, ale wcześniej porozmawiajmy o niektórych głównych zasadach.
Gettery i settery są używane w wielu obiektowych językach programowania, aby zapewnić zasadę enkapsulacji danych (jest to postrzegane jako wiązanie danych z metodami operującymi na tych danych).
Te metody są oczywiście narzędziem pobierającym dane i ustawiającym dane.
Zgodnie z tą zasadą atrybuty klasy są prywatne, aby je ukryć i chronić przed innym kodem.
Tak, @property jest w zasadzie pytonicznym sposobem używania programów pobierających i ustawiających.
Python ma świetną koncepcję zwaną właściwością, która znacznie upraszcza życie programisty obiektowego.
Załóżmy, że zdecydujesz się stworzyć klasę, która może przechowywać temperaturę w stopniach Celsjusza.
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
Refactored Code, oto jak moglibyśmy to osiągnąć za pomocą nieruchomości.
W Pythonie property () jest wbudowaną funkcją, która tworzy i zwraca obiekt właściwości.
Obiekt właściwości ma trzy metody: getter (), setter () i 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)
Tutaj,
temperature = property(get_temperature,set_temperature)
mógł zostać rozbity, ponieważ
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
Uwaga:
- get_temperature pozostaje właściwością zamiast metody.
Teraz możesz uzyskać dostęp do wartości temperatury pisząc.
C = Celsius()
C.temperature
# instead of writing C.get_temperature()
Możemy dalej iść i nie definiować nazw get_temperature i set_temperature, ponieważ są one niepotrzebne i zanieczyszczają przestrzeń nazw klas.
Pythonic sposób radzić sobie z powyższego problemu jest użycie @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
Punkty do odnotowania -
- Metodę używaną do uzyskania wartości ozdobiono „@property”.
- Metoda, która musi działać jako seter, jest dekorowana za pomocą „@ temperature.setter”. Gdyby funkcja została nazwana „x”, musielibyśmy ją ozdobić za pomocą „@ x.setter”.
- Napisaliśmy „dwie” metody o tej samej nazwie i różnej liczbie parametrów „def def (self)” i „def temperature (self, x)”.
Jak widać, kod jest zdecydowanie mniej elegancki.
Porozmawiajmy teraz o jednym praktycznym scenariuszu z życia.
Powiedzmy, że zaprojektowałeś klasę w następujący sposób:
class OurClass:
def __init__(self, a):
self.x = a
y = OurClass(10)
print(y.x)
Teraz załóżmy dalej, że nasza klasa stała się popularna wśród klientów i zaczęli używać jej w swoich programach, wykonywali wszelkiego rodzaju przypisania do obiektu.
Pewnego pamiętnego dnia przyszedł do nas zaufany klient i zasugerował, że „x” musi być wartością od 0 do 1000, to naprawdę okropny scenariusz!
Ze względu na właściwości jest to łatwe: tworzymy wersję właściwości „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
To wspaniale, prawda? Możesz zacząć od najprostszej możliwej implementacji i możesz później migrować do wersji właściwości bez konieczności zmiany interfejsu! Więc właściwości nie są tylko zamiennikiem dla pobierających i ustawiających!
Możesz sprawdzić tę implementację tutaj