【问题标题】:Access Python class `__dict__` within class initializer?在类初始化程序中访问 Python 类`__dict__`?
【发布时间】:2013-10-27 09:23:24
【问题描述】:

如何在其自己的“类初始值设定项”(类定义)代码中访问类的 __dict__?如果做不到这一点,我如何才能将类的待定义属性作为字符串访问(以便可以通过编程方式生成它们的名称并设置它们的值)

class A:  # from a library by a stubborn maintainer
  def __init__ (self):
    self.hello = "Hello"
    # + some huge misguided crap that I do not control
  def f1 (self):
    self.f2 ()
  def f2 (self):
    print self.hello

class B:
  f1 = A.__dict__ ['f1'] # works
  # how to loop over 2..10 and set B.f$i <- A.f$i ?
  # this doesn't work:
  #B.__dict__ ['f2'] = A.__dict__ ['f2']  # B not yet defined
  def __init__ (self):
    #self.hello = A ().hello  # the actual code has to be copied manually
    # misguided crap removed

a = B ()
a.f2()

最好不要复制/粘贴 self.hello 初始化以绕过被误导的东西,但我认为没有重构工具可以轻松解决。

【问题讨论】:

  • 子类 A 怎么样?
  • 然后运行 ​​A 的初始化程序?有没有办法避免它?尽管如此,我还是想挑选方法。如果有错误,我可以决定是否添加更多方法
  • 你可以在你的子类中重写你不喜欢的方法,并且永远不要调用super。也许我对这个问题理解得不够好。
  • A 有数百种方法。它是许多概念的合并。我想挑选,看看它是什么(以及是否)有效。我会在需要的地方使用剪切/粘贴/修改。但是关于不调用 super 的好处(不过,我仍然需要一些来自 super 的代码,必须复制/粘贴)
  • 哦,我明白了。这听起来像是元类魔法的一个很好的用例……一秒钟。

标签: python class inheritance reflection initialization


【解决方案1】:

抱歉,在定义类时您不能真正做到这一点。换句话说,你不能在自己的定义中访问一个类的__dict__,因为执行定义的结果将成为字典的内容(这就是为什么类名还没有绑定到任何东西的原因)。可能最简单的解决方法是给你的班级一个__metaclass__

元类是类中的类,因此它们的实例是类(因此得名)。为简单起见,我在其中定义了一个 forB,因为它的使用和范围将仅限于该类(但是,这在 Python 3 中无法做到)。

__metaclass__.__new__()中的代码只在it的实例被创建时执行,即在classB的定义期间,而不是在classB的实例被创建时执行,所以开销只有当这种情况发生时才会发生复制所有方法的操作——通常是一次。请注意,由于类方法也是描述符对象,因此有必要调用每个方法的__get__ 方法,以便获得将它们绑定到另一个类对象的正确值。有关详细信息,请参阅文档中 Raymond Hettinger 的标题为 Descriptor HowTo Guide 的部分。

class A(object):
    def __init__(self):
        self.hello = "Hello"
        # + some huge misguided crap that I do not control
    def f1(self):
        self.f2()
    def f2(self):
        print self.hello

class B(object):
    class __metaclass__(type):
        A_METHODS = ['f{}'.format(i) for i in xrange(1, 11)] # 'f1'...'f10'

        def __new__(mcls, classname, bases, classdict):
            classobj = type.__new__(mcls, classname, bases, classdict)
            classobj.hello = A().hello
            for fn in mcls.A_METHODS:
                if fn in A.__dict__:
                    setattr(classobj, fn, A.__dict__[fn].__get__(classobj, A))
            return classobj

    def __init__(self):
        pass

a = B()
a.f2()  # --> Hello

【讨论】:

  • 很有趣,解决了原来的问题,虽然它在技术上没有回答原来的问题。我最终可能会走那条路,谢谢。虽然我仍然对原始语法/范围问题感到好奇。
  • 我的意思是在类初始化器(在任何方法之前跟在class B之后的东西),而不是在实例_init_ 必须在每次创建实例时执行。如何修改我的问题以使其更清楚?
  • 您不能在类定义中访问B.__dict__,因为B(及其__dict__)在其定义中的代码完成执行之前将不存在。
  • 是的。因此原始问题 :) 和“失败”我如何以编程方式访问类属性作为字符串等...
  • 当然,补充一点——尽管按照其他答案,显然有一种不可移植的、可能不稳定的方法。我也不会去——我会在定义类 def 之后更新它。我对 Py 的元/反射功能很好奇,所以我问了这个问题
【解决方案2】:

完全可能有更好的方法(比如分叉A类并删除误导的代码,或者让每个B实例持有或共享一个A实例,或者在B的定义完成后修改B.__dict__) .

也就是说,您可以替换:

B.__dict__ ['f2'] = A.__dict__ ['f2']  # B not yet defined

与:

locals()['f2'] = A.__dict__ ['f2']

这不能保证工作,因为你不能修改locals(),但它似乎至少在 CPython 2.7.5 中工作:

class Foo:
    a = locals()
    locals()['b'] = 0

print Foo.a is Foo.__dict__, Foo.b

输出True 0。有趣的是,如果Foo继承自object,它会输出False 0,这表明我所处的困境。

当然,这是对特定于实现的行为的可怕滥用,不推荐,可能会在某个地方出错,等等 :-)

【讨论】:

  • 是的,我写了f1 = A.__dict__ ['f1']来证明我对那条路线的了解:) 假设我想以字符串形式以编程方式批量访问 B 的属性(阅读问题的第 1 段)。假设我想根据名称复制方法(或属性)。
  • @dan3:那么也许“替代”会起作用。这取决于您是否需要已在类代码中设置属性,例如,如果您需要在注释行之后的行上使用值 f2,那么延迟 __dict__ 操作将无济于事你。
  • @dan3:是的,我也意识到了这一点(这就是为什么我在上一条评论中改变了我的例子,这是一个糟糕的例子......)
  • 哦,我考虑过在定义后修改 B(这当然有效),但这并不能回答原始 Q(在类初始化程序中将 B 的属性作为字符串访问)。也许这是不可能的......无论如何我都会投票。
猜你喜欢
  • 1970-01-01
  • 2010-10-31
  • 2022-08-16
  • 1970-01-01
  • 2021-06-02
  • 2012-10-17
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
相关资源
最近更新 更多