#๐Ÿ”’ Finding the proper container class for storing custom classes

29 messages ยท Page 1 of 1 (latest)

sonic dagger
#

Greetings, I'm trying to write a small script to audit my architecture library that is split between a file (YAML) and folder structure.

In order to not lose myself in nested dictionaries I've decided to try out my hand at dataclasses, but I've hit a particular problem, how to store them? Say I have the "Architect" class, I want to be able to has a set() but be able to retrieve the instance of the class if the hashed attribute is the same (the name) to be able to compare/update/join instances whether it's missing info from one side or the other.

@dataclass
class Architect:
    name: str
    based: Optional[str] = None
    url: Optional[str] = None

    def __hash__(self) -> int:
        return hash(self.name)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, type(self)):
            return False
        else:
            return self.name == other.name

architect = Architect(name=name)
if architect in architects:
    old_instance = architects.get(architect)

The approach that I tried was mimicking the dict() builtin and always use hash() as the key for the item:

class RetrivableSet:
    def __init__(self, iterable: Optional[Iterable[Hashable]] = None):
        self.data: dict[Hashable, Hashable] = {}
        if iterable is not None:
            self.add(*iterable)

    def __contains__(self, item: Hashable):
        return hash(item) in self.data

    def __getitem__(self, item: Hashable):
        return self.data[hash(item)]

    def get(self, item: Hashable) -> Hashable:
        key = hash(item)
        if key in self:
            return self[key]
        else:
            return item

    def add(self, item: Hashable):
        self.data[hash(item)] = item

    def remove(self, item: Hashable):
        del self.data[hash(item)]

architects: RetrivableSet = RetrivableSet()
architects.add(architect)
split ravineBOT
#

@sonic dagger

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

modest flax
#

I've only glanced at your question, but it feels like you're overthinking it

#

you probably want to use the frozen keyword to the decorator

#

!e

from dataclasses import dataclass
from typing import Optional


@dataclass(frozen=True)
class Architect:
    name: str
    based: Optional[str] = None
    url: Optional[str] = None


architects = set()

architect = Architect(name="Le Corbusier")
if architect in architects:
    old_instance = architects.get(architect)
else:
    architects.add(architect)

print(architects)
split ravineBOT
modest flax
#

Now, this doesn't specify equality -- I think it'll let you have two different "Le Corbusier"s, if their based or url fields differ.

#

!e

from dataclasses import dataclass
from typing import Optional


@dataclass(frozen=True)
class Architect:
    name: str
    based: Optional[str] = None
    url: Optional[str] = None


architects = set()

sam = Architect(name="sam", based="The Shire")
samuel = Architect(name="sam", based="Westchester County, NY")

print(f"{sam=} == {samuel=}? {sam == samuel}")
split ravineBOT
modest flax
#

!e

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional


@dataclass(frozen=True)
class Architect:
    name: str
    based: Optional[str] = None
    url: Optional[str] = None

    def __eq__(self, o: Architect) -> bool:
        return self.name == o.name


architects = set()

sam = Architect(name="sam", based="The Shire")
samuel = Architect(name="sam", based="Westchester County, NY")

print(f"{sam=} == {samuel=}? {sam == samuel}")
``` this makes equality pay attention only to the name.
split ravineBOT
sonic dagger
#

I actually want to be able to change them, what I'm really after is a way to store them in something that does not require me to loop through it.

A more ugly way that I have of writing what I want is the following:

#

!e

from dataclasses import dataclass
from typing import Optional


@dataclass(kw_only=True)
class Architect:
    name: str
    based: Optional[str] = None
    url: Optional[str] = None

    def __hash__(self) -> int:
        return hash(self.name)


architects: dict[int, Architect] = {}
architect1 = Architect(name="Corbusier")
architect2 = Architect(name="Corbusier")
architects[hash(architect1)] = architect1

if hash(architect2) in architects:
    old_architect = architects[hash(architect2)]

print(architect1 is old_architect, architect2 is old_architect)
split ravineBOT
modest flax
#

I must not be paying attention -- you just posted something that seems to do just what you want, so ... what's your question again?

sonic dagger
#

Ah, this seems quite cumbersome to write, because I have to remember to hash it.
I guess I had two questions:

  • Is there already a container class that does this kind of thing? I tried to use a set() but I cannot retrieve the item from there without iterating through it
  • If there isn't, how can I properly annotate the class?
modest flax
#

could you not just do dict[arch.name] = arch?

#

you can't put your architects into a set because they're mutable; that's why I suggested "frozen". But if you want them to be mutable, you'll have to do ... pretty much what you're currently doing

#

I'd have thought that if you add __eq__ you can do it

#

also don't compare with is; use ==

sonic dagger
#

I could yeah, but wanted to have an easier way to change how they are compared.

sonic dagger
modest flax
#

I apologize, I'm not paying close attention

sonic dagger
#

All good, thank you for help anyway!

modest flax
#

!e

from dataclasses import dataclass
from typing import Optional


@dataclass(kw_only=True)
class Architect:
    name: str
    based: Optional[str] = None
    url: Optional[str] = None

    def __hash__(self) -> int:
        return hash(self.name)

    def __eq__(self, o) -> bool:
        return self.name == o.name


architects: dict[int, Architect] = {}
architect1 = Architect(name="Corbusier")
architect2 = Architect(name="Corbusier", url="http://say/wat")
architects[architect1] = architect1

if architect2 in architects:
    old_architect = architects[architect2]
    print("Fetched it, Boss")

print(architect1 == old_architect, architect2 == old_architect)
split ravineBOT
modest flax
#

last attempt ๐Ÿ™‚

split ravineBOT
#
Python help channel closed for inactivity

This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.