-4

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).

IllustriousMagenta
  • 514
  • 1
  • 4
  • 19
  • 2
    Wat do you mean "Use a class definition so all the functions are namespaced"? Also what is the outer class? You question(s) aren't clear. – martineau Sep 16 '18 at 02:03
  • How is this not clear? Namespaced = all the functions are contained within the class so I don't pollute the global namespace of the module - event if I do have `_name`, I still don't like having that many functions. – IllustriousMagenta Sep 16 '18 at 02:33
  • It's unclear because I don't understand what you want to do/accomplish. The only thing further I can say is, if it was more understandable, you'd likely get better answers... – martineau Sep 16 '18 at 13:09
  • I clearly outlined what I want (under my wishes), what I've tried, and then the quesiton at the end? Is there anything more clear? – IllustriousMagenta Sep 16 '18 at 21:39
  • 1
    OK, to be more specific, putting functions in a class just to have them in a namespace is unusual (and likely unnecessary)—but if was done, there won't be a `self` class instance involved, just the class name itself. Therefore it's unclear what `self` you're talking about. You also haven't defined what the outer class is nor what purpose or role it plays in all this. – martineau Sep 17 '18 at 01:09
  • I've rewritten the question, hopefully it's more clear? – IllustriousMagenta Sep 18 '18 at 03:55
  • The changes you made are an improvement. It strikes me that one thing it seems you don't understand is that [staticmethods](https://docs.python.org/3/library/functions.html#staticmethod) don't receive an implicit/automatic `self` instance argument like regular class methods do. I'm not totally sure yet, but it seems like that fact alone might be enough to prevent your question from making a much sense even its in updated form. – martineau Sep 18 '18 at 15:50
  • No, the purpose of placing self in the static method is it receives it from the method property, this inherits it allowing me to make another argument named self which is from the outer class. – IllustriousMagenta Sep 18 '18 at 20:57

3 Answers3

1

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.

Evgeny
  • 4,173
  • 2
  • 19
  • 39
  • I actually have a class called `Inbox` - I am not using `Users`. Your code, btw is incorrect and is not valid (you need to have a self in def get()) and it is not passing self anywhere around it. – IllustriousMagenta Sep 16 '18 at 02:31
  • The things you point out are an issue with the GMAIL API. I'm not going against all style guides just to keep with it, I just wanted to provide some rough context for the question to avoid the XY problem - I just wanted the attributes. – IllustriousMagenta Sep 16 '18 at 02:36
  • @Modelmat, please read through the lenses of the word 'scetch'. It is intended to give you idea of what needs to be done - place \_\_init\_\_ and self accordingly. – Evgeny Sep 16 '18 at 02:39
  • I can't find any meaning for the word 'scetch' on google. Your answer does not provide anything useful at all, as said previously I **do not** wish to do it this way - I've tried doing it, and it doesn't do what I want. – IllustriousMagenta Sep 16 '18 at 02:41
  • 1
    Sure, stick with code you have, it is quite an artefact. – Evgeny Sep 16 '18 at 02:46
  • Also try spotting the difference between this answer and @Jack Taylor, which is _a better way_ – Evgeny Sep 16 '18 at 02:51
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/180132/discussion-between-modelmat-and-epo). – IllustriousMagenta Sep 16 '18 at 02:52
1

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.

Jack Taylor
  • 5,588
  • 19
  • 35
  • You are likely to instantinate messages as an attribute in Test, not a property. Propery is too much sugar for this, and more impotantly you also might want to save changes somewhere, eg for settings. Property will not allow this. – Evgeny Sep 16 '18 at 02:16
  • While this does provide a better way of doing it, this question was actually asking if it was possible and if so how. I know that I should do it in a different way (and I probably will), but this was a project to try some interesting python features. – IllustriousMagenta Sep 16 '18 at 02:37
  • 1
    @Modelmat, you are kind of wasting other people time by changing your mind and focus in the middle of poorly formulated question. – Evgeny Sep 16 '18 at 02:43
  • Look, I asked [this](https://i.imgur.com/d2XsDqx.png), not if there was a better way to organise my code. – IllustriousMagenta Sep 16 '18 at 02:45
  • 1
    @Modelmat, double check if Stack Overflow is really for you. – Evgeny Sep 16 '18 at 03:02
  • 1
    @Modelmat's question is a reasonable one, though, and it hasn't really been answered yet. It does deserve a straight answer, in my opinion. (Btw, the quick answer is "Yes, I think you can, although not many people use it and I would have to look up how to do it.") – Jack Taylor Sep 16 '18 at 03:27
  • I'm going to mark this as the answer as I have ended up doing it this way; I still would like to see a way of doing it though. – IllustriousMagenta Nov 07 '18 at 08:06
0

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.

R.R.C.
  • 5,439
  • 3
  • 14
  • 19
  • this doesn't meet any of the requirements in the original question and has no relevance whatsoever. The question linked is also not relevant, and has the same issues as the other two answers. – IllustriousMagenta Sep 16 '18 at 04:35
  • @Modelmat, you din't receive anymore answers and you got some downvote. Just to compesate that, I tried one more time to give you a good answer. See if that helps you out somehow. Good luck! – R.R.C. Sep 18 '18 at 04:08
  • While I appreciate the attempt, inheritance isn't what I wanted either. I wish to be able to use `foo.method.get`. This doesn't solve this. – IllustriousMagenta Sep 18 '18 at 06:02