如何在python中从变量参数(kwargs)设置类属性

fij*_*ron 93 python

假设我有一个带有构造函数(或其他函数)的类,它接受可变数量的参数,然后有条件地将它们设置为类属性.

我可以手动设置它们,但似乎变量参数在python中很常见,应该有一个共同的习惯用法.但我不确定如何动态地这样做.

我有一个使用eval的例子,但这几乎不安全.我想知道这样做的正确方法 - 也许是lambda?

class Foo:
    def setAllManually(self, a=None, b=None, c=None):
        if a!=None: 
            self.a = a
        if b!=None:
            self.b = b
        if c!=None:
            self.c = c
    def setAllWithEval(self, **kwargs):
        for key in **kwargs:
            if kwargs[param] != None
                eval("self." + key + "=" + kwargs[param])
Run Code Online (Sandbox Code Playgroud)

lar*_*sks 135

您可以使用以下setattr()方法:

class Foo:
  def setAllWithKwArgs(self, **kwargs):
    for key, value in kwargs.items():
      setattr(self, key, value)
Run Code Online (Sandbox Code Playgroud)

有一种类似的getattr()方法来检索属性.

  • 与[接受的答案有什么区别?](/sf/answers/573118591/)。他们的优点和缺点是什么? (4认同)

fqx*_*qxp 109

您可以__dict__使用关键字参数更新属性(以字典的形式表示类属性):

class Bar(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
Run Code Online (Sandbox Code Playgroud)

然后你可以:

>>> bar = Bar(a=1, b=2)
>>> bar.a
1
Run Code Online (Sandbox Code Playgroud)

和类似的东西:

allowed_keys = {'a', 'b', 'c'}
self.__dict__.update((k, v) for k, v in kwargs.items() if k in allowed_keys)
Run Code Online (Sandbox Code Playgroud)

你可以预先过滤键(如果你还在使用Python 2.x,请使用iteritems而不是items).

  • 如果你使用 `self.__dict__.update(locals())` 来复制位置参数,那就更好了。 (3认同)
  • 甚至更好地使allowed_keys成为一组:) (2认同)
  • 我想你现在需要这个.. kwargs[0].items() 而不是 kwargs.iteritems() - (我在写作时使用 Python 3.6.5) (2认同)

小智 13

这里的大多数答案都没有涵盖将所有允许的属性初始化为一个默认值的好方法.所以,要添加@fqxp@mmj给出的答案:

class Myclass:

    def __init__(self, **kwargs):
        # all those keys will be initialized as class attributes
        allowed_keys = set(['attr1','attr2','attr3'])
        # initialize all allowed keys to false
        self.__dict__.update((key, False) for key in allowed_keys)
        # and update the given keys by their given values
        self.__dict__.update((key, value) for key, value in kwargs.items() if key in allowed_keys)
Run Code Online (Sandbox Code Playgroud)


bil*_*oie 10

另一个基于mmjfqxp出色答案的变体。如果我们想怎么办

  1. 避免对允许的属性列表进行硬编码
  2. 直接明确地为构造函数中的每个属性设置默认值
  3. 通过以下任一方式将 kwargs 限制为预定义的属性
    • 默默地拒绝无效的参数,可选地,
    • 引发错误。

通过“直接”,我的意思是避免使用无关的default_attributes字典。

class Bar(object):
    def __init__(self, **kwargs):

        # Predefine attributes with default values
        self.a = 0
        self.b = 0
        self.A = True
        self.B = True

        # get a list of all predefined values directly from __dict__
        allowed_keys = list(self.__dict__.keys())

        # Update __dict__ but only for keys that have been predefined 
        # (silently ignore others)
        self.__dict__.update((key, value) for key, value in kwargs.items() 
                             if key in allowed_keys)

        # To NOT silently ignore rejected keys
        rejected_keys = set(kwargs.keys()) - set(allowed_keys)
        if rejected_keys:
            raise ValueError("Invalid arguments in constructor:{}".format(rejected_keys))
Run Code Online (Sandbox Code Playgroud)

不是重大突破,但可能对某人有用......

编辑: 如果我们的类使用@property装饰器来封装带有 getter 和 setter 的“受保护”属性,并且如果我们希望能够使用我们的构造函数设置这些属性,我们可能希望allowed_keys使用来自 的值扩展列表dir(self),如下所示:

allowed_keys = [i for i in dir(self) if "__" not in i and any([j.endswith(i) for j in self.__dict__.keys()])]
Run Code Online (Sandbox Code Playgroud)

上面的代码不包括

  • 来自dir()(基于“__”存在的排除)中的任何隐藏变量,以及
  • dir()在属性名称(受保护或其他)的末尾找不到其名称的任何方法 from __dict__.keys(),因此可能只保留 @property 修饰的方法。

此编辑可能仅对 Python 3 及更高版本有效。


mmj*_*mmj 8

我提出了fqxp答案的变体,除了允许的属性之外,还可以设置属性的默认值:

class Foo():
    def __init__(self, **kwargs):
        # define default attributes
        default_attr = dict(a=0, b=None, c=True)
        # define (additional) allowed attributes with no default value
        more_allowed_attr = ['d','e','f']
        allowed_attr = list(default_attr.keys()) + more_allowed_attr
        default_attr.update(kwargs)
        self.__dict__.update((k,v) for k,v in default_attr.items() if k in allowed_attr)
Run Code Online (Sandbox Code Playgroud)

这是Python 3.x代码,对于Python 2.x,您需要至少进行一次调整,iteritems()而不是items().