Python 输入验证函数保留旧变量

Python Input validation function keeping old variables

我目前正在编写一个税收计算器,我注意到如果我输入了一个无效的税码,一开始它看起来会被拒绝,但随后程序似乎会保留初始输入并反向循环它们订单(像这样):

[User Input] Gross Income: £32,000
[User Input] Tax Code: 32,000
Tax Code after input: 32,000
Failed letter check
Invalid input. Please enter your tax code.
[User Input] Tax Code: y      
Tax Code after input: y
Passed letter check
Tax Code after letter check: y
Tax Code during dictionary match check (false): y
Failed dictionary match check
Invalid input. Please enter your tax code.       
[User Input] Tax Code: 1257L  
Tax Code after input: 1257L
Passed letter check
Tax Code after letter check: 1257L
Tax Code during dictionary match check (true): 1257L
Passed dictionary match check
Tax Code after dictionary match check: 1257L
Personal Allowance: 12570
Tax Letter: L
Tax Code after dictionary match check: y
Personal Allowance: 0
Tax Letter: Y
Tax Code after letter check: 32,000
Traceback (most recent call last):
  File "C:\Users\Joshua.Riberio\Git\taxcalc\taxcalc.py", line 336, in <module>   
    salarycalc()
  File "C:\Users\Joshua.Riberio\Git\taxcalc\taxcalc.py", line 243, in salarycalc 
    personal_allowance, tax_letter = get_tax_code()
  File "C:\Users\Joshua.Riberio\Git\taxcalc\taxcalc.py", line 78, in get_tax_code
    tax_letter = tax_code[tax_letter_index:].upper()
TypeError: slice indices must be integers or None or have an __index__ method

这是我的代码:

def get_tax_code():
    tax_code = input('Tax Code: ')
    print('Tax Code after input:', tax_code)
    tax_letter_index = ''
    # Checking input contains a letter
    for char in tax_code:
        if char.upper() in alpha:
            tax_letter_index = tax_code.index(char)
            print('Passed letter check')
            break
    
    if tax_letter_index == '':
        print('Failed letter check')
        print('Invalid input. Please enter your tax code.')
        get_tax_code()

    print('Tax Code after letter check:', tax_code)

    tax_letter = tax_code[tax_letter_index:].upper()

    # Checking input has a key match in the tax_letters dictionary
    if tax_letter not in tax_letters.keys():
        print('Tax Code during dictionary match check (false):', tax_code)
        print('Failed dictionary match check')
        print('Invalid input. Please enter your tax code.')
        get_tax_code()
    elif tax_letter in tax_letters.keys():
        print('Tax Code during dictionary match check (true):', tax_code)
        print('Passed dictionary match check')
        
    print('Tax Code after dictionary match check:', tax_code)

    # Getting personal allowance from Tax Code
    personal_allowance = tax_code[:tax_letter_index]
    if personal_allowance == '':
        personal_allowance = 0
    else:
        personal_allowance = int(personal_allowance) * 10

    # Setting personal allowance exceptions for gross income over £100,000
    if gross_income > 100000:
        personal_allowance = set_personal_allowance - ((gross_income - 100000) / 2)
        if personal_allowance < 0:
            personal_allowance = 0
            
    print('Personal Allowance:', personal_allowance)
    print('Tax Letter:', tax_letter)
    
    return personal_allowance, tax_letter

过多的打印只是为了让我可以看到输入被更改的地方。似乎在 运行 成功通过后,代码使用先前的无效输入循环回到顶部,导致代码无法 运行.

有人能看出我错在哪里吗?

备注

tax_letters 是定义的字典,将更新它以从选定的 CSV 中提取:

tax_letters = {
    "L": "You’re entitled to the standard tax-free Personal Allowance",
    "M": "Marriage Allowance: you’ve received a transfer of 10% of your partner’s Personal Allowance",
    "N": "Marriage Allowance: you’ve transferred 10% of your Personal Allowance to your partner",
    "T": "Your tax code includes other calculations to work out your Personal Allowance",
    "0T": "Your Personal Allowance has been used up, or you’ve started a new job and your employer does not have the details they need to give you a tax code",
    "BR": "All your income from this job or pension is taxed at the basic rate (usually used if you’ve got more than one job or pension)",
    "D0": "All your income from this job or pension is taxed at the higher rate (usually used if you’ve got more than one job or pension)",
    "D1": "All your income from this job or pension is taxed at the additional rate (usually used if you’ve got more than one job or pension)",
    "NT": "You’re not paying any tax on this income",
    "S": "Your income or pension is taxed using the rates in Scotland",
    "S0T": "Your Personal Allowance (Scotland) has been used up, or you’ve started a new job and your employer does not have the details they need to give you a tax code",
    "SBR": "All your income from this job or pension is taxed at the basic rate in Scotland (usually used if you’ve got more than one job or pension)",
    "SD0": "All your income from this job or pension is taxed at the intermediate rate in Scotland (usually used if you’ve got more than one job or pension)",
    "SD1": "All your income from this job or pension is taxed at the higher rate in Scotland (usually used if you’ve got more than one job or pension)",
    "SD2": "All your income from this job or pension is taxed at the top rate in Scotland (usually used if you’ve got more than one job or pension)",
    "C": "Your income or pension is taxed using the rates in Wales",
    "C0T": "Your Personal Allowance (Wales) has been used up, or you’ve started a new job and your employer does not have the details they need to give you a tax code",
    "CBR": "All your income from this job or pension is taxed at the basic rate in Wales (usually used if you’ve got more than one job or pension)",
    "CD0": "All your income from this job or pension is taxed at the higher rate in Wales (usually used if you’ve got more than one job or pension)",
    "CD1": "All your income from this job or pension is taxed at the additional rate in Wales (usually used if you’ve got more than one job or pension)"
    }

gross_income定义在main函数中:

def salarycalc():
    screen_clear()

    global gross_income
    gross_income = input('Gross Income: £')

    if ',' in gross_income:
        gross_income = gross_income.replace(',', '')

    if '£' in gross_income:
        gross_income = gross_income.replace('£', '')

    gross_income = float(gross_income)

set_personal_allowance 是由 'tax_brackets' 字典中的值定义的变量,稍后将再次更新它以从 CSV 的选择中提取:

tax_brackets = {
    'Personal Allowance': 12570,
    'Basic': [0, 0.2],
    'Higher': [50270, 0.4],
    'Additional': [150000, 0.45]
}

set_personal_allowance = tax_brackets['Personal Allowance']

因此,您的代码可能不起作用的主要原因是重复的递归调用。为了使方法更简单,我将所需的功能分解为 2 个验证检查。请注意,我已经包含了一些示例数据来检查程序,您可以相应地忽略它。
只要两个检查为真,驱动程序函数 get_tax_code 就会运行,这意味着它只会在税码通过字母检查和字典匹配验证后才停止要求输入。

def get_tax_code(tax_letters):
    gross_income = 32000
    set_personal_allowance = 12570
    check1 = True
    check2 = True

    while check1 or check2:
        tax_code = input('Tax Code: ')
        print('Tax Code after input:', tax_code)
        tax_letter_index = verify1(tax_code)
        if tax_letter_index != -1:
            check1 = False
            tax_letter = tax_code[tax_letter_index:].upper()
            print('Tax Code after letter check:', tax_code)
            x = verify2(tax_letter, tax_letters)
            if x:
                print('Tax Code during dictionary match check (true):', tax_code)
                print('Passed dictionary match check')
                print('Tax Code after dictionary match check:', tax_code)
                check2 = False
            else:
                print('Tax Code during dictionary match check (false):', tax_code)
                print('Failed dictionary match check')
                print('Invalid input. Please enter your tax code.')
        
        else:
            print('Failed letter check')
            print('Invalid input. Please enter your tax code.')

    
    # Getting personal allowance from Tax Code
    personal_allowance = tax_code[:tax_letter_index]
    if personal_allowance == '':
        personal_allowance = 0
    else:
        personal_allowance = int(personal_allowance) * 10

    # Setting personal allowance exceptions for gross income over £100,000
    if gross_income > 100000:
        personal_allowance = set_personal_allowance - ((gross_income - 100000) / 2)
        if personal_allowance < 0:
            personal_allowance = 0
            
    print('Personal Allowance:', personal_allowance)
    print('Tax Letter:', tax_letter)

这些是之前包含在 get_tax_code 中的两个辅助函数。
第一个函数 verify1() 负责字母检查,而第二个 verify2() 处理字典匹配。从这两个函数返回的值决定了检查值是否更新,进而决定用户是否必须再次提供 tax_code 输入。

def verify1(tax_code):
    tax_letter_index = ''
    # Checking input contains a letter
    for char in tax_code:
        if char.upper().isalpha():
            tax_letter_index = tax_code.index(char)
            print('Passed letter check')
            break
    if tax_letter_index == '':
        return -1
    else:
        return tax_letter_index

def verify2(tax_letter, tax_letters):
    if tax_letter not in tax_letters.keys():
        return False
    elif tax_letter in tax_letters.keys():
        return True

输出

Tax Code: 3200
Tax Code after input: 3200
Failed letter check
Invalid input. Please enter your tax code.
Tax Code: y
Tax Code after input: y
Passed letter check
Tax Code during dictionary match check (false): y
Failed dictionary match check
Invalid input. Please enter your tax code.
Tax Code: 1257L
Tax Code after input: 1257L
Passed letter check
Tax Code during dictionary match check (true): 1257L
Passed dictionary match check
Tax Code after dictionary match check: 1257L
Personal Allowance: 12570
Tax Letter: L