Search code examples
pythondesign-patternsdependency-injectionglobal-variablesproxy-classes

When using the proxy pattern in Python, how can proxy classes access state in the calling object?


In the following code, Graph() is acting as a proxy to Vertex and Edge -- clients only access Vertex and Edge through Graph():

from rest import Resource
from elements import Vertex, Edge

class Graph(object):
    def __init__(self,db_url):
        self.resource = Resource(db_url)
        self.vertices = Vertex
        self.edges = Edge

g1 = Graph('http://localhost/one')   
g2 = Graph('http://localhost/two')

What are the best ways for Vertex and Edge to access the resource object, without having to pass it as a param to Vertex and Edge?

One of the reasons I don't want to pass it as a param is because Vertex and Edge have classmethods, such as create(), that need access to the resource object too.

Flask/Werkzeug uses "context locals" (http://werkzeug.pocoo.org/docs/local/) -- is that the right approach here, or is there a better way?


Solution

  • If your resource object is unique, could you make it a singleton? The fact that you want to use it from a class method makes me think that it's probably the case. If its only purpose is to provide the database connection, could you consider using a connection pool?

    If you still need to pass it to your classes, you can simply assign it to class attributes.

    class Vertex(object):
        @classmethod
        def foo(cls):
            print cls.resource
    
    Vertex.resource = 'something'
    v = Vertex()
    v.foo()
    

    This can also be done in __init__:

    class Vertex(object):
    
        def __init__(self, resource):
            if not hasattr(self.__class__, 'resource'):
                self.__class__.resource = resource
    
        @classmethod
        def foo(cls):
            print cls.resource
    
    resource = 'some resource'
    v = Vertex(resource)
    v.foo()
    

    But really my intuition is that you should look into using a singleton, which in many cases can be implemented in Python simply as a module.

    Finally if I can make a couple of remarks about your code, I find it confusing that you're assigning classes to plural variable names. When I see self.edges I would expect a collection or an iterable, not a class. I also wonder why you would want a class method called create. What does it do that __init__ cannot do?