Use outer class instance as self in inner class?

197 Views Asked by At

I'm writing a wrapper for the GMAIL API. In this wrapper, I am trying to include subattributes in the "main class" so it more closely follows the below:

picture

Previously, I was use methods such as:

class Foo:
    def __init__(self, ...):
        # add some attributes

    def get_method(self, ...):
        return some_stuff

This allows me to do foo.get_method(...). To follow the GMAIL API, I try to do:

class Foo:
     def __init__(self, ...):
         # add some attributes

     @property
     def method(self):
         class _Method:
             @staticmethod
             def get(self, ...):
                 return some_stuff
         return _Method()

Which allows me to do foo.method.get(...). The above has some problems, it redefines the class every time, and I have to add @staticmethod above every method as part of it. I do realise that I could create the class at the outer class level, and set a hidden variable for each which then .method returns or creates, but this seems like too much workaround.

tldr: Is it possible to make the instance passed to the inner class as self be the instance of the outer class (I do not wish to have to pass the attributes of the outer class to each inner class).

3

There are 3 best solutions below

7
Jack Taylor On BEST ANSWER

Instead of sharing the self parameter between classes, you are probably better off just passing the things you need to the constructor of the class you instantiate.

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

    def method(self, other_arg):
        return self.name + other_arg

class Test:
    name = "hi"

    def __init__(self):
        self.messages = Messages(name=self.name)

If you need to pass a lot of information to the constructor and it starts becoming unwieldy, you can do something like split the shared code into a third class, and then pass that between the Test and Messages classes as a single object.

In Python there are all sorts of clever things that you can do with metaclasses and magic methods, but in 99% of cases just refactoring things into different classes and functions will get you more readable and maintainable code.

7
Evgeny On

Users should have an instance of messages, which allows method get. The scetch for code is:

class Messages:
      ...
      def get()
            ...


class Users:
      ...
      messages = Messages(...)

allows

users =  Users()
users.messages.get()

The bad thing in this API is plural names, which is a bad sign for class. If done from scratch you would rather have classes User and Message, which make more sense.

If you have a closer look at GET/POST calls in the API you link provided, you would notice the urls are like UserId/settings, another hint to implement User class, not Users.

3
R.R.C. On

self in the methods reference the self of the outer class

maybe this is what you want factory-method

Although the example code I'll provide bellow might be similar to the already provided answers, and the link above to another answer might satify you wish, because it is slight different formed I'll still provide my vision on what you asked. The code is self explanatory.

class User:
    def __init__(self, pk, name):
        self.pk = pk
        self.name = name
        self._messages = None

    def messages(self):
        if self.messages is None:
            self._messages = Messages(self.pk)
        return self._messages


class Messages:
    def __init__(self, usr):
        self.usr = usr

    def get(self):
        return self._grab_data()

    def _grab_data(self):
        # grab the data from DB
        if self.usr == 1:
            print('All messages of usr 1')
        elif self.usr == 2:
            print('All messages of usr 2')
        elif self.usr == 3:
            print('All messages of usr 3')



one = User(1, 'One')
two = User(2, 'Two')
three = User(3, 'Three')

one.messages().get()
two.messages().get()
three.messages().get()

The messages method approach practical would be the same for labels, history etc.

Edit: I'll give one more try to myself trying to understand what you want to achieve, even though you said that

I have tried numerous things with defining the classes outside of the container class [...]

. I don't know if you tried inheritance, since your inner class me, despite it quite don't represent nothing here, but still looks like you want to make use of its functionality somehow. You said as well

self in the methods reference the self of the outer class

This sounds to me like you want inheritance at the end. Then the way to go would be (a proximity idea by using inheritance):

class me(object):
    def __init__(self):
        self.__other_arg = None # private and hidden variable
    # setter and getter methods
    def set_other_arg(self, new_other_arg):
        self.__other_arg = new_other_arg
    def get_other_arg(self): 
        return self.__other_arg

class Test(me):
    name = 'Class Test'
    @property
    def message(self):
        other_arg = self.get_other_arg()
        if other_arg is not None:
            return '{} {}'.format(self.name, other_arg)
        else:
            return self.name

t = Test()
t.set_other_arg('said Hello')
print(t.message)
# output >>> Class Test said Hello

I think this could be a preferable way to go rather than your inner class approach, my opinion, you'll decide. Just one side note, look up for getter and setter in python, it might help you if you want to stick with the inheritance idea given.