Python Programming
 
Forums: » Register « |  User CP |  Games |  Calendar |  Members |  FAQs |  Sitemap |  Support | 
User Name:
Password:
Remember me
Go Back   Dev Shed ForumsProgramming LanguagesPython Programming
Receive the tools necessary to be the rock star of your field. Our 12-month program teaches you the evolving world of multi-channel marketing as well as the complex issues and opportunities found in the industry.

ASP Free and Iron Speed Designer are giving away $5,500+ in FREE licenses. Iron Speed's RAD CASE toolset can save up to 80% of your coding time. One free license per week, one perpetual license per month!
Download and Activate to enter!

Web development can be a daunting task, even for specialists. There is a lot of information to absorb and a lot of technologies to learn in order to manage a superior website. When trying to learn the ropes, developers need a reliable source to introduce new ideas that can be easily implemented. When working on large projects, even web veterans may run into a technology or an aspect of a technology that they are unfamiliar with.

Learn More!


Download to Enter
| Contest Rules

Tutorials | Forums

Reply
Add This Thread To:
  Del.icio.us   Digg   Google   Spurl   Blink   Furl   Simpy   Y! MyWeb 
Thread Tools Search this Thread Rate Thread Display Modes
 
Unread Dev Shed Forums Sponsor:
  #1  
Old January 29th, 2012, 03:44 PM
jnever1 jnever1 is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Location: MI
Posts: 6 jnever1 User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 3 h 33 m 12 sec
Reputation Power: 0
Limited looping

I'm afraid I don't know how to phrase my topic any more clearly. As a result, my searching efforts have remained fruitless. If this topic has been addressed elsewhere, please simply post a link and consider the topic resolved.

What I would like to do is provide a fail-out for a password input much like any decent web site does. The user has X chances to input a correct password or else the system kicks the user.

My scenario is a mock bank transaction. User names and ID numbers have been pre-defined as globals with lists.

main() makes the following function call:
validate_customer_ID(customer_ID, customer_index, customer_name)

which refers to the following definition:
Code:
# Verify ID entered is correct for the given customer name
##
## This function compares int ID against ID in CUSTOMER_ID at an index retrieved
## previously from CUSTOMER_NAMES (see main()). If ID at index does not match,
## user is prompted with invalid statement and to re-enter.
##
def validate_customer_ID(ID_number, index, name):
    print('\nValidating '+name+"'s Customer ID...")
    i = 3
    while ID_number != CUSTOMER_ID[index]:
        if i == 0:
            ID_number = False
            return ID_number
        else:
            print('\n'+name+"'s Customer ID number is not \""+str(ID_number)+
                '\".\n',sep='')
            print(i,'entries remaining')
            ID_number = get_an_int('Please enter '+name+
                "'s Customer ID number: ")
            print('\nValidating '+name+"'s Customer ID...")
            i -= 1
    print('Success!\n')
    return ID_number

Which is followed by...
Code:
    if not customer_ID:
        print('Maximum attempts exceeded.')
        break

...in main() that is meant to jump out of main() and end the program.

The problem is that returning ID_number = False from the if statement doesn't seem to change customer_ID. Returning ID_number = input(...) from the while statement does...
(i've tested using various print statements that have proven that the "= False" isn't sticking)

If I force customer_ID = validate_customer_ID(customer_ID, customer_name, customer_index) then everything is good.

I am happy that it seems to work, but unhappy that I had to explicitly state what I understand to be implied.

Can someone walk me through the logic that is the root of this distinction?

I can post the whole program if necessary, but I have 250 lines of code, fully commented.

Last edited by jnever1 : January 31st, 2012 at 07:03 PM. Reason: figured out how to properly format code in forum ^^

Reply With Quote
  #2  
Old January 29th, 2012, 09:13 PM
b49P23TIvg's Avatar
b49P23TIvg b49P23TIvg is offline
Contributing User
Click here for more information.
 
Join Date: Aug 2011
Posts: 1,075 b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level) 
Time spent in forums: 4 Weeks 1 Day 4 h 41 m 27 sec
Reputation Power: 98
Two ways to get information directly from a python function:
* Use the function return value.
* Pass a mutable object in the argument list.

(Indirectly your function can change global data, class attributes,
files, and all sorts of bits about the external state of the world.)

Code:
# using the function return value

def f():
    return 'The alphabet'

alph = f()

# then everything is good.
customer_ID = validate_customer_ID(customer_ID, customer_name, customer_index)



# using a mutable object passed as an argument

def g(d):
    d['Frozen food'] = 'Peas'

grocery_trip = dict()
g(grocery_trip)
print(grocery_trip)

Reply With Quote
  #3  
Old January 31st, 2012, 09:00 AM
jnever1 jnever1 is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Location: MI
Posts: 6 jnever1 User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 3 h 33 m 12 sec
Reputation Power: 0
Quote:
Originally Posted by b49P23TIvg
Two ways to get information directly from a python function:


I appreciate your reply, b49P23TIvg.

However something remains unclear to me:

Before I attempted to implement a countdown, I was able to pass ID_result as an INT out of the function and into main() without explicitly stating customer_ID = f(stuff). As a matter of debugging, I found that the last INT value of ID_result was passed to main() regardless of establishing ID_result = False, in the case of i == 0.

Is the True/False logic repressed by literals/numericals?

Am I correct in my understanding that literals may be passed but not logicals? In question form: Why can I not pass ID_result = False from the function to main() once a numerical value has been established?

Thank you for your time.

Reply With Quote
  #4  
Old January 31st, 2012, 10:19 AM
b49P23TIvg's Avatar
b49P23TIvg b49P23TIvg is offline
Contributing User
Click here for more information.
 
Join Date: Aug 2011
Posts: 1,075 b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level) 
Time spent in forums: 4 Weeks 1 Day 4 h 41 m 27 sec
Reputation Power: 98
Quote:
Am I correct in my understanding that literals may be passed but not logicals?

Incorrect.

Numbers, strings, lists, dictionaries, ... These are all objects. Python passes objects.

I think that you're victim to a mistaken, inconclusive, or incomplete observation. Please post a complete program demonstrating the situation and tell your expectation. You might need to post 2 codes.

Meanwhile---see if this helps your understanding:


Code:
>>> d={}      # create a mutable object
>>> print(id(d))
41125472
>>> d[8]=32   # change the state of the object.
>>> print(id(d))  # id is unchanged.  d refers to the same object
41125472
>>> 
>>> 
>>> s='a non-mutable object'
>>> print(id(s))
42924720
>>> s[2]='X'   # illegal
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> print(id(s))
42924720
>>> s='another string'
>>> print(id(s))  # s refers to a different object
42925000
>>> def f(variable_local_to_f):
...     print(id(variable_local_to_f))
...     variable_local_to_f = 7
...     print(id(variable_local_to_f))
... 

>>> f(s)
42925000
9427936
>>> print(id(s)) # changing the function scope variable did not affect the module scope variable
42925000
>>>


Experiment: if the name variable_local_to_f were changed to s would the result differ? Answer: no. The scope matters. You can declare a variable to have global scope. I forgot about that in my first post. But anyway, you don't have a global statement, nor do I.

Reply With Quote
  #5  
Old January 31st, 2012, 02:11 PM
jnever1 jnever1 is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Location: MI
Posts: 6 jnever1 User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 3 h 33 m 12 sec
Reputation Power: 0
b49P23TIvg,
As before, your guidance is rich and much appreciated. I wish to spend more time than I have at the moment ( break between classes) digesting your example and performing, as you mentioned, experiments of my own.

The following is the code I had originally written:
Code:
#===============================================================================
# Program:	Bank of Python
# Programmer:	xxxxxx
# Date:         2012-01-28
# Abstract:		This program accepts a user name and ID (verified
#               against predefined and corresponding lists) and performs
#               withdrawal or deposit transactions against a predefined starting
#               balance. Invalid inputs are rejected then user is prompted for
#               correction.
#===============================================================================

# Initialize CONSTANTS
##
## LISTS, index is primary key to customer.
## CUSTOMER_NAMES provides a list of customers, identified only by first name, 
## who have been granted transaction access.
## CUSTOMER_ID provides a corresponding list of passcode-like IDs that confirm
## an individual customer.
## INITIAL_BALANCE, for lack of database access, contains a corresponding list
## of customer starting balances. These are never modified in the sense of a
## transaction but are used to populate appropriate mutables.
## ACCEPTABLE_ACTION contains a list of potential user input
##
CUSTOMER_NAME = ('sally','bill','linda','tom','rich','chris')
CUSTOMER_ID = (10,23,42,31,75,40) ## not the most 'secure' of methods
INITIAL_BALANCE = (10.00,10.00,10.50,25.72,1234.56,10.00)
ACCEPTABLE_ACTION = ('w','W','d','D') ## case difficult to enforce at input

# MESSAGES
##
## Used with message() function, meant to reduce the clutter in main()
##
## The opening comment
##
WELCOME = '''
   **********************************
   ***         Welcome to         ***
   ***     The Bank of Python     ***
   **********************************
'''

## Transaction options message
##
OPTIONS = '''Please select from the following transactions:

Make a (W)ithdrawal
Make a (D)eposit
<enter \"W\" or \"D\"> : '''

## The exit comment
##
GOODBYE = '''\nThank you for banking with us!
Goodbye!'''

## No express reason to pull this out of main() other than to clearly establish
## the an exit point/method early in the code. A second exit point exists where
## the user fails to enter a valid ID within a certain amount of attempts
##
NAME_PROMPT = '''Please enter your name: <press enter to quit> '''

# define main()
def main():
    while True: ## infinite loop
        ## get customer name, format to proper name convention
        customer_name = input(NAME_PROMPT).title()
        
        ## Pass customer input to name validation function
        customer_name = validate_name(customer_name)
        
        ## If no input is received, exit main()
        if not customer_name:
            break
        
        ## Say hello
        print('Hello, '+customer_name+'!\n')
        
        ## Get list index, used as primary key for further list data retrieval
        customer_index = CUSTOMER_NAME.index(customer_name.lower())        
        
        ## Name is verified, get user ID
        customer_ID = get_an_int('Please enter '+customer_name+
                                 "'s Customer ID: ")
        ## Validate ID
        validate_customer_ID(customer_ID, customer_index,
                                           customer_name)
        
        if not customer_ID:
            print('\nMaximum attempts exceeded.')
            print('Please try again later.')
            break
        
        ## ID validated. Get customer balance.
        print('One moment while we retrieve your information...')
        current_balance = float(INITIAL_BALANCE[customer_index])
        print('Retrieval complete.\n')        
        
        ## Call transaction_decision function to get user choice of transaction
        transaction_decision = get_transaction_choice(OPTIONS)
        
        ## Test user input for decision, call user indicated function
        if transaction_decision == "w":
            new_balance = withdrawal(current_balance)
        else:
            new_balance = deposit(current_balance)
        
        ## Print balance reflecting user transaction
        print('\nYour new balance is: $\t'+format(new_balance, '8,.2f')+'\n')

# Display various multi-line messages
def message(comment):
    print(comment)

# Verify name entered is in the list of current customers, ensure name is a
# customer name before return
##
## This function forces lower status on user name input and checks it against
## CUSTOMER_NAMES list. Where no match is found, user is prompted to re-enter or
## exit. Case formatting is performed to enable exact list matching and proper 
## address.
## 
def validate_name(name_result):
    print('\nOne moment while we validate '+name_result+' as a customer...')
    name_result = name_result.lower() 
    while name_result not in CUSTOMER_NAME:
        if not name_result:
            return name_result
        else:
            name_result = name_result.title()
            print("We're sorry, but "+name_result+' is not currently a'
                 ' customer.')
            name_result = input('\nPlease enter your name: <press enter to'
                               ' quit> ')
            if not name_result:
                return name_result
            else:
                print('\nOne moment while we validate '+name_result+' as a'
                     ' customer...')
                name_result = name_result.lower()
    name_result = name_result.title()
    print('Confirmed! '+name_result+' is a valued customer!\n')
    return name_result 

# Test ID input for type = int, ensure int is entered before return
##
## This function ensures that the ID input provided by the user is int-able.
## Where translation is not possible, user is prompted to enter a numerical.
##
def get_an_int(ID_prompt):
    ID_result = input(ID_prompt) # get user ID
    while isinstance(ID_result, str):
        try:
            ID_result = int(ID_result)
        except:
            print('\nThe Customer ID is a number.\n')
            ID_result = input(ID_prompt)
    ID_result = int(ID_result)
    return ID_result

# Verify ID entered is correct for the given customer name
##
## This function compares int ID against ID in CUSTOMER_ID at an index retrieved
## previously from CUSTOMER_NAMES (see main()). If ID at index does not match,
## user is prompted with invalid statement and to re-enter.
##
def validate_customer_ID(ID_number, index, name):
    print('\nValidating '+name+"'s Customer ID...")
    i = 3
    while ID_number != CUSTOMER_ID[index]:
        if i == 0:
            ID_number = False
            return ID_number
        else:
            print('\n'+name+"'s Customer ID number is not \""+str(ID_number)+
                '\".\n',sep='')
            print(i,'entries remaining')
            ID_number = get_an_int('Please enter '+name+
                "'s Customer ID number: ")
            print('\nValidating '+name+"'s Customer ID...")
            i -= 1
    print('Success!\n')
    return ID_number

# Prompt customer to choose a transaction operation, ensure a valid entry is
# received before return
##
## Would have been nice to be able to force lowercase on trans_result in order
## to reduce the valid response list. However, retaining original case for 
## invalid input rejection statement proved too problematic.
##
## This function is obtaining withdrawal or deposit decision from user. If the
## user does not provide proper input, user is prompted to re-enter.
##
def get_transaction_choice(trans_prompt):
    trans_result = input(trans_prompt) 
    while trans_result not in ACCEPTABLE_ACTION: 
        print('\n\"'+trans_result+'\" is not an available option.\n')
        trans_result = input(trans_prompt)
    return trans_result.lower()

# Test for numerical transaction 
##
## This function retrieves input from user regarding the desired amount of
## transaction, tests for float-ability and returns user input as float to
## main() if translatable. Otherwise, user is prompted to input numericals until
## a numerical is received.
##
def get_a_float(amount_prompt):
    trans_amount = input(amount_prompt)
    while isinstance(trans_amount, str):
        try:
            trans_amount = float(trans_amount) 
        except:
            print('\nPlease enter a numerical value.')
            trans_amount = input(amount_prompt)
    trans_result = float(trans_amount)
    return trans_amount

# Process a withdrawal
##
## This function forces float on balance retrieved from INITIAL_BALANCE list
## then calls the get_a_float function to retrieve numerical input from user. A
## test is then run to ensure that the user is not withdrawing more funds than 
## are available. If all OK, withdrawal is processed, new_balance is calculated
## and returned.
##
def withdrawal(balance):
    withdrawal_amount = get_a_float('\nHow much would you like to withdraw? ')
    while withdrawal_amount > balance:
        print('\nAmount exceeds available balance.')
        withdrawal_amount = get_a_float('\nHow much would you '
                                        'like to withdraw? ')
    new_balance = balance - withdrawal_amount
    return new_balance

# Process a deposit
##
## This function accepts balance from main() and forces float status.
## get_a_float() is called to retrieve numerical input from user. Since deposits
## are addition operations by nature, no validation is required. Deposit is
## processed, new_balance is calculated and returned.
##
def deposit(balance):
    deposit_amount = get_a_float('\nEnter the amount to deposit: ')
    new_balance = balance + deposit_amount
    return new_balance
    
## Begin program
message(WELCOME)
main()
message(GOODBYE)

My understanding, and expectation, was that setting ID_result = False and returning ID_result, which was associated with customer_ID through argument/parameter relationship, would reassign customer_ID on the way out to customer_ID = False.
Instead, the last numerical value for ID_result is passed to customer_ID. This behavior was proven with a print(customer_ID) statement immediately following the validate_customer_ID function call.

Replacing the naked function call with customer_ID = validate_customer_ID(...) makes everything work the way I intended.

I understand why/how the explicit statement works. I do not understand why/how the implicit statement only works with numbers.

As you said though, False is not an object...

#############################
Upon a more rigorous investigation, I now realize that ID_number was never "passing" its value back to customer_ID. customer_ID remained as first defined by user input. I failed to notice this because I was not varying my fail input (I kept using "99" as a fail ID). The logic worked because, regardless on custom_ID value, it was the function that would not exit until a valid response was entered (or time-out). Sound logic - faulty application.

Apparently, I need to go back and review the argument/parameter relationship again, as well as variable scope.

Thank you for your time.

Last edited by jnever1 : January 31st, 2012 at 06:58 PM. Reason: Underdeveloped debugging skills

Reply With Quote
  #6  
Old January 31st, 2012, 05:05 PM
b49P23TIvg's Avatar
b49P23TIvg b49P23TIvg is offline
Contributing User
Click here for more information.
 
Join Date: Aug 2011
Posts: 1,075 b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level)b49P23TIvg User rank is Second Lieutenant (5000 - 10000 Reputation Level) 
Time spent in forums: 4 Weeks 1 Day 4 h 41 m 27 sec
Reputation Power: 98
You're using python3. In python3 True False are both reserved words and objects. In earlier pythons you can reassign True and False .

For you followers who cannot read the code, I *think* it boils down to this, and if you can explain better than I have please go for it.


Code:
#! python3

# HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE 
DEMONSTRATE_ISSUE = False  # run the program with False and with True

CUSTOMER_NAME = ('sally','bill',)
CUSTOMER_ID = (1,2,)

def main():
    customer_name = CUSTOMER_NAME[0].title()
    customer_index = CUSTOMER_NAME.index(customer_name.lower())
    customer_ID = get_an_int('Please enter '+customer_name+"'s Customer ID: ")
    if DEMONSTRATE_ISSUE:
        # HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE  HERE 
        # explain why this version does not change customer_ID
        validate_customer_ID(customer_ID, customer_index,customer_name)
    else:
        customer_ID = validate_customer_ID(customer_ID, customer_index,customer_name)
    if not customer_ID:
        print('\nMaximum attempts exceeded.')
        print('Please try again later.')
        break
    print('One moment while we retrieve your information...')

def get_an_int(ID_prompt):
    ID_result = input(ID_prompt)
    while isinstance(ID_result, str):
        try:
            ID_result = int(ID_result)
        except:
            print('\nThe Customer ID is a number.\n')
            ID_result = input(ID_prompt)
    return ID_result

def validate_customer_ID(ID_number, index, name):
    print('\nValidating '+name+"'s Customer ID...")
    i = 3
    while ID_number != CUSTOMER_ID[index]:
        if i == 0:
            ID_number = False
            return ID_number
        else:
            print('\n'+name+"'s Customer ID number is not \""+str(ID_number)+'\".\n',sep='')
            print(i,'entries remaining')
            ID_number = get_an_int('Please enter '+name+"'s Customer ID number: ")
            print('\nValidating '+name+"'s Customer ID...")
            i -= 1
    print('Success!\n')
    return ID_number

main()
Comments on this post
jnever1 agrees!

Reply With Quote
  #7  
Old January 31st, 2012, 06:46 PM
jnever1 jnever1 is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Location: MI
Posts: 6 jnever1 User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 3 h 33 m 12 sec
Reputation Power: 0
Quote:
Originally Posted by b49P23TIvg
I *think* it boils down to this


Code:
## Simpler still..  
def main():
    A = 6
    B = 7
    C = 8
    print(A,B,C)
    pass_int_return_false(A,B,C)
    if not A:
        print('A was returned False without explicit assignment')
        print(A,B,C)
    else:
        print('A was not returned false without explicit assignment')
        print(A,B,C)
    print()

    A,B,C = pass_int_return_false(A,B,C)
    if not A:
        print('A was returned False with explicit assignment')
        print(A,B,C)
    else:
        print('A was not returned false with explicit assignment')
        print(A,B,C)

def pass_int_return_false(d,e,f):
    d = False
    e += 1
    f -= 1
    return d,e,f

main()


So, my question boiled down to this: Why can I pass arguments into a function but not parameters out of a function?

The misunderstanding has to do with the difference between call-by-reference and call-by-value evaluation strategies. When I did not explicitly assign the customer_ID = (function returning "False") I was assuming that renaming passed variables did only that, renamed for clarity sake. I did not understand that I was in fact making copies instead...

My professor did a poor job of expressing this feature of Python3.

Again, thank you for your examples and input. I would not have been able to phrase my (re)search to a conclusive result without it!

Rep to you.

Reply With Quote
Reply

Viewing: Dev Shed ForumsProgramming LanguagesPython Programming > Limited looping


Thread Tools  Search this Thread 
Search this Thread:

Advanced Search
Display Modes  Rate This Thread 
Rate This Thread:


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
View Your Warnings | New Posts | Latest News | Latest Threads | Shoutbox
Forum Jump

Forums: » Register « |  User CP |  Games |  Calendar |  Members |  FAQs |  Sitemap |  Support | 
  
 


Powered by: vBulletin Version 3.0.5
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.

© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 7 - Follow our Sitemap