Python:迭代构造函数的参数

时间:2011-07-20 10:34:04

标签: python constructor arguments

我经常发现自己编写类构造函数:

class foo:
    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

如果参数(和类属性)的数量变高,这显然会变得很痛苦。我正在寻找循环构造函数的参数列表并相应地分配属性的最pythonic方法。我正在使用Python 2.7,所以理想情况下我正在寻找该版本的帮助。

9 个答案:

答案 0 :(得分:21)

最恐怖的方式就是你已经写过的。如果您乐意要求命名参数,可以这样做:

class foo:
    def __init__(self, **kwargs):
        vars(self).update(kwargs)

答案 1 :(得分:8)

提供的答案依赖于*vargs**kargs参数,如果您想要限制具有特定名称的特定参数集,这可能不方便:您必须完成所有操作手工检查。

这是一个装饰器,它将方法提供的参数作为属性及其各自的名称存储在其绑定实例中。

import inspect
import functools

def store_args(method):
    """Stores provided method args as instance attributes."""
    argspec = inspect.getargspec(method)
    defaults = dict(zip( argspec.args[-len(argspec.defaults):], argspec.defaults ))
    arg_names = argspec.args[1:]
    @functools.wraps(method)
    def wrapper(*positional_args, **keyword_args):
        self = positional_args[0]
        # Get default arg values
        args = defaults.copy()
        # Add provided arg values
        list(map( args.update, ( zip(arg_names, positional_args[1:]), keyword_args.items() ) ))
        # Store values in instance as attributes
        self.__dict__.update(args)
        return method(*positional_args, **keyword_args)

    return wrapper

然后您可以像这样使用它:

class A:
    @store_args
    def __init__(self, a, b, c=3, d=4, e=5):
        pass

a = A(1,2)
print(a.a, a.b, a.c, a.d, a.e)

结果将是Python3.x上的1 2 3 4 5或Python2.x上的(1, 2, 3, 4, 5)

答案 2 :(得分:1)

class foo:
    def __init__(self, **kwargs):
        for arg_name, arg_value in kwargs.items():
            setattr(self, arg_name, arg_value)

这需要命名参数:

obj = foo(arg1 = 1, arg2 = 2)

答案 3 :(得分:1)

您可以为位置和关键字参数执行此操作:

class Foo(object):
    def __init__(self, *args, **kwargs):
        for arg in args:
            print arg
        for kwarg in kwargs:
            print kwarg

*将位元参数打包成元组中的元组和**关键字参数:

foo = Foo(1, 2, 3, a=4, b=5, c=6) // args = (1, 2, 3), kwargs = {'a' : 4, ...}

答案 4 :(得分:1)

这个怎么样?

class foo:
    def __init__(self, arg1, arg2, arg3):
        for _prop in dir():
            setattr(self, _prop, locals()[_prop])

这使用内置的python dir函数迭代所有局部变量。 它有一个副作用,创建一个无关的自我引用,但你可以过滤,如果你真的想要。 此外,如果你在dir()调用之前声明任何其他本地,它们也会被添加为构造对象的属性。

答案 5 :(得分:0)

正如其他人所说,你应该坚持原来的“pythonic”。大多数情况下的方法。

但是,如果你真的想要整整九码,这里有一些代码可以整齐地处理args,如果需要,可以使用关键字args,并避免样板重复:

def convert_all_args_to_attribs(self, class_locals):
    class_locals.pop('self')
    if 'kwargs' in class_locals:
        vars(self).update(class_locals.pop('kwargs'))
    vars(self).update(class_locals)

class FooCls:
    def __init__(self, foo, bar):
        convert_all_args_to_attribs(self, locals())

class FooClsWithKeywords:
    def __init__(self, foo, bar, **kwargs):
        convert_all_args_to_attribs(self, locals())

f1 = FooCls(1,2)
f2 = FooClsWithKeywords(3,4, cheese='stilton')


print vars(f1) #{'foo': 1, 'bar': 2}
print vars(f2) #{'cheese': 'stilton', 'foo': 3, 'bar': 4}

答案 6 :(得分:0)

如何迭代显式变量名?

class foo:
    def __init__(self, arg1, arg2, arg3):
        for arg_name in 'arg1,arg2,arg3'.split(','):
            setattr(self, arg_name, locals()[arg_name])

f = foo(5,'six', 7)

导致

print vars(f)

{'arg1': 5, 'arg2': 'six', 'arg3': 7}

我的建议类似于@peepsalot,但它更明确,并且没有使用dir(),根据documentation

  

其详细行为可能会在不同版本中发生变化

答案 7 :(得分:0)

对于python> = 3.7,请查看@dataclass类装饰器。 除其他外,它可以解决您的__init__样板编码问题。

来自doc

@dataclass
class InventoryItem:
    name: str
    unit_price: float
    quantity_on_hand: int = 0

将自动添加一个__init__

def __init__(self, name: str, unit_price: float, quantity_on_hand: int=0):
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand

答案 8 :(得分:-2)

* args是一个序列,因此您可以使用索引来访问项目:

def __init__(self, *args):
        if args:       
            self.arg1 = args[0]    
            self.arg2 = args[1]    
            self.arg3 = args[2]    
            ...  

或者你可以遍历所有这些

for arg in args:
   #do assignments