Die Python-Funktion property() macht aus Methoden berechnete Attribute — von außen sieht es aus wie ein normales Attribut, intern läuft eine Funktion. Mit Getter, Setter und Deleter lassen sich Validierung, Lazy-Loading und Read-Only-Felder ohne separate get_*/set_*-Methoden umsetzen. Im Alltag wird property als Decorator @property verwendet.

Einleitung

In Sprachen wie Java schreibt man getName() und setName() als Methoden — Konsumenten müssen wissen, dass es Methoden sind. Python kennt eine sauberere Lösung: Properties. obj.attr und obj.attr = value sehen aus wie Attribut-Zugriffe, rufen aber unter der Haube Funktionen auf.

Drei Hauptanwendungen:

  1. Computed Properties: circle.area aus radius berechnen.
  2. Validierung beim Setzen: Nur erlaubte Werte zulassen.
  3. Lazy-Loading: Teures Berechnen erst beim ersten Zugriff.

property() lässt sich in zwei Formen nutzen:

  • als Decorator @property (Standard, lesbar)
  • als Funktionsaufruf prop = property(fget, fset, fdel, doc) (für Metaklassen, dynamisch)

Syntax

Python Syntax
# Decorator-Form (Standard)
class MyClass:
    @property
    def attr(self):
        ...
    @attr.setter
    def attr(self, value):
        ...
    @attr.deleter
    def attr(self):
        ...

# Funktions-Form
attr = property(fget=None, fset=None, fdel=None, doc=None)
Parameter
fget

Getter-Funktion. Bekommt self.

fset

(Optional) Setter-Funktion. Bekommt self und value.

fdel

(Optional) Deleter-Funktion. Bekommt self.

doc

(Optional) Docstring.

Rückgabewert

Ein property-Descriptor — das Objekt, das beim Attribut-Zugriff Getter/Setter/Deleter triggert.

Beispiele

Computed Property (read-only)

Klassischer Anwendungsfall — area und circumference werden bei jedem Zugriff aus radius berechnet:

Python Beispiel
from math import pi

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

    @property
    def area(self):
        return pi * self.radius ** 2

    @property
    def circumference(self):
        return 2 * pi * self.radius

c = Circle(5)
print(round(c.area, 2))
print(round(c.circumference, 2))
Output
78.54
31.42

Mit Setter und Validierung

Mit Setter lässt sich beim Schreiben validieren — der Konsument bemerkt nichts vom Property-Mechanismus:

Python Beispiel
class Temperature:
    def __init__(self, celsius=0):
        self.celsius = celsius   # ruft Setter

    @property
    def celsius(self):
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("Unter dem absoluten Nullpunkt!")
        self._celsius = value

t = Temperature(25)
print(t.celsius)
t.celsius = -10
print(t.celsius)
try:
    t.celsius = -300
except ValueError as e:
    print(e)
Output
25
-10
Unter dem absoluten Nullpunkt!

Mit Deleter

Selten gebraucht, aber praktisch — der Deleter kann Aufräumarbeit machen:

Python Beispiel
class Cache:
    def __init__(self):
        self._data = {"key": "value"}

    @property
    def data(self):
        return self._data

    @data.deleter
    def data(self):
        print("Cache wird geleert")
        self._data.clear()

c = Cache()
print(c.data)
del c.data
print(c.data)
Output
{'key': 'value'}
Cache wird geleert
{}

Praktische Beispiele

Lazy-Loading mit Property

Teure Berechnungen nur einmal — beim ersten Zugriff cachen, danach das gespeicherte Ergebnis liefern:

Python Beispiel
class Report:
    def __init__(self, raw_data):
        self.raw_data = raw_data
        self._summary = None

    @property
    def summary(self):
        if self._summary is None:
            print("(berechne summary ...)")
            self._summary = sum(self.raw_data)
        return self._summary

r = Report([1, 2, 3, 4, 5])
print(r.summary)
print(r.summary)   # cached
Output
(berechne summary ...)
15
15

functools.cached_property

In Python 3.8+ gibt es eine spezielle Decorator-Variante für genau diesen Pattern — kein manuelles Caching nötig:

Python Beispiel
from functools import cached_property

class Report:
    def __init__(self, raw_data):
        self.raw_data = raw_data

    @cached_property
    def summary(self):
        print("(berechne ...)")
        return sum(self.raw_data)

r = Report([1, 2, 3])
print(r.summary)
print(r.summary)   # nicht erneut berechnet
Output
(berechne ...)
6
6

Praktische Hinweise

  • Sauberer als get_*/set_*-Methoden — und konsumenten-transparent.
  • Read-only: Nur @property-Getter ohne Setter macht ein Read-only-Attribut.
  • Convention: das interne Backing-Feld bekommt ein führendes _ (self._celsius).
  • functools.cached_property für Lazy-One-Time-Berechnungen.
  • Verwandte Konzepte: Descriptors, __get__/__set__/__delete__, dataclass(field).
/ Weiter

Zurück zu Builtin Functions

Zur Übersicht