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

问题描述

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

@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

通常,应该避免第二个函数调用,因为它更容易出错。

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

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.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中的模式,或者当心不要写入关键字参数。