diff --git a/setclass/setclass.py b/setclass/setclass.py index b8598cc..bb58d50 100644 --- a/setclass/setclass.py +++ b/setclass/setclass.py @@ -1,23 +1,9 @@ #!/usr/bin/env python3 from __future__ import annotations +from functools import cached_property 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): """ Musical set class, containing zero or more pitch classes. @@ -64,7 +50,7 @@ class SetClass(list): def pitch_classes(self) -> list(int): return list(self) - @cache_property + @cached_property def tonality(self) -> int: """ 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 - @cache_property + @cached_property def cardinality(self) -> int: """Returns the cardinality of the set class, i.e. the number of pitch classes.""" return len(self.pitch_classes) - @cache_property + @cached_property def brightness(self) -> int: """Returns the brightness of the set class, defined as the sum of the pitch class values.""" return sum(self.pitch_classes) - @cache_property + @cached_property def decimal(self) -> int: """ 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]) - @cache_property + @cached_property def adjacency_intervals(self) -> list(int): """Adjacency intervals between the pitch classes, used for Leonard notation subscripts.""" if not self.pitch_classes: @@ -106,7 +92,7 @@ class SetClass(list): intervals.append(self.tonality - prev) return intervals - @cache_property + @cached_property def z_relations(self) -> list: """ 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] - @cache_property + @cached_property def interval_vector(self) -> list: """ 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)) - @cache_property + @cached_property def versions(self) -> list(SetClass): """ 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) return versions - @cache_property + @cached_property def rahn_normal_form(self) -> SetClass: """ 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 return versions[0] - @cache_property + @cached_property def packed_left(self) -> SetClass: """ 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 return versions[0] - @cache_property + @cached_property def prime_form(self) -> SetClass: """ 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 return versions[0] - @cache_property + @cached_property def darkest_form(self) -> SetClass: """ Returns the version with the smallest brightness value, most packed to the left. @@ -244,7 +230,7 @@ class SetClass(list): n += 1 return versions[0] - @cache_property + @cached_property def brightest_form(self) -> SetClass: """ Returns the version with the largest brightness value. @@ -252,7 +238,7 @@ class SetClass(list): """ return self.versions[-1] if self.versions else self - @cache_property + @cached_property def inversion(self) -> SetClass: """ 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) - @cache_property + @cached_property def is_symmetrical(self) -> bool: """ 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 - @cache_property + @cached_property def complement(self) -> SetClass: """ 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) - @cache_property + @cached_property def dozenal_notation(self) -> str: """ 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', '↋') - @cache_property + @cached_property def duodecimal_notation(self) -> str: """ 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') - @cache_property + @cached_property def leonard_notation(self) -> str: """ Returns a string representation of this set class using subscripts to denote the adjacency