Reputation: 515
i have a class in Python which shall perform a remote connection to a server. Because there are several protocols (TCP/IP, HTTP) to access the same data which can all be used depending on the situation (performance issues,...), I would like to use the class as a decorator for different functions which encapsulate the implementation.
My class looks like this:
class RemoteConnection(object):
def __init__(self, function):
self.function = function
self.results = None
def __call__(self, *args):
self.function(self, *args)
def setResults(*args):
self.results = args
@RemoteConnection
def performTCPConnection(instance, username, password):
#...perform connection
instance.setResults(results) #set the results of the connection (a query)
return instance
@RemoteConnection
def performHTTPConnection(instance, username, password):
#...perform connection
#and so on
if __name__ == '__main__':
performTCPConnection("admin", "admin") #OR
#performHTTPConnection("admin, "admin")
The parameters (more than username and passwords, these are only for example) later shall be given to the programm by using argparse
or something else.
If I call the programm like this, the __init__()
-method is called two times because of the decorator. Also, the performTCPConnection
-function is called and before the __call__
-method is called. What I want now is that the instance of the RemoteConnection class is returned by the performTCPConnection
-function to a variable in the main-function.
If i do something like the following:
foo = perfprmTCPConnection("admin","admin")
print foo
None is printed to StdOut.
If i try to access the instance in the performTCPConnection-function like that:
@RemoteConnection
def performTCPConnection(instance, username, password):
#...perform connection
instance.setResults(results) #set the results of the connection (a query)
print instance
return instance
something like <__main__.ServerConnection object at 0x7f4803516fd0>
is written to StdOut.
My idea is a bit driven from the Java-world like implementing a Strategy-Pattern.
Unfortunately, I didn't find a good explanation of the explained behaviour until now. Is this also a good design approach?
Upvotes: 1
Views: 85
Reputation: 50220
I'd definitely say this is not good design. It's a clever way to effectively get inheritance without using the inheritance mechanism, but it's non-standard, unintuitive, and unnecessary since there are more direct ways to do all this.
The answer of @zmo shows how to properly set up your derived classes. But I wouldn't even do this: Consider separating the connector objects from the class itself, and setting up your class signature like this:
class RemoteConnection(object):
def __init__(self, connector):
self.connection = connector
Then you simply instantiate it with a reference to the right connector:
conn = RemoteConnection(TCPConnector(user, password))
This way RemoteConnection
encapsulates the protocol-independent functions, and the protocol-dependent parts are independent functions or objects (which could be derived from a common class if you wish). If the connector will be needed during the life of the connection, you're saving the connector object so you can call it later. If not, then TCPConnection
could just return the results
value you have now.
Upvotes: 2
Reputation: 24802
If I'm following what you wrote, I believe you're over-engineering this. Basically, your strategy using the decorator would be strictly equivalent to:
def performTCPConnection(username, password):
instance = RemoteConnection()
#...perform connection
instance.setResults(results) #set the results of the connection (a query)
return instance
but reading what you want, you're still not there yet. Because if I understand the purpose of the RemoteConnection
class in your pattern, it's to defer the connection at a time when you'll call the .function()
method.
So what you'd want is actually:
def prepareTCPConnection(username, password):
rc = RemoteConnection()
def connection_handler():
# perform the connection, and setup results
rc.setResults(results)
rc.function = connection_handler
return rc
you'd have to adapt RemoteConnection
appropriately, obviously.
Here, what you do is take advantage of inner function (and closure) to define a function to be called at a later convenient point in your program flow.
But to be honest, you can achieve the same in a way simpler fashion using simple subclassing:
class PerformConnection():
def __init__(self, username, password):
self.username = username
self.password = password
def perform(self):
raise NotImplementedError
class PerformConnectionTCP(PerformConnection):
def perform(self):
# do what you got to do here...
return results
then in your main you can do:
if __name__ == "__main__":
instance = PerformConnectionTCP('admin', 'admin')
instance.perform()
Upvotes: 1
Reputation: 515
I found the error, i forgot to return the result of the decorated function in the call()-method.
This solved the problem:
class RemoteConnection(object):
def __init__(self, function):
self.function = function
self.results = None
def __call__(self, *args):
return self.function(self, *args) #here was the problem!
def setResults(*args):
self.results = args
Upvotes: 0