David
David

Reputation: 5016

Python design patterns, cross importing

I am using Python for automating a complex procedure that has few options. I want to have the following structure in python. - One "flow-class" containing the flow - One helper class that contains a lot of "black boxes" (functions that do not often get changed).

99% of the time, I modify things in the flow-class so I only want code there that is often modified so I do not have to scroll around a lot to find the code I want to modify. This class also contains global variables (configuration settings) that often get changed. The helper class contains global variables that not often get changed.

In the flow-class I have a global variable that I want the user to be forced to input at every run. The line looks like this. print ("Would you like to see debug output (enter = no)? ") debug = getUserInput()

The getUserInput() function should be located in the helper class as it is never modified. The getUserInput needs a global variable from the flow class, which indicates whether the user input should be consistent with Linux command line or Eclipse (running on Windows).

My question is: How can I structure this in the best way? Currently it looks like the following: The flow-class:

import helper_class

isLinux = 1

debug = getUserInput()

The helper-class:

import os, flow_class

def getUserInput():
    userInput = input ()
    if (flow_class.isLinux == 1):
        userInput = userInput[:-1]
    return userInput

This currently gives me the following error due to the cross importing:

Traceback (most recent call last):
  File "flow_class.py", line 1, in <module>
    import helper_class
  File "helper_class.py", line 1, in <module>
    import os, flow_class
  File "flow_class.py", line 5, in <module>
    debug = getUserInput()
NameError: name 'getUserInput' is not defined

I know that I could obviously solve this by always passing isLinux as a parameter to getUserInput, but this complicates the usage of this method and makes it less intuitive.

Upvotes: 1

Views: 1315

Answers (3)

Jon W
Jon W

Reputation: 15806

I know that I could obviously solve this by always passing isLinux as a parameter to getUserInput, but this complicates the usage of this method and makes it less intuitive.

Actually using global variables complicates the usage of this program waaaay more than a simple parameter.

try something like:

debug = getUserInput(isLinux=True)

Here's some other suggestions

  • You mention there are lots of parameters that you'll change often. Should these be hard coded? Try using a configuration file, or passing a dict() from 'flow' as a parameter. That way you have a central place to change common variables without having to dive in!
  • your 'flow/helper' class sounds like a Controller/Model paradigm. This is good. But your model shouldn't have to import your controller.

These aren't suggestions specific to 'pythonic style' these are general programming practices. If you're concerned about program design try reading The Pragmatic Programmer, they have great tips for workflow and design. There's also Code Complete which Roberto suggested.

Upvotes: 1

SilentGhost
SilentGhost

Reputation: 319601

you need to do helper_class.getUserIinput() in your flow_class. It's not about cross-importing. Once it's fixed you'll get AttributeError that is indeed related to cross-importing.

At this stage you'll need to implement logic of getting getUserInput defined before importing flow_class.

And to comment on your last statement: your assumption is not correct. Code would be much clearer if you use explicit local values.

Upvotes: 2

rob
rob

Reputation: 37644

I would like to question you on your last sentence.

Usually, as also outlined in CC2, usage of global variables helps in writing code, but not in reading it.
And code is read many more times than it is written; in your case, I understand you modify the same script over and over again.

The problem you are facing now is just a consequence of the generic design decision to use global variables.
Explicit parameter passing would make it much clearer, and easier to maintain.

As said in the the Zen of Python, explicit is better than implicit.

Upvotes: 1

Related Questions