Skip to content

Commit

Permalink
Merge pull request #582 from realpython/python-property
Browse files Browse the repository at this point in the history
Sample code for the article on properties
  • Loading branch information
brendaweles authored Oct 1, 2024
2 parents 16be0cf + f0b6542 commit 66d67b3
Show file tree
Hide file tree
Showing 22 changed files with 382 additions and 0 deletions.
3 changes: 3 additions & 0 deletions python-property/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Python's property(): Add Managed Attributes to Your Classes

This folder provides the code examples for the Real Python tutorial [Python's property(): Add Managed Attributes to Your Classes](https://realpython.com/python-property/).
22 changes: 22 additions & 0 deletions python-property/circle_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Circle:
def __init__(self, radius):
self._radius = radius

def _get_radius(self):
print("Get radius")
return self._radius

def _set_radius(self, value):
print("Set radius")
self._radius = value

def _del_radius(self):
print("Delete radius")
del self._radius

radius = property(
fget=_get_radius,
fset=_set_radius,
fdel=_del_radius,
doc="The radius property.",
)
19 changes: 19 additions & 0 deletions python-property/circle_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Circle:
def __init__(self, radius):
self._radius = radius

@property
def radius(self):
"""The radius property."""
print("Get radius")
return self._radius

@radius.setter
def radius(self, value):
print("Set radius")
self._radius = value

@radius.deleter
def radius(self):
print("Delete radius")
del self._radius
19 changes: 19 additions & 0 deletions python-property/circle_v3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Circle:
def __init__(self, radius):
self.radius = radius

@property
def radius(self):
return self._radius

@radius.setter
def radius(self, value):
self._radius = float(value)

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

@diameter.setter
def diameter(self, value):
self.radius = value / 2
14 changes: 14 additions & 0 deletions python-property/circle_v4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from time import sleep


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

@property
def diameter(self):
if self._diameter is None:
sleep(0.5) # Simulate a costly computation
self._diameter = self.radius * 2
return self._diameter
22 changes: 22 additions & 0 deletions python-property/circle_v5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from time import sleep


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

@property
def radius(self):
return self._radius

@radius.setter
def radius(self, value):
self._diameter = None
self._radius = value

@property
def diameter(self):
if self._diameter is None:
sleep(0.5) # Simulate a costly computation
self._diameter = self._radius * 2
return self._diameter
12 changes: 12 additions & 0 deletions python-property/circle_v6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from functools import cached_property
from time import sleep


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

@cached_property
def diameter(self):
sleep(0.5) # Simulate a costly computation
return self.radius * 2
13 changes: 13 additions & 0 deletions python-property/circle_v7.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from functools import cache
from time import sleep


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

@property
@cache
def diameter(self):
sleep(0.5) # Simulate a costly computation
return self.radius * 2
26 changes: 26 additions & 0 deletions python-property/circle_v8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import logging

logging.basicConfig(
format="%(asctime)s: %(message)s",
level=logging.INFO,
datefmt="%H:%M:%S",
)


class Circle:
def __init__(self, radius):
self._msg = '"radius" was %s. Current value: %s'
self.radius = radius

@property
def radius(self):
logging.info(self._msg % ("accessed", str(self._radius)))
return self._radius

@radius.setter
def radius(self, value):
try:
self._radius = float(value)
logging.info(self._msg % ("mutated", str(self._radius)))
except ValueError:
logging.info('validation error while mutating "radius"')
6 changes: 6 additions & 0 deletions python-property/currency_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Currency:
def __init__(self, units, cents):
self.units = units
self.cents = cents

# Currency implementation...
24 changes: 24 additions & 0 deletions python-property/currency_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CENTS_PER_UNIT = 100


class Currency:
def __init__(self, units, cents):
self._total_cents = units * CENTS_PER_UNIT + cents

@property
def units(self):
return self._total_cents // CENTS_PER_UNIT

@units.setter
def units(self, value):
self._total_cents = self.cents + value * CENTS_PER_UNIT

@property
def cents(self):
return self._total_cents % CENTS_PER_UNIT

@cents.setter
def cents(self, value):
self._total_cents = self.units * CENTS_PER_UNIT + value

# Currency implementation...
23 changes: 23 additions & 0 deletions python-property/node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class TreeNode:
def __init__(self, data):
self._data = data
self._children = []

@property
def children(self):
return self._children

@children.setter
def children(self, value):
if isinstance(value, list):
self._children = value
else:
del self.children
self._children.append(value)

@children.deleter
def children(self):
self._children.clear()

def __repr__(self):
return f'{self.__class__.__name__}("{self._data}")'
21 changes: 21 additions & 0 deletions python-property/persons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Person:
def __init__(self, name):
self._name = name

@property
def name(self):
return self._name

@name.setter
def name(self, value):
self._name = value

# Person implementation...


class Employee(Person):
@property
def name(self):
return super().name.upper()

# Employee implementation...
16 changes: 16 additions & 0 deletions python-property/point_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Point:
def __init__(self, x, y):
self._x = x
self._y = y

def get_x(self):
return self._x

def set_x(self, value):
self._x = value

def get_y(self):
return self._y

def set_y(self, value):
self._y = value
12 changes: 12 additions & 0 deletions python-property/point_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Point:
def __init__(self, x, y):
self._x = x
self._y = y

@property
def x(self):
return self._x

@property
def y(self):
return self._y
24 changes: 24 additions & 0 deletions python-property/point_v3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class WriteCoordinateError(Exception):
pass


class Point:
def __init__(self, x, y):
self._x = x
self._y = y

@property
def x(self):
return self._x

@x.setter
def x(self, value):
raise WriteCoordinateError("x coordinate is read-only")

@property
def y(self):
return self._y

@y.setter
def y(self, value):
raise WriteCoordinateError("y coordinate is read-only")
28 changes: 28 additions & 0 deletions python-property/point_v4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

@property
def x(self):
return self._x

@x.setter
def x(self, value):
try:
self._x = float(value)
print("Validated!")
except ValueError:
raise ValueError('"x" must be a number') from None

@property
def y(self):
return self._y

@y.setter
def y(self, value):
try:
self._y = float(value)
print("Validated!")
except ValueError:
raise ValueError('"y" must be a number') from None
22 changes: 22 additions & 0 deletions python-property/point_v5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Coordinate:
def __set_name__(self, owner, name):
self._name = name

def __get__(self, instance, owner):
return instance.__dict__[self._name]

def __set__(self, instance, value):
try:
instance.__dict__[self._name] = float(value)
print("Validated!")
except ValueError:
raise ValueError(f'"{self._name}" must be a number') from None


class Point:
x = Coordinate()
y = Coordinate()

def __init__(self, x, y):
self.x = x
self.y = y
21 changes: 21 additions & 0 deletions python-property/point_v6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import math


class Point:
def __init__(self, x, y):
self.x = x
self.y = y

@property
def distance(self):
return round(math.dist((0, 0), (self.x, self.y)))

@property
def angle(self):
return round(math.degrees(math.atan(self.y / self.x)), 1)

def as_cartesian(self):
return self.x, self.y

def as_polar(self):
return self.distance, self.angle
8 changes: 8 additions & 0 deletions python-property/product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Product:
def __init__(self, name, price):
self._name = name
self._price = float(price)

@property
def price(self):
return f"${self._price:,.2f}"
8 changes: 8 additions & 0 deletions python-property/rectangle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height

@property
def area(self):
return self.width * self.height
Loading

0 comments on commit 66d67b3

Please sign in to comment.