Replace handrolled cached property with the built-in

This commit is contained in:
Jonathan Harker 2024-10-02 09:35:19 +13:00
parent 6c42477d7a
commit 42d4cfb427

View file

@ -1,23 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import annotations from __future__ import annotations
from functools import cached_property
import re import re
class cache_property:
"""
Property that only computes its value once when first accessed, and caches the result.
"""
def __init__(self, function):
self.function = function
self.name = function.__name__
self.__doc__ = function.__doc__
def __get__(self, obj, type=None) -> object:
obj.__dict__[self.name] = self.function(obj)
return obj.__dict__[self.name]
class SetClass(list): class SetClass(list):
""" """
Musical set class, containing zero or more pitch classes. Musical set class, containing zero or more pitch classes.
@ -64,7 +50,7 @@ class SetClass(list):
def pitch_classes(self) -> list(int): def pitch_classes(self) -> list(int):
return list(self) return list(self)
@cache_property @cached_property
def tonality(self) -> int: def tonality(self) -> int:
""" """
Returns the number of (equal) divisions of the octave. The default value is 12, which Returns the number of (equal) divisions of the octave. The default value is 12, which
@ -72,17 +58,17 @@ class SetClass(list):
""" """
return self._tonality return self._tonality
@cache_property @cached_property
def cardinality(self) -> int: def cardinality(self) -> int:
"""Returns the cardinality of the set class, i.e. the number of pitch classes.""" """Returns the cardinality of the set class, i.e. the number of pitch classes."""
return len(self.pitch_classes) return len(self.pitch_classes)
@cache_property @cached_property
def brightness(self) -> int: def brightness(self) -> int:
"""Returns the brightness of the set class, defined as the sum of the pitch class values.""" """Returns the brightness of the set class, defined as the sum of the pitch class values."""
return sum(self.pitch_classes) return sum(self.pitch_classes)
@cache_property @cached_property
def decimal(self) -> int: def decimal(self) -> int:
""" """
Returns the decimal value of the pitch classes expressed as a binary bit mask, i.e. the sum Returns the decimal value of the pitch classes expressed as a binary bit mask, i.e. the sum
@ -92,7 +78,7 @@ class SetClass(list):
""" """
return sum([2**i for i in self.pitch_classes]) return sum([2**i for i in self.pitch_classes])
@cache_property @cached_property
def adjacency_intervals(self) -> list(int): def adjacency_intervals(self) -> list(int):
"""Adjacency intervals between the pitch classes, used for Leonard notation subscripts.""" """Adjacency intervals between the pitch classes, used for Leonard notation subscripts."""
if not self.pitch_classes: if not self.pitch_classes:
@ -106,7 +92,7 @@ class SetClass(list):
intervals.append(self.tonality - prev) intervals.append(self.tonality - prev)
return intervals return intervals
@cache_property @cached_property
def z_relations(self) -> list: def z_relations(self) -> list:
""" """
Return all distinct set classes with the same interval vector (Allen Forte: "Z-related"). Return all distinct set classes with the same interval vector (Allen Forte: "Z-related").
@ -115,7 +101,7 @@ class SetClass(list):
""" """
return [i for i in SetClass.darkest_of_cardinality(self.cardinality) if i.interval_vector == self.interval_vector] return [i for i in SetClass.darkest_of_cardinality(self.cardinality) if i.interval_vector == self.interval_vector]
@cache_property @cached_property
def interval_vector(self) -> list: def interval_vector(self) -> list:
""" """
An ordered tuple containing the multiplicities of each interval class in the set class. An ordered tuple containing the multiplicities of each interval class in the set class.
@ -147,7 +133,7 @@ class SetClass(list):
""" """
return min(SetClass.ordered_interval(a, b), SetClass.ordered_interval(b, a)) return min(SetClass.ordered_interval(a, b), SetClass.ordered_interval(b, a))
@cache_property @cached_property
def versions(self) -> list(SetClass): def versions(self) -> list(SetClass):
""" """
Returns all possible zero-normalised versions (clock rotations) of this set class, Returns all possible zero-normalised versions (clock rotations) of this set class,
@ -165,7 +151,7 @@ class SetClass(list):
versions.sort(key=lambda x: x.brightness) versions.sort(key=lambda x: x.brightness)
return versions return versions
@cache_property @cached_property
def rahn_normal_form(self) -> SetClass: def rahn_normal_form(self) -> SetClass:
""" """
Return the Rahn normal form of the set class; Leonard describes this as "most dispersed from Return the Rahn normal form of the set class; Leonard describes this as "most dispersed from
@ -182,7 +168,7 @@ class SetClass(list):
n += 1 n += 1
return versions[0] return versions[0]
@cache_property @cached_property
def packed_left(self) -> SetClass: def packed_left(self) -> SetClass:
""" """
Return the form of the set class that is most packed to the left (smallest adjacency Return the form of the set class that is most packed to the left (smallest adjacency
@ -199,7 +185,7 @@ class SetClass(list):
n += 1 n += 1
return versions[0] return versions[0]
@cache_property @cached_property
def prime_form(self) -> SetClass: def prime_form(self) -> SetClass:
""" """
Return the prime form of the set class. Find the forms with the smallest outside interval, Return the prime form of the set class. Find the forms with the smallest outside interval,
@ -222,7 +208,7 @@ class SetClass(list):
n += 1 n += 1
return versions[0] return versions[0]
@cache_property @cached_property
def darkest_form(self) -> SetClass: def darkest_form(self) -> SetClass:
""" """
Returns the version with the smallest brightness value, most packed to the left. Returns the version with the smallest brightness value, most packed to the left.
@ -244,7 +230,7 @@ class SetClass(list):
n += 1 n += 1
return versions[0] return versions[0]
@cache_property @cached_property
def brightest_form(self) -> SetClass: def brightest_form(self) -> SetClass:
""" """
Returns the version with the largest brightness value. Returns the version with the largest brightness value.
@ -252,7 +238,7 @@ class SetClass(list):
""" """
return self.versions[-1] if self.versions else self return self.versions[-1] if self.versions else self
@cache_property @cached_property
def inversion(self) -> SetClass: def inversion(self) -> SetClass:
""" """
Returns the inversion of this set class, equivalent of reflection through the 0 axis on a Returns the inversion of this set class, equivalent of reflection through the 0 axis on a
@ -260,7 +246,7 @@ class SetClass(list):
""" """
return SetClass(*[self.tonality - i for i in self.pitch_classes], tonality=self.tonality) return SetClass(*[self.tonality - i for i in self.pitch_classes], tonality=self.tonality)
@cache_property @cached_property
def is_symmetrical(self) -> bool: def is_symmetrical(self) -> bool:
""" """
Returns whether this set class is symmetrical upon inversion, for example Forte 5-Z17: Returns whether this set class is symmetrical upon inversion, for example Forte 5-Z17:
@ -268,7 +254,7 @@ class SetClass(list):
""" """
return self.darkest_form == self.inversion.darkest_form return self.darkest_form == self.inversion.darkest_form
@cache_property @cached_property
def complement(self) -> SetClass: def complement(self) -> SetClass:
""" """
Returns the set class containing all pitch classes absent in this one (rotated so the Returns the set class containing all pitch classes absent in this one (rotated so the
@ -276,7 +262,7 @@ class SetClass(list):
""" """
return SetClass(*[i for i in range(self.tonality) if i not in self.pitch_classes], tonality=self.tonality) return SetClass(*[i for i in range(self.tonality) if i not in self.pitch_classes], tonality=self.tonality)
@cache_property @cached_property
def dozenal_notation(self) -> str: def dozenal_notation(self) -> str:
""" """
If tonality is no greater than 12, return a string representation using Dozenal Society If tonality is no greater than 12, return a string representation using Dozenal Society
@ -284,14 +270,14 @@ class SetClass(list):
""" """
return f"{self}" if self.tonality > 12 else f"{self}".replace('10', '').replace('11', '') return f"{self}" if self.tonality > 12 else f"{self}".replace('10', '').replace('11', '')
@cache_property @cached_property
def duodecimal_notation(self) -> str: def duodecimal_notation(self) -> str:
""" """
If tonality is no greater than 12, replace 10 and 11 with 'T' and 'E'. If tonality is no greater than 12, replace 10 and 11 with 'T' and 'E'.
""" """
return f"{self}" if self.tonality > 12 else f"{self}".replace('10', 'T').replace('11', 'E') return f"{self}" if self.tonality > 12 else f"{self}".replace('10', 'T').replace('11', 'E')
@cache_property @cached_property
def leonard_notation(self) -> str: def leonard_notation(self) -> str:
""" """
Returns a string representation of this set class using subscripts to denote the adjacency Returns a string representation of this set class using subscripts to denote the adjacency