【问题标题】:Python: programmatic class instance variable initialisation with locals()Python:使用 locals() 进行编程类实例变量初始化
【发布时间】:2017-04-15 10:56:51
【问题描述】:

我有一个包含许多具有默认值的实例变量的类,可以选择在实例化中覆盖这些变量(注意:没有可变的默认参数)。

由于多次写self.x = x等相当多余,我以编程方式初始化变量。

为了说明,考虑这个例子(为简洁起见,它只有 5 个实例变量和省略任何方法):

示例:

# The "painful" way
class A:
    def __init__(self, a, b=2, c=3, d=4.5, e=5):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.e = e

# The "lazy" way
class B:
    def __init__(self, a, b=2, c=3, d=4.5, e=5):
        self.__dict__.update({k: v for k, v in locals().items() if k!='self'})

# The "better lazy" way suggested
class C:
    def __init__(self, a, b=2, c=3, d=4.5, e=5):
            for k, v in locals().items():
                if k != 'self':
                    setattr(self, k, v)

x = A(1, c=7)
y = B(1, c=7)
z = C(1, c=7)

print(x.__dict__)  # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
print(y.__dict__)  # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
print(z.__dict__)  # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}

所以为了让我的生活更轻松,我使用 B 类中显示的习语,它产生与 A 相同的结果。

这是不好的做法吗?有什么陷阱吗?

附录: 使用这个成语的另一个原因是为了节省一些空间——我打算在MicroPython 中使用它。 无论出于何种原因 Because locals work differently there,只有A类中显示的方式在其中起作用。

【问题讨论】:

  • 是的。这是不好的做法。为什么不使用dict()?它们必须是类属性吗?
  • 我想不出有什么明显的陷阱,但我确实觉得这很恶心。
  • 在我写的时候我确实有方法,我只是把它们放在例子中。
  • 我猜在使用这种方法的 IDE 中自动完成类的属性不会很有用

标签: python class micropython


【解决方案1】:

尝试更 Python 的方法:

class C:
  def __init__(self,a,b=2,c=3,d=4.5,e=5):
    for k,v in locals().iteritems():
      setattr(self,k,v)
c = C(1)
print c.a, c.b
1 2

这种方法可能会长一两行,但行长更短,你的意图也不会那么复杂。此外,任何可能尝试重用您的代码的人都将能够按预期访问您的对象的属性。

希望这会有所帮助。

编辑:使用 kwargs bc 删除了第二种方法,它没有解决默认变量要求。

这里重要的一点是,如果按照您的示例类 B 所示操作,您的代码用户将无法按预期访问您的对象的属性。

【讨论】:

  • kwargs 在这种情况下没有用:不能保证所有需要的变量都被传入,不需要的变量可以传入并存储为实例变量。
  • 您的第二种方法不允许使用默认值,这在问题中显然是需要的。在您的第一种方法中,将 self 从您的迭代中排除
  • 好点,如果需要默认值,请使用第一种方法。 @donkopotamus:setattr 需要 3 个参数。第一个需要是对象的实例。如果在类定义中操作,这个实例必须是 self。
  • @donkopotamus 哎呀,我的错,我明白你的意思了。好点。
  • 他的意思是 self 包含在 locals() 中,因为它是方法签名的一部分,所以实际上您正在创建实例变量 self.self 指向,嗯,self
【解决方案2】:

我实际上建议使用class A 中显示的代码。您所拥有的是重复代码,而不是冗余代码,并且重复并不总是不好。您只需编写一次__init__,并且为每个实例变量保留一个赋值是您的类期望的实例变量的良好文档(明确且清晰)。

但要记住的一点是,太多您可以初始化为不同参数的变量可能表明您的类需要重新设计。将某些单独的参数分组到单独的列表、字典甚至其他类中是否更有意义?

【讨论】:

  • Alex Martelli 说了一些 should be in the stdlib 来避免这种重复。然而,7年后,什么都没有。所以.....¯\_(ツ)_/¯ ....在这里,有一个+1。
猜你喜欢
  • 2015-11-05
  • 2021-08-12
  • 1970-01-01
  • 2013-03-12
  • 1970-01-01
  • 2010-10-06
  • 2011-06-21
  • 2012-01-20
  • 1970-01-01
相关资源
最近更新 更多