有没有内置的方法来定义一个接受1个参数或3个参数的函数?

时间:2018-11-12 12:03:58

标签: python function

在不带参数的情况下调用内置type()时,Python中会出现此错误:

TypeError: type() takes 1 or 3 arguments

我们如何定义这种方法? 有内置的方法吗?或者我们需要执行以下操作:

>>> def one_or_three(*args):
...   if len(args) not in [1,3]:
...     raise TypeError("one_or_three() takes 1 or 3 arguments")
... 
>>> one_or_three(1)
>>> one_or_three()
TypeError: one_or_three() takes 1 or 3 arguments
>>> one_or_three(1,2)
TypeError: one_or_three() takes 1 or 3 arguments

2 个答案:

答案 0 :(得分:4)

首先,类型不是本机Python(至少在CPython中),而是C。

inspect模块可以轻松地确认它(即使记录为内置函数,type也作为CPython中的类实现):

>>> print(inspect.signature(type.__init__))
(self, /, *args, **kwargs)
>>> print(sig.parameters['self'].kind)
POSITIONAL_ONLY

参数种类为POSITIONAL_ONLY,无法在Python中创建。

这意味着将不可能完全复制精确类型的行为。

Python源仅允许2个签名用于可变数量的参数:

  • 3个参数,其中2个是可选的:

    type(object_or_name, bases = None, dict = None)
    

    这将是非常不同的,因为它将很乐意接受type(obj, None),这绝对不是这里所期望的

  • 一个简单的*args参数,该参数的长度将通过手工进行测试-您的建议。这将更接近本机type的行为,因为无论值如何,它实际上都需要1或3个参数。

TL / DR:您的问题的答案是我们确实需要类似的方式

答案 1 :(得分:1)

此行为在Python中完全可以实现。

def one_or_three(one, two=object(), three=object()):
    two_sentinel, three_sentinel = one_or_three.__defaults__
    if (two == two_sentinel) != (three == three_sentinel):
        raise TypeError("one_or_three() takes 1 or 3 arguments")
    ...

这具有命名参数(语义值,按名称传递等)的优点,但基本上是检查某些变量的值以了解是否传递了一件事或三件事的一种变体。

请注意,对object()的两次调用产生了两个不同的对象,并将它们绑定到函数:

>>> one_or_three.__defaults__
(<object object at 0x00168540>, <object object at 0x00168ED0>)

这样,它们是全局唯一的。如果您改为使用None作为哨兵对象,则不可能将None作为参数传递(当然,这可能是需要的)。