LegendaryJLD
LegendaryJLD

Reputation: 148

Referencing parent object instance from inner class

I'm trying to create a syntax friendly library that doesn't require the user to constantly pass in credentials or type redundant or excessive statements. My strategy is to focus on how I intend the library syntax to look, and then design the library to function in that way. I know I can implement the following:

api = Api(user="admin", pswd="blahblah")

new_order = api.Order()
new_order.status = "pending"
api.create(new_order)
api.order_assign_user(new_order, user)

existing_order = api.get_order(orderId=12345)
existing_order.status = "shipped"
api.update(existing_order)

using something like the following:

class Api(object):
    def __init__(self, user=None, pswd=None):
        self.user = user
        self.pswd = pswd

    class Order(api):
        def __init__ (self, status=None):
            self.status = status

    def create(self, x):
        auth = Auth(self.user, self.pswd)
        # use authorization credentials to create data remotely
        return x

    def update(self, x):
        auth = Auth(self.user, self.pswd)
        # use authorization credentials to update data from remote
        return x

    def get_order(self, orderId=None):
        auth = Auth(self.user, self.pswd)
        # use authorization credentials to update data from remote
        return order

But I'd like to be able to use the following statements:

new_order.create() # instead of api.create(new_order)

new_order.assign_user(user) # instead of api.order_assign_user(new_order, user)

existing_order = api.Order.get(orderId=12345) # returns retrieved Order Instance

Which presents me with a problem:

How would I get the Order() instance to access attributes of the Api() instance that created it? Without access to these, any attributes like 'user' and 'pswd' would be inaccessible (they're needed for requests.get() calls)

I've tried variations of functions and classes to accomplish this, but could never solve this problem. This is the closest I've been able to achieve:

class Api(object):
    def __init__(self, user=None, pswd=None):
        self.user = user
        self.pswd = pswd

    class Order(api):
        def __init__ (self, status=None):
            self.status = status

        @classmethod
        def get(cls, value):
            return cls(status=value)

        def create(self):
            auth = Auth(self.user, self.pswd) 
            # these credentials need to come from the Api() instance?, not self
            return x

        def update(self):
            auth = Auth(self.user, self.pswd) 
            # these credentials need to come from the Api() instance?, not self
            return x

Is this possible? Or am I going about this the wrong way? I've considered making this into a module but that didn't seem to be a valid option either.

Upvotes: 0

Views: 62

Answers (2)

Dalvenjia
Dalvenjia

Reputation: 2033

This demonstrates a basic implementation of what I think you want to do, I found this better than the __get__, __set__ hackery that I had in mind when I commented your question

import requests

class Order:
    def __init__(self, user:str, passwd: str):
        self.session = requests.Session()
        self.session.auth = (user, passwd)
        self.status = None

    # This is only to make the class callable, since the class is
    # already instantiated in Api init calling it again would raise an exception
    def __call__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        return self

    def get(self):
        response = self.session.get('https://httpbin.org/basic-auth/my_user/my_pass')
        print(response.text)

    def post(self):
        response = self.session.post('https://httpbin.org/post', data={'status': self.status})
        print(response.text)


class Api:
    def __init__(self, user: str, passwd: str):
        self.Order = Order(user, passwd)

Upvotes: 1

Mikhail Burshteyn
Mikhail Burshteyn

Reputation: 5012

Inner classes in Python are just separate classes, and their instances are not in any way automatically associated with an instance of an outer class.

I would suggest a solution in which the constructor of Order would take an Api instance as an argument and work with that instance:

class Api(object):
    def __init__(self, user=None, pswd=None):
        self.user = user
        self.pswd = pswd

class Order(object):
    def __init__(self, api, status=None):
        self.api = api
        self.status = status

    @classmethod
    def get(cls, api, value):
        return cls(api, status=value)

    def create(self):
        auth = Auth(self.api.user, self.api.pswd)
        return x

    def update(self):
        auth = Auth(self.api.user, self.api.pswd)
        return x

Upvotes: 1

Related Questions