Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: retain instance

Working with PRAW, in my main thread I've created a Reddit instance:

import praw

reddit = praw.Reddit(client_id='my client id',
                     client_secret='my client secret',
                     user_agent='my user agent')

Which works fine.

As the code grows, I've created various modules (.py files) which the main .py file where main is imports them into.

There are times where these other modules need to access the above instance instead of creating a new one that would just waste resources or API rate limits.

In Python 3.6, what's the appropriate way to handle this? Are global variables just the answer here? It also does not seem very organized to pass this as a function argument dozens of times deep if there are a lot of functions being called.

like image 315
Zeno Avatar asked Jul 07 '19 03:07

Zeno


2 Answers

You can put that code in its own module called reddit

reddit.py:

import praw

reddit = praw.Reddit(client_id='my client id',
                     client_secret='my client secret',
                     user_agent='my user agent')

And then use it like this.

some_other_module.py

import reddit

for submission in reddit.reddit.subreddit('learnpython').hot(limit=10):
    print(submission.title)

Python will only run through the code in the module the first time it is imported, and then it keeps the module around internally so that on future imports the same module is referenced.

A little example you can do to see this is to create the following modules.

a.py

import b # j.x is now 4
import c # j.x is now 9
import j # j.x is still 9
print(j.x) # 9 is printed out

j.py

x = 1

b.py

import j
j.x += 3

c.py

import j
j.x += 5

The number 9 will be printed out because x was only set to 1 the very first time it was imported. Future references to the module where all using the same x.

In your case you could have main.py and tdepend.py import reddit.py, and they would both be using the exact same reddit object.


FYI, you can use the following to see how close you are to reaching Reddit's API limits: praw.models.Auth(reddit).limits().

like image 136
hostingutilities.com Avatar answered Sep 28 '22 22:09

hostingutilities.com


I had a similar problem in a project that I worked on, we ended up with 3 possible scenario:

Approach 1

In our case we, after some research, brainstorming etc., decided to solve it out through class and I've to say it's working pretty well. The use of classes in python has a LOT of advantages in these situations.

Let's say you have your tdepend.py that defines a function caller() that you need your reddit instance in it :

def caller( a ) :
    print( a.reddit )

then define a class and define reddit as an attribute of the class, so it will be accessible from everywhere the class object is accessed. It's to say, you can also re-think of your main.py script as a class object and all the variables as its attributes, as example:

import praw
from tdepend import caller

class MyClass() :
     def __init__(self, name):
         self.name = name
         self.reddit = praw.Reddit(client_id='my client id',
                                    client_secret='my client secret',
                                    user_agent='my user agent')
        ### define bunch of other stuffs

a = MyClass( "a" )

caller( a )
### bunch of other functions on "a" object

this solution also allows you to define the class in another script and simply import it, with reddit and everything else you need in it already configured and just operate functions defined elsewhere on its objects (and believe me, it makes the organization and future needs to modify the code and remember where you defined everything a lot easier). It also allows you to pass a LOT of argument passing just one object and to reuse the same kind of attributes in different objects etc. I'm really happy with that.

Approach 2

If you don't wanna to go through class you can also, for this particular situation, pass it as an argument (function( reddit )) not only to avoid globals() related-risks but also to make your code more readable when you will access it in future (e.g. when you will have to retrieve where the hell reddit came from when reading one of these "dependency" file).

Approach 3

If for some reason you really don't want neither to pass it newer as argument nor pass a class object as argument (that allows you to pass in a single argument a bunch of other objects) you have to use globals() but be well aware of the problems related with global variables and especially the future readability.

Hope it helps!

EDITED on your comments:

With classes you can do exactly what you asked: with this approach you make the API call when you create the a object and that's all, no need to recall it, as you can see it remains the same (for the same object).

### main.py
caller(a)
<praw.reddit.Reddit object at 0x7f74d89a1470>

# the same
a.reddit
<praw.reddit.Reddit object at 0x7f74d89a1470>

### if you create another object of course it will be a different one
b = MyClass( "b" )
caller(b)
<praw.reddit.Reddit object at 0x7f74bf60bc88>

and if you have some function that need to acces the reddit element you created in MyClass() in tdepend.py within tdepend.py only...just define them inside the class:

tdependclass.py

import praw

class MyClass() :
    def __init__(self, name):
        self.name = name
        self.reddit = praw.Reddit(client_id='my client id',
                                client_secret='my client secret',
                                user_agent='my user agent')
        ### if you want to be called at the initiation of the object
        self.redditStringized = str( self.reddit ) + " hello!"
    ### if you want to be able to decide when calling the function from main
    def stringizeReddit( self, string ) :
        return str( self.reddit ) + " " + string
    ### ... define a bunch of other stuffs and functions ...

main.py :

from tdependclass import MyClass
from tdepend import caller

a = MyClass( "a" )

caller( a )
<praw.reddit.Reddit object at 0x7f00c787d668>

a.reddit
<praw.reddit.Reddit object at 0x7f00c787d668>

### called here
a.stringizeReddit( "hello!" )
'<praw.reddit.Reddit object at 0x7f00c787d668> hello!'

### defined at initiation of object
a.redditStringized
'<praw.reddit.Reddit object at 0x7f00c787d668> hello!'
like image 31
cccnrc Avatar answered Sep 28 '22 22:09

cccnrc