You mentioned trying both global variables and functools.partial (which works a lot like a lambda function), and neither worked.
I believe the same methods you tried would work if you used both of those together.
Here is an example where I've done that (I used lambda but if you prefer partial it should be an easy substitution):
class TelegramClient:
def __init__(self, name):
self.name = name
def showProofOfWorking(self):
print("--- SUCCESS! This is a TelegramClient named " + self.name)
# In order to avoid namespace errors, there must be a reference to the clients in global scope.
# However, the value of client1 doesn't need to be set until 'main', as will be demonstrated in the
# first of the three methods tested in this example code.
globalVars = {'client1': None, 'client2': None}
# For comparison purposes. Compare how this globally scoped client reference acts relative to the function-scoped 'localClient' inside 'main'
otherGlobalClient = TelegramClient("OTHER CLIENT IN GLOBAL SCOPE")
def refersToGlobalClient(client = globalVars['client1']):
client.showProofOfWorking()
def refersToGlobalWrappedClient(clientWrapper = lambda: globalVars['client1']):
unwrappedClient = clientWrapper()
unwrappedClient.showProofOfWorking()
def refersToLocalWrappedClient(clientWrapper = lambda: client1):
unwrappedClient = clientWrapper()
unwrappedClient.showProofOfWorking()
def main():
client1 = TelegramClient('CLIENT 1')
client2 = TelegramClient('CLIENT 2')
# No references to this from the global scope: it only exists inside 'main'
otherLocalClient = TelegramClient('CLIENT ONLY IN LOCAL SCOPE')
# Only 'client1' will be put into global scope. 'client2' is not defined outside the scope of 'main'.
globalVars['client1'] = client1
# ----- This is the only one of the three different methods tried here that works. -----
# It combines a global variable with a lambda function, which changes the reference of the wrapped global variable from a compile-time value to a runtime value,
# circumventing the fact that at compile time, the value of the global variable will be None as it will not be set to its final value until inside 'main'.
print("FIRST METHOD: Global Scope plus Lambda Wrapper: Works in All Cases")
print("Inside main(), we will now call 'refersToGlobalWrappedClient' with its default argument, which is a function that returns the current value of 'globalVars['client1']'")
refersToGlobalWrappedClient()
print("\nNow we'll call the same function with a different argument from global scope.")
refersToGlobalWrappedClient(lambda: otherGlobalClient)
print("\nNow we'll call the same function with a different argument from local scope.")
refersToGlobalWrappedClient(lambda: otherLocalClient)
print("\n__________________________\n")
print("SECOND METHOD: Global Scope with No Wrapper: Works With Arguments, Does Not Work With Default Parameter")
print("Inside main(), we will now attempt to call 'refersToGlobalClient' with its default argument, which is 'globalVars['client1']'")
try:
refersToGlobalClient()
except Exception as error:
print("\nAN EXCEPTION OCCURRED! Exception:")
print(error)
print("\nNow we'll call the same function with a different argument from global scope.")
refersToGlobalClient(otherGlobalClient)
print("\nNow we'll call the same function with a different argument from local scope.")
refersToGlobalClient(otherLocalClient)
print("\n__________________________\n")
print("THIRD METHOD: Local Scope with Lambda Wrapper: ")
print("Inside main(), we will now attempt to call 'refersToLocalClient' with its default argument, which is a function that returns 'client1'")
try:
refersToLocalWrappedClient()
except Exception as error:
print("\nAN EXCEPTION OCCURRED! Exception:")
print(error)
print("\nNow we'll call the same function with a different argument from global scope.")
refersToLocalWrappedClient(lambda: otherGlobalClient)
print("\nNow we'll call the same function with a different argument from local scope.")
refersToLocalWrappedClient(lambda: otherLocalClient)
print("\n__________________________\n")
main()
Here, I tried three different methods. Like you said, only adding a reference to client1 in global scope, or only wrapping the reference to the not-yet-defined client1 inside a function, do not work and cause errors when you try to use client1 as a default argument.
But, if you both set up a reference to your client1 in global scope (here, it's inside a dict called globalVars) and wrap the default client1 parameter inside a function, you can call your functions successfully from inside main() with the default parameter (client1) or with any other TelegramClient as an argument.
The global scoped reference is necessary to avoid the namespace error name 'client1' is not defined.
The lambda wrapper is necessary to avoid the error you would get from the value of the global reference not being set until main() runs (at compile-time when the default parameters are evaluated, it will be None and you'd get the error 'NoneType' object has no attribute 'showProofOfWorking').