Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Industrial programming >> Python

Python's @property-decorator beheersen: schone, backward-compatibele getters & setters

Python's @property-decorator beheersen

Ontdek hoe Python's ingebouwde @property-decorator je laat schrijven beknopte, onderhoudbare getters en setters terwijl je API backward-compatibel blijft.

Python's @property-decorator is een krachtig hulpmiddel voor het encapsuleren van attribuuttoegang. Het stelt je in staat om custom logica te definiëren die draait wanneer een attribuut gelezen of geschreven wordt, zonder aanroepers hun code te laten wijzigen. In de volgende secties lopen we door een real-world voorbeeld – temperatuurconversies – tonen hoe je constraints afdwingt, en leggen uit waarom @property de idomatische oplossing is.


Een klasse zonder getters en setters

Stel we hebben een simpele klasse nodig die temperatuur in Celsius opslaat en kan converteren naar Fahrenheit:

class Celsius:
    def __init__(self, temperatuur=0):
        self.temperatuur = temperatuur
    def naar_fahrenheit(self):
        return (self.temperatuur * 1.8) + 32

Gebruik is eenvoudig:

# Maak nieuw object
mens = Celsius()
# Zet temperatuur
mens.temperatuur = 37
# Lees temperatuur
print(mens.temperatuur)
# Converteer naar Fahrenheit
print(mens.naar_fahrenheit())

Uitvoer

37 98.60000000000001

Het drijvende-komma resultaat is verwacht door binaire representatie van decimalen. Wanneer je mens.__dict__ inspecteer, zie je dat Python de waarde in een gewone dictionary-entry opslaat:

>>> mens.__dict__
{'temperatuur': 37}

Constraints toevoegen met getters en setters

Real-world objects hebben vaak validatie nodig. Bijvoorbeeld, temperaturen kunnen niet onder absolute nul (-273.15 °C) dalen. Een directe manier is het attribuut te verbergen en expliciete getter/setter-methoden te exposeren:

class Celsius:
    def __init__(self, temperatuur=0):
        self.set_temperatuur(temperatuur)
    def naar_fahrenheit(self):
        return (self.get_temperatuur() * 1.8) + 32
    # getter
    def get_temperatuur(self):
        return self._temperatuur
    # setter
    def set_temperatuur(self, waarde):
        if waarde < -273.15:
            raise ValueError("Temperatuur onder -273.15 is niet mogelijk.")
        self._temperatuur = waarde

Nu dwingt de klasse de ondergrens af, maar bestaande code die obj.temperatuur benaderde, moet geüpdatet worden naar obj.get_temperatuur() en obj.set_temperatuur(waarde). Dat is een grote refactor voor grote codebases.

Bovendien zijn Python's "private" variabelen slechts een naamconventie – _temperatuur kan nog steeds direct benaderd worden, wat encapsulatie breekt. Hier schittert @property.


De property-klasse gebruiken

Python biedt een property-klasse die getter- en setter-functies koppelt aan een attribuutnaam:

class Celsius:
    def __init__(self, temperatuur=0):
        self.temperatuur = temperatuur
    def naar_fahrenheit(self):
        return (self.temperatuur * 1.8) + 32
    # getter
    def get_temperatuur(self):
        print("Waarde ophalen...")
        return self._temperatuur
    # setter
    def set_temperatuur(self, waarde):
        print("Waarde instellen...")
        if waarde < -273.15:
            raise ValueError("Temperatuur onder -273.15 is niet mogelijk")
        self._temperatuur = waarde
    # exposeren als property
    temperatuur = property(get_temperatuur, set_temperatuur)

Met deze setup gedraagt de volgende code zich precies als de oorspronkelijke simpele klasse, maar met validatie achter de schermen:

mens = Celsius(37)
print(mens.temperatuur)  # triggert getter
print(mens.naar_fahrenheit())  # gebruikt getter intern
mens.temperatuur = -300  # triggert setter en gooit ValueError

Uitvoer

Waarde instellen... Waarde ophalen... 37 Waarde ophalen... 98.60000000000001 Waarde instellen... Traceback (most recent call last): ... ValueError: Temperatuur onder -273 is niet mogelijk

Let op hoe de code die temperatuur benadert ongewijzigd blijft; alleen de onderliggende implementatie kreeg validatie.

Tip: De echte data zit in _temperatuur; temperatuur is slechts een handige, veilige interface.


De @property-decorator

Hoewel de property-klasse werkt, biedt Python's decorator-syntax een schonere, leesbaardere manier om hetzelfde te bereiken. De decorator creëert automatisch het property-object en koppelt de getter en setter aan de attribuutnaam.

class Celsius:
    def __init__(self, temperatuur=0):
        self.temperatuur = temperatuur
    def naar_fahrenheit(self):
        return (self.temperatuur * 1.8) + 32
    @property
    def temperatuur(self):
        print("Waarde ophalen...")
        return self._temperatuur
    @temperatuur.setter
    def temperatuur(self, waarde):
        print("Waarde instellen...")
        if waarde < -273.15:
            raise ValueError("Temperatuur onder -273 is niet mogelijk")
        self._temperatuur = waarde

Gebruik blijft hetzelfde:

mens = Celsius(37)
print(mens.temperatuur)
print(mens.naar_fahrenheit())
# Poging om ongeldige temperatuur in te stellen
try:
    Celsius(-300)
except ValueError as e:
    print(e)

Uitvoer

Waarde instellen... Waarde ophalen... 37 Waarde ophalen... 98.60000000000001 Waarde instellen... Traceback (most recent call last): ... ValueError: Temperatuur onder -273 is niet mogelijk

@property gebruiken houdt de naamruimte netjes, elimineert de noodzaak voor aparte getter/setter-methodenamen, en is het aanbevolen patroon voor de meeste Python-ontwikkelaars.

Python

  1. Python Datatypes onder de knie krijgen: Een praktische gids
  2. Python Operators onder de knie krijgen: Een uitgebreide gids
  3. Python Pass Statement onder de knie krijgen: Een praktische gids
  4. Python Functie-argumenten onder de knie krijgen: Positionele, Keyword en Default Parameters
  5. Python-dictionaries beheersen: aanmaken, manipuleren en geavanceerde technieken
  6. Python-erfelijkheid beheersen: concepten, syntax en praktische voorbeelden
  7. Operator overloading in Python beheersen: een praktische gids
  8. Python-iterators: hoe ze werken en hoe je je eigen maakt
  9. Python-closures uitgelegd: hoe geneste functies variabelen vangen
  10. Python's @property-decorator beheersen: schone, backward-compatibele getters & setters
  11. Python datetime beheersen: praktische gids voor datums, tijden en tijdzones