函数在调用自身后不会 return 一个值

Function will not return a value after it calls itself

几天前才开始学习Python。我正在玩国际象棋游戏,我遇到的麻烦是确定玩家想要玩的棋子位置的代码。如果我输入一个包含两个数字的字符串,该函数会解析它们并分配它们以及 y 和 x 值。

但是,如果在字符串中只找到一个数字或没有找到数字,它会打印“没有找到数字或没有找到足够的数字”,让您重新输入一个字符串,然后再次调用该函数。我的问题是,在它调用自身后,如果您输入一个有效字符串,它会识别数字,将其打包到一个列表中,就像它应该做的那样,但随后不会 return 任何东西。我检查以确保列表已正确填写,它只是 returns none.

这里是错误:

Traceback (most recent call last):
  File "C:\Users\Jack\PycharmProjects\Messing With 2D Lists\Messing With 2D Lists.py", line 91, in <module>
    piece_num = position[0]
TypeError: 'NoneType' object is not subscriptable

这是调用该函数的代码,最后一行是触发错误的代码,因为它接收的不是列表,而是 none 类型:

posit = input("Choose your piece\nEnter coordinates: ")

position = piece_position(posit)

print(position)

piece_num = position[0]

如果您想重现我的错误,这是“棋盘”,因为它在代码中被引用。

chess = [
    [1, 2, 1, 2, 1, 2, 1, 2],
    [2, 1, 2, 1, 2, 1, 2, 1],
    [1, 2, 1, 2, 1, 2, 1, 2],
    [2, 1, 2, 1, 2, 1, 2, 1],
    [1, 2, 1, 2, 1, 2, 1, 2],
    [2, 1, 2, 1, 2, 1, 2, 1],
    [1, 2, 1, 2, 1, 2, 1, 2],
    [2, 1, 2, 1, 6, 1, 2, 1],

]

这里是确定棋子位置的函数:

def piece_position(pos):
    
    num1d = False
    num2d = False

    for char in pos:
        if not num1d:
            if char.isdigit():
                num1 = int(char) - 1
                num1d = True
            else:
                pass
        elif not num2d:
            if char.isdigit():
                num2 = int(char) - 1
                num2d = True
            else:
                pass

    print(num1d)
    print(num2d)

    if num1d and num2d:
        print(num1, num2)
        result = [chess[num1][num2]]
        result.append(num1)
        result.append(num2)
        print(result)
        return result
    else:
        print("No digits or not enough digits found")
        posit2 = input("Choose your piece\nEnter coordinates: ")
        piece_position(posit2)

另外我知道当它翻转时我称它为posit2而不是posit,并且变量正在询问posit的结果,但是无论我将其更改为posit还是似乎没有任何区别posit2.

请随意提出建设性的组织批评,我还是 Python 的新手并且还在摸索,我知道我有很多需要改进的地方,还有很多我不明白的地方。

你会在重复函数的 else 语句中得到 None,你可以 return 函数调用结果,或者你可以只使用 while 循环

def piece_position(pos):
    
    num1d = False 
    num2d = False

    while not num1d and not num2d:
        for char in pos:
            if not num1d:
                if char.isdigit():
                    num1 = int(char) - 1
                    num1d = True
            elif not num2d:
                if char.isdigit():
                    num2 = int(char) - 1
                    num2d = True
        if not num1d and not num2d:
            pos = input("Try again:") 
            num1d = False 
            num2d = False

    result = []
    #... 
    return result

或者,可能更简单的方法是在函数体外部而不是在函数体内部做同样的事情(这种方式绝对更容易测试)

pos = input("Enter coordinates") 
result = piece_position(pos)
while not result:  # assuming this returned None, or an empty list, for example 
    pos = input("Enter coordinates") 
    result = piece_position(pos)

虽然你已经解决了,但再次:你忘记了return递归调用。

def func(n):
    ...

    return func(n')

另外,为什么要检查输入是否为数字?这几乎和通过 isinstance 检查一样糟糕。这不是C,我们宁愿根本不检查它.

如果您无论如何都要将值转换为 int,请先尝试,然后捕获错误。这就是所谓的 EAFP 风格。

try:
    a = int('Nope.')  # try first
except ValueError:
    pass  # Fail, then that's a sad thing. Do some actions for this rouge cases.
else:
    pass  # We're good, keep going and do something with a. 

更简单的实现方式是这样的:

chess = [
    [1, 2, 1, 2, 1, 2, 1, 2],
    [2, 1, 2, 1, 2, 1, 2, 1],
    [1, 2, 1, 2, 1, 2, 1, 2],
    [2, 1, 2, 1, 2, 1, 2, 1],
    [1, 2, 1, 2, 1, 2, 1, 2],
    [2, 1, 2, 1, 2, 1, 2, 1],
    [1, 2, 1, 2, 1, 2, 1, 2],
    [2, 1, 2, 1, 6, 1, 2, 1],
]

def validate_position(board, pos: str) -> (int, int):
    try:
        x, y = map(int, pos.split())  # Fail-fast
    except ValueError as err:
        raise ValueError(f"Not enough / Too many values, expected 2!") from err

    # put some rule check here.
    if (0 <= x < len(board)) and (0 <= y < len(board[0])):
        return board[x][y], x, y

    raise ValueError(f"Position {(x, y)} is outside of board!")


def piece_position(board):
    inp = input("Enter coordinates (x y): ")  # Expecting 1 10 format
    try:
        return validate_position(board, inp)
    except ValueError as err:
        print(err)
        return piece_position(board)


print(f"Selected: {piece_position(chess)}")

Enter coordinates (x y): 1 2 3
Not enough / Too many values, expected 2!
Enter coordinates (x y): 1
Not enough / Too many values, expected 2!
Enter coordinates (x y): 10 3
Position (10, 3) is outside of board!
Enter coordinates (x y): 2 3
Selected: (2, 2, 3)

如果将 x、y 坐标放在不同的 input() 调用是绝对强制性的,请按以下方式更改行:

inp = input("Enter coordinates (x y): ")  # Expecting 1 10 format

# to 

inp = " ".join(input("Enter X: "), input("Enter Y: "))

正如您所说,您是 python 的新手,您最好了解 python 中递归的缺点。两者都不适用于您的 use-case,但最好了解以后的情况。

>>> def recurse(called=1):
...     print(called)
...     return recurse(called + 1)
... 
>>> print(recurse())
.
.
.
985
986
987
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 3, in recurse
  File "<input>", line 3, in recurse
  File "<input>", line 3, in recurse
  [Previous line repeated 984 more times]
  File "<input>", line 2, in recurse
RecursionError: maximum recursion depth exceeded while calling a Python object

Python不进行tail-recursion优化,由于CPython栈限制,容易造成栈溢出引发RecursionError。当然,在大约 1000 次迭代中,人们永远不会输入错误。

另一个缺点是性能。

from functools import lru_cache
import timeit


@lru_cache(512)
def fibo_recurse_cached(n):
    if n == 0:
        return 0
    if n == 1:
        return 1

    return fibo_recurse_cached(n - 2) + fibo_recurse_cached(n - 1)


def fibo_recurs(n):
    if n == 0:
        return 0
    if n == 1:
        return 1

    return fibo_recurs(n - 2) + fibo_recurs(n - 1)


def fibo_gen(n):
    a, b = 1, 1
    for _ in range(n):
        yield a
        a, b = b, a + b


print(f"Naive recursive : {timeit.timeit(lambda: fibo_recurs(38), number=10)}")
print(f"Cached recursive: {timeit.timeit(lambda: fibo_recurse_cached(38), number=10)}")
print(f"Generator ver.  : {timeit.timeit(lambda: list(fibo_gen(38)), number=10)}")
Naive recursive : 136.2449525
Cached recursive: 1.939999998512576e-05
Generator ver.  : 4.369999999198626e-05

如果您在递归内部所做的事情很简单并且与外部作用域没有任何交互,那么 functools.lru_cache 将大大提高性能。