-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should palettes be iterables? #25
Comments
First of all, nice work you guys! The documentation is extremely helpful and thorough, and the API feels nice, and above all, the plots and color options look very nice. Speaking as 0.5.0's likely first user here. @mezarque, I noticed this question you made based on the title "Should palettes be iterables?", because when using the API I intuitively expected IMO, the Here are the method implementations that turn @overload
def __getitem__(self, index: int) -> HexCode: ...
@overload
def __getitem__(self, index: slice) -> T: ...
def __getitem__(self, index: Union[int, slice]) -> Union[HexCode, T]:
if isinstance(index, slice):
return self.__class__(name=f"{self.name}_slice", colors=self.colors[index])
else:
return self.colors[index]
def __iter__(self):
return iter(self.colors)
def __len__(self):
return len(self.colors) The change in action:
But if you look at the last example, it's not all roses. I think both these workarounds illustrate a potential shortcoming in the inheritance relationship between Operationally, the I'm really pulling on the thread here, so I would like to preface this by saying I think the code is perfectly fine and we probably shouldn't touch it, but I would prefer composition over inheritance here, as it solves all the issues that led me to unravel this sweater. Something like this: @dataclass
class Anchor:
color: HexCode
value: float
class Gradient:
def __init__(self, name: str, anchor_colors: List[HexCode], anchor_values: Union[List[float], None] = None):
"""
A Gradient is a sequence of pairs of HexCode objects and numeric values
from 0 to 1 that represent the position of each color in the gradient.
Args:
name (str): the name of the gradient
colors (list): a list of HexCode objects
values (list): a list of float values corresponding
to the position of colors on a 0 to 1 scale.
"""
self.name = name
if anchor_values is not None:
(...)
self.anchors = [Anchor(color, value) for color, value in zip(anchor_colors, anchor_values)]
@property
def num_anchors(self) -> int:
return len(self.anchors) Not inheriting from a sequence, After massaging Gradient's methods to use With Gradient no longer constrained to act like a color sequence, we can just get rid of the ColorSequence class altogether and dump whatever we need ( Again, I think it's great as is, and I'm really enjoying using it. But I wanted to sketch out a path for how one would cleanly enable the feature described in this issue. Alongside that, I also sketched out some hacky alternatives that could be implemented in the near term if we really want Palettes to be sequences. |
Thanks so much for taking the time to try out the package and think about this issue! Overall, I think your suggestions are really helpful. I agree that That detail aside, I think there's a handful of places that things would need to be rearranged in the package if |
Nice. I'll open a PR the next time I have some down time.
I was more just thinking of how I would like to structure the class in a world where it doesn't inherit from ColorSequence, and thought of a data structure that reflected the current docstring description. But upon further thought, I agree we don't need to replace the underlying structure... But it could be useful to instead have a property that zips colors and values together in a list of color-value two-ples, since that is an accepted format for passing custom color gradient definitions to plotly, and also the way we create matplotlib LinearSegmentedColormap objects, as illustrated in to_mpl_cmap: def to_mpl_cmap(self):
colors = [(value, color.hex_code) for value, color in zip(self.values, self.anchor_colors)]
return mcolors.LinearSegmentedColormap.from_list(
self.name,
colors=colors,
) |
That sounds good to me! In a previous version we had implemented something like this (particularly to accommodate usage with |
Currently, we use palettes just to group discrete colors together. It might be more useful to allow them to be accessed by their indices, e.g.
apc.palettes.accent[0:2]
might return aPalette
containing the first two colors. I suppose users can do this usingapc.palettes.accent.colors[0:2]
, but that feels a little cumbersome.The text was updated successfully, but these errors were encountered: