Python 带有可选数据 class 参数的函数调用导致错误

Python function call with optional Data class parameter causes error

我试图将数据 class 对象作为可选参数传递给函数,但卡住了。 因此,这是描述问题的简化代码。 首先,我定义了一个数据 class:

@dataclass
class SomeObject:
    A: str

如果我将参数设置为非可选参数,则没有问题:

def printObject0 (Index, AnyObject):
    print(Index, AnyObject.A)

Object1 = SomeObject ("A")

printObject0 (1, Object1)

# gives:
# 1 A

但是,如果我尝试将其设为可选参数 *args,就像这样...

def printObject1 (Index, *AnyObject):
    print(Index, AnyObject.A)

Object1 = SomeObject ("A")

printObject1 (1, Object1)
# gives:
# AttributeError: 'tuple' object has no attribute 'A'

...或像这样的 **kwargs...

def printObject2 (Index, **AnyObject):
    print(Index, AnyObject.A)
    
Object1 = SomeObject ("A")

printObject2 (1, Object1)
# gives:
# TypeError: printObject2() takes 1 positional argument but 2 were given

我得到了上述错误。

有谁知道我如何使对象可选?

函数参数 *AnyObject 表示 AnyObject 是所有剩余位置参数的元组。所以你可以做

from dataclasses import dataclass


@dataclass
class SomeObject:
    A: str

def printObject_1(idx, *objs):
    print(idx, objs[0].A)

printObject_1(1, SomeObject('A'))  # 1 A

此外,函数参数**AnyObject意味着AnyObject是所有剩余关键字参数的字典。所以你可以这样做:

def printObject_2(idx, **objs):
    print(idx, objs['foo'].A)

printObject_2(1, foo=SomeObject('A'))  # 1 A

顺便说一句:通常,可选的函数参数有默认值,比如

def printObject_3(idx, obj=SomeObject('A')):
    print(idx, obj.A)


printObject_3(1)  # 1 A
printObject_3(1, SomeObject('ABC'))  # 1 ABC
printObject_3(1, obj=SomeObject('ABC'))  # 1 ABC

通常情况下,应该避免第二次函数调用,因为它比较error-prone。

但由于默认参数值在这种情况下是可变的(见下文),这样做更安全

def printObject_4(idx, obj=None):
    if obj is None:
        obj = SomeObject('A')
    print(idx, obj.A)

printObject_4(1)  # 1 A
printObject_4(1, obj=SomeObject('ABC'))  # 1 ABC

printObject_3的问题在于您可以在函数内部更改默认值:

def printObject_3_buggy(idx, obj=SomeObject('A')):
    print(idx, obj.A)
    obj.A = 321

printObject_3_buggy.__defaults__  # (SomeObject(A='A'),)

printObject_3_buggy(1)  # 1 A

printObject_3_buggy.__defaults__  # (SomeObject(A=312),)

printObject_3_buggy(1)  # 1 321

出现这种意外行为的原因是默认值是可变的。因此,当您想使用可变默认值时,请使用 printObject_4 中的模式,或者小心不要写入关键字参数。