Reading a series of input / output in Python
For my app, I ne开发者_运维技巧ed to print out a series of outputs and then accepts inputs from the user. What would be the best way of doing this?
Like:
print '1'
x = raw_input()
print '2'
y = raw_input()
Something like this, but it would go on for at least 10 times. My only concern with doing the above is that it would make up for poor code readability.
How should I do it? Should I create a function like this:
def printOut(string):
print string
Or is there a better way?
First one note: raw_input()
takes an optional argument ... a prompt string.
Regarding the broader question, a simplistic approach would be to create a class which defines the elements of your form and provides the functions for their input, validation, and later manipulations or output.
With such a class instances can be created (instantiated), and collected, stored, etc.
Such an approach need not any more complicated than something like:
#!/usr/bin/python
# I use /usr/bin/env python; but making SO's syntax highlighter happy.
class generic_form:
def __init__(self, element_list):
self.form_elements = element_list
self.contents= dict()
def fill_it_in(self):
for prompt in self.form_elements:
self.contents[prompt] = raw_input(prompt)
def get(self, item):
return self.contents[item]
def print_it(self):
for each in self.form_elements:
print each, self.contents[each]
if __name__ == '__main__':
sample_fields = ("Given Name: ",
"Surname: ",
"Date of Birth: ",
"Notes: ")
example = generic_form(sample_fields)
print "Fill in my form:"
example.fill_it_in()
print
print "Please review your input:"
example.print_it()
# store(:%s, %s: %s" % (example.get('Surname: '), \
# example.get('Given Name: '), example.get('Notes: '))
The main code is only a dozen lines long to define a generic form class with input
and output functionality (and a simple get()
method for further illustrative purposes).
The rest of this example simply creates an instance and shows how it could be used.
Because my generic_form
class is generic, we have to supply a list of field names which are to be filled in. The names are used as both the names of the fields for later access (see the get()
method for an example). Personally I wouldn't do it this way, I'd provide a list of short field names and prompts similar to Marcelo's example. However, I wanted this particular example to be a short as possible to get the main point across.
(The comment at the end would be a call to a hypothetical "store()" function to store this for posterity, by the way).
This is the most minimal approach. However, you'd rapidly find that it's far more useful to have a richer class with validation for each field, and separate classes which format and output instances of that in different ways, and different classes for input. "teletype" input (as provided by the Python raw_input()
built-in function) is the crudest form (primarily useful for simplicity and for the ability to process files using shell redirection). One could also support input with the GNU readline
support (already included as a standard library in Python), curses support (also included), and one could imagine writing some HTML wrapper and CGI code for handling web-based input.
Coupling "raw_input()
" and "print
" into our class would mean more work if we ever needed or wanted to support any forms of input or output other than "dumb terminal."
If we create a class which only concerns itself with the data to be collected, then it could provide an interface for any other input class to get the list of the prompts with references to "setter
" functions (and perhaps a "required" or "optional" flag). Then any instance of any input class could request the list of desired/required inputs for any form ... present the prompts, call the "setter
" methods (which return a boolean to indicate if the data supplied was valid), loop over bad inputs on "required" fields, offer to skip "optional" fields, and so on.
Notice that the logic for displaying prompts, accepting responses, relaying those back to the data object via their setter methods, and handling invalid inputs and be the same for many types of forms. All we need is a way for the form to provide the list of prompts and their corresponding validation functions (and we need to ensure that all these validation functions have the same semantics --- taking the same parameters and so on).
Here's an example of separating the input behavior from the storage and validation of the data fields:
#!/usr/bin/env python
class generic_form:
def __init__(self, element_list):
self.hints = list()
for each in element_list:
self.hints.append((each, each, self.store))
self.contents= dict()
def store(self, key, data):
'''Called by client instances
'''
self.contents[key] = data
return True
def get_hints(self):
return self.hints
def get(self, item):
return self.contents[item]
def form_input(form):
for each, key, fn in form.get_hints():
while True:
if fn(key,raw_input(each)):
break
else:
keep_trying = raw_input("Try again:")
if keep_trying.lower() in ['n', 'no', 'naw']:
break
if __name__ == '__main__':
sample_fields = ("Given Name: ",
"Surname: ",
"Date of Birth: ",
"etc: ")
example = generic_form(sample_fields)
print "Fill in my form:"
form_input(example)
print
print "Please review your input:"
for i, x, x in example.get_hints():
print example.get(i),
In this case the extra complication is not doing anything useful. Our generic_form performs no validation. However, this same input function could be used with any data/form class that provided the same interface. That interface, in this example, only requires a get_hints()
method providing tuples of "prompt string", storage key, and storage function references, and a store()
method which must return "True" or "False" and take arguments for the key and data to be stored.
The fact that our storage key is passed to our input "client" as an opaque item that must be passed back through its calls to our store()
method is a bit subtle; but it allows us to use any single validation function for multiple form elements ... all names can be any string, all dates must pass some call to time.strftime()
or some third party parser ... and so on.
The main point is that I can create better forms classes which implement data validation methods as appropriate to the data being gathered and stored. The input example will work for our original dumb forms, but it will work better with forms that return meaningful results from our calls to store()
(A better interface between forms and input handling might supply "error" and "help" prompts as well as the simple short "input" prompt we show here. A more complex system might pass "datum" objects through the get_hints()
methods. That would require that the forms class instantiate such objects and store a list of them instead of the tuples I'm showing here).
Another benefit is that I can also write other input functions (or classes which implement such functions) that can also use this same interface to any form. Thus I could write some HTML rendering and CGI processing which could use all of the forms that had developed with no changes to my data validation semantics.
(In this example I'm using the get_hints()
method as hints for my crude output function as well as my inputs. I'm only doing this to keep the example simple. In practice I'd want to separate input hinting from output handling).
If you are reading in several fields, you might want to do something like this:
field_defs = [
('name', 'Name'),
('dob' , 'Date of Birth'),
('sex' , 'Gender'),
#...
]
# Figure out the widest description.
maxlen = max(len(descr) for (name, descr) in field_defs)
fields = {}
for (name, descr) in field_defs:
# Pad to the widest description.
print '%-*s:' % (maxlen, descr),
fields[name] = raw_input()
# You should access the fields directly from the fields variable.
# But if you really want to access the fields as local variables...
locals().update(fields)
print name, dob, sex
"10 times... poor code readability"
Not really. You'll have to provide something more complex than that.
20 lines of code is hardly a problem. You can easily write more than 20 lines of code trying to save yourself from simply writing 20 lines of code.
You should, also, read the description of raw_input
. http://docs.python.org/library/functions.html#raw_input
It writes a prompt. Your four lines of code is really
x = raw_input( '1' )
y = raw_input( '2' )
You can't simplify this much more.
精彩评论