1

I'm creating a structure of classes for a wrapper of an API I'm currently writing. I have multiple classes defined inside my models file. I want to assign the default value of some attributes of classes to other classes. When I do this, I get a NameError because sometimes I try to use classes that are defined below the current class, thus Python does not know these classes yet. I've tried multiple solutions but none of them seem to work. Does anybody know an alternative or has experience with this?

my classes I've defined:

class RateResponse(BaseModel):

    def __init__(self, 
        provider=Provider()
    ):

        self.provider = provider


class Provider(ObjectListModel):

    def __init__(self):

        super(Provider, self).__init__(list=[], listObject=ProviderItem)

    @property
    def providerItems(self):
        return self.list

class ProviderItem(BaseModel):

    def __init__(self,
        code=None,
        notification=Notification(),
        service=Service()
    ):

        self.code = code
        self.notification = notification
        self.service = service

As you can see above, I'm initialising the attribute 'provider' on the class RateResponse with the an empty object of the class Provider, which is defined below it. I'm getting a NameError on this line because it's defined below RateResponse.

provider=Provider()
NameError: name 'Provider' is not defined

The simple solution to above would be to shift the places of the classes. However, this is only a snippet of my file that is currently 400 lines long, all with these types of classes and initializations. It would be impossible to order them all correctly.

I've looked up some solutions where I thought I could return an empty object of a class by a string. I thought the function would only evaluate after all the classes were defined, but I was wrong. This is what I tried:

def getInstanceByString(classStr):
    return globals()[classStr]()


class RateResponse(BaseModel):

    def __init__(self, 
        provider=getInstanceByString('Provider')
    ):

        self.provider = provider

But to no avail. Does anybody have experience with this? Is this even possible within Python? Is my structure just wrong? Any help is appreciated. Thanks.

  • Ordering classes in a 400 lines source code... doesn't sound like all that much, to be honest. Is there something actually preventing you from ordering them, e.g. cyclic dependencies? – MisterMiyagi Jul 13 '21 at 13:20
  • 1
    @MisterMiyagi Fair point, indeed there are classes that are referencing each other so that would've given me errors either way I think. Turns out I was thinking wrong as well, so I've solved it by using the suggestion that I've marked as the answer. – Alexander Schillemans Jul 13 '21 at 13:31

1 Answers1

4

This code might not mean what you want it to mean:

class RateResponse(BaseModel):

    def __init__(self, 
        provider=Provider()
    ):
        ...

This code is saying that when this class is declared you want to make an instance of Provider which will be the default value for the provider parameter.

You may have meant that the default argument should be a new instance of Provider for each client that makes an instance of RateResponse.

You can use the Mutable Default Argument pattern to get the latter:

class RateResponse(BaseModel):

    def __init__(self, provider=None):
        if provider is None:
            provider = Provider()
        ...

However, if you really do want a single instance when the client wants the default you could add a single instance below the Provider definition:

class Provider(ObjectListModel):
    ...
Singleton_Provider = Provider()

Then the RateResponse class could still use the current pattern, but instead perform this assignment inside the if:

        if provider is None:
            provider = Singleton_Provider

At the time that the assignment is performed, the Singleton_Provider will have been created.

quamrana
  • 37,849
  • 12
  • 53
  • 71
  • Indeed, I was confused. I thought that it would create an empty instance of Provider each time it was called. I have changed my code as you suggested and it is working now. Thanks! A follow-up to your edit though: if I were to use the singleton_provider, it means that every instance of RateRequest that does not explicitly assign a Provider instance, would have the same instance as their default? – Alexander Schillemans Jul 13 '21 at 13:22
  • 1
    Yes, that's the idea. I couldn't be sure which way you meant to go, so I included code for both. In general, there are equally valid reasons for going either way on this one. – quamrana Jul 13 '21 at 13:25