0

The code is Python, but it's more an algorithmic question I can't solve.

I use an API which defines a set of instances, each one is reachable by its Name property:

print(APIClass[a_name].Name)
>>> 'a_name'
print(APIClass[another_one].Name)
>>> 'another_one'

I want (and I'm allowed) to rename a specific instance, but I want to prevent any duplicate (which leads to an API crash). If I rename the second in my example to 'a_name', I have first to rename the first one, eg:

APIClass[a_name].Name = 'a_name_old'
print(APIClass[a_name_old].Name)
>>> 'a_name_old'

and it's OK (APIClass[another_name] = 'a_name'). But I have to check first that no one else has a_name_old as property, and so on.

Should I populate a set with the Name properties, then rename each item, and then rename the instances? But in this case (I tried it), how to be sure that a 'new' name will not be the same that an old one? (in this case, my tries failed when renaming instances) Please note I don't want to remove any duplicates, just rename them.

EDIT:

The 'APIClass' are provided, I'm only a user and I can't modify them (by adding methods, or so on). I can only modify instances. So I want to modify the 'Name' property of one of them, but I have to check (and rename if needed) the other ones. If I rename one, in my opinion I have to re-check (and re-rename) the remainings. So I could do it with a recursive algorithm, but I thought this is not efficient (for memory eg).

Aurélien Vasseur
  • 135
  • 1
  • 1
  • 9
  • What is ˋAPIClassˋ algorithmically? Can you query its keys, instances and/or pairs efficiently? Can you efficiently check if names are taken? Is it fine to have transient conflicts, or must names always be unique? Do you have to follow a naming scheme for (automatically) named instances? Is the namespace of desired names dense or sparse, I.e. do you consider conflicts as likely or not? – MisterMiyagi Dec 12 '19 at 18:38
  • 1
    Your problem boils down to keeping track of class instances: https://stackoverflow.com/questions/12101958/how-to-keep-track-of-class-instances. I'd suggest following that guide but instead tracking a set of all names. Then (using a validation step or property) check that set for duplicates every time you change an instances name. – Brian Dec 12 '19 at 18:41
  • 1
    @MisterMiyagi : 'APIClass' is a name used here only for example. The API provides several classes, for several of them we have access to 'Name' property. The only way to get Names is to loop through instances (`for c in APIClass: (...)`). No transient conflict allowed. No naming scheme. Conflicts are likely. – Aurélien Vasseur Dec 13 '19 at 09:59
  • @BrianJoseph : not sure to really understand the link you send. Perhaps beyond my skills. I will take some time to understand it. – Aurélien Vasseur Dec 13 '19 at 10:11
  • @AurélienVasseur, no problem, I've updated with an answer – Brian Dec 13 '19 at 15:38

2 Answers2

0

Yes, you should have some sort of name directory as a class variable. Also, build a simple get/set interface for the Name property, so that any user will go through that instead of directly manipulating the name.

Your set_name method will check the list of existing names and act appropriately if the suggested name is already in use. Resolving a collision can be simple, such as appending a number.

Prune
  • 76,765
  • 14
  • 60
  • 81
0

I'm not sure what limitations you have on your APIClass. However, if you can implement some combination of instance name tracking and property setting/deleting then you can achieve what you want. Here is some code that implements just that:

class APIClass:
    instances = set()

    def __init__(self, name):
        self.name = name

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

    @name.setter
    def name(self, new_name):

        assert isinstance(new_name, str)
        if new_name in self.instances:
            raise ValueError(f'Name already in use: {new_name}')

        else:
            if hasattr(self, '_name'):  # must not be first time setting name
                self.instances.remove(self._name)  # Don't forget to remove the old name before changing
            self.instances.add(new_name)
            self._name = new_name

    @name.deleter
    def name(self):
        if self._name in self.instances:
            self.instances.remove(self._name)

    def __del__(self):

        # I wont delve into why this is here. If the instance creation gets aborted due to a naming conflict,
        # Then __del__ gets called, but that leads to an attribute error, hence the try block.
        try:
            self.instances.remove(self._name)

        except AttributeError:
            pass

While this isn't great practice, it answers your question.

Here's a demo of what it lets you do:

foo = APIClass('foo')
bar = APIClass('bar')
baz = APIClass('baz')

print(APIClass.instances)  # All 3 names tracked

foo.name = 'changed_foo'

print(APIClass.instances)  # Note the updated foo.name

del foo

print(APIClass.instances)  # foo.name was removed from the set

bar_2 = APIClass('bar')  # Should error since "bar" is in use

output:

{'baz', 'bar', 'foo'}
{'changed_foo', 'baz', 'bar'}
{'baz', 'bar'}
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
# ... < long stack trace here> ...
<ValueError: Name already in use: bar

Brian
  • 1,572
  • 9
  • 18