如何编写一个能够处理单个输入或输入列表的函数

4
我将用一些非常简单的函数来说明我的问题,这些函数只是平方数。但我想了解的是编写函数的通用最佳实践。
假设我有一个要平方的数字,我可以编写以下函数。
def square (x):
    y = x**2
    return y

如果我有一个数字列表,想要得到每个元素被平方后的列表,我可以使用:
def square (x_list):
    y_list = []
    for x in x_list:
        y_list.append( x**2 )
    return y_list

但是我想写一个函数来处理这两种情况。它将查看输入是否为数字或列表,并相应地进行操作。我可以使用 type 来检测类型,但我想知道最pythonic的方法是什么。

6个回答

2
你可以检查传入参数的类型并相应地进行操作:
# check the variable instance type
def square (x):
    """Returns squares of ints and lists of ints - even if boxed inside each other.
    It uses recursion, so do not go too deep ;o)"""

    if isinstance(x,int):
        return x**2

    elif isinstance(x,list):
        return [square(b) for b in x] #  recursion for lists of ints/lists of ints

    else:
        raise ValueError("Only ints and list of ints/list of ints allowed")


print(square(4))
print(square([2,3,4]))
print(square([2,3,[9,10],11]))

try:
    print(square(2.6))
except ValueError as e:
    print(e)

输出:

16
[4, 9, 16]
[4, 9, [81, 100], 121]

Only ints and list of ints/list of ints allowed

2

我同意 @Bryan Oakley 的答案,最好编写代码仅接受单个参数类型。话虽如此,我想呈现一个处理可变数量输入参数的函数示例:

def square(*args):

    return [arg**2 for arg in args]

请注意,这将始终返回一个列表:
y = square(2,4,6,8)

y = square(4)

产生:

[4, 16, 36, 64]
[16]

2
作为其他答案已经解释过的,这可能不是一个很好的设计。
首先,“number”可以是一个“int”,或者是一些用户定义的“int”子类,或者是一个“float”,或者是一些用户定义的“Quaternion”类型。通常,你只需要使用鸭子类型:如果 x ** 2 可以工作,那么 x 就像一个数字在叫,这就足够了。
但是一个整数列表不像一个整数。那么,你能做什么呢?
嗯,通常,你会想要明确地遍历它们:
>>> xs = [1, 2, 3]
>>> sqs = [square(x) for x in xs]

...或者编写一个执行此操作的函数:

>>> def squares(xs): return [square(x) for x in xs]
>>> sqs = squares(xs)

...或者使用懂得向量化数学运算的类型:

>>> xs = np.array([1, 2, 3])
>>> sqs = square(xs)

事实上,即使你想处理两种不同类型,你通常也可以依赖鸭子类型:
def square(x):
    try:
        return x**2
    except ValueError:
        return [i**2 for i in x]

这将对类似数字的内容进行平方,然后迭代平方所有不可平方元素的元素,并为任何失败的元素(因为它不可迭代或其元素不可平方)引发合理的异常。保留html标签。
偶尔,您确实需要进行类型切换。但仍然希望尽可能接近鸭子类型,这意味着使用isinstance(这样,例如,用户的int子类型仍然被视为数字),并且通常使用抽象基类(这样,例如,用户的Quaternion类型仍然被视为数字)。
在这种情况下,这意味着要特别处理数字,并假设其他任何内容都是可迭代的:
def square(x):
    if isinstance(x, numbers.Number):
        return x**2
    else:
        return [i**2 for i in x]

...或者特别处理可迭代对象,并假设其他所有内容都是数字:

def square(x):
    if isinstance(x, collections.abc.Iterable):
        return [i**2 for i in x]
    else:
        return x**2

或者特别对待这两个,将其他所有内容视为错误:

def square(x):
    if isinstance(x, numbers.Number):
        return x**2
    elif isinstance(x, collections.abc.Iterable):
        return [i**2 for i in x]
    raise ValueError(f"'{type(x).__name__}' instance is not a number or numbers")

1

如果使用numpy,这非常简单。对于多个元素,请将其制作成数组。对于单个元素,则应用该函数。

import numpy as np

def square(a):
    return a**2

a = np.array([2,3,4,5])
print(square(a)) # array([ 4,  9, 16, 25])

b = 9
print(square(b)) # 81

1
是的,虽然这并不是一个直接回答问题的答案,但任何想要将数字集合视为数字并对其进行数学运算的人已经在 numpy 数组处理术语中思考了,除非他们有充分的理由不使用 numpy,否则应该使用 numpy。 - abarnert

1
将参数视为列表输入,并在其不是列表时处理异常。

EAFP 易于请求宽恕,而不是事先获得许可。这种常见的Python编程风格假定存在有效的键或属性,并在假设证明为假时捕获异常。这种清晰快速的风格的特点是存在许多try和except语句。该技术与许多其他语言(如C)常见的LBYL风格形成对比。docs

def square(x):    
    try:
        y = [e ** 2 for e in x]
    except TypeError:
        y = x ** 2
    return y

square(2)
# Out: 4
square([2, 3])
# Out: [4, 9]

0

你可以使用类型来编写一行返回:

def square(x):
    return(x**2 if type(x) is int else [i**2 for i in x])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接