【问题标题】:Dynamic class field creation before metaclass machinery在元类机器之前创建动态类字段
【发布时间】:2011-03-26 07:12:29
【问题描述】:

我正在尝试在类似于此的代码中摆脱exec

class A(object):
    for field in ['one', 'two', 'three']:
        exec '%s = "%s value"' % (field, field)

...这样:

>>> A.one
'one value'
>>> A.two
'two value'
>>> A.three
'three value'

编辑:并且还满足主题中提到的要求,即A.one'one value',在A 被实例化之前(不要被误认为A() 实例化)。

有办法吗?

【问题讨论】:

  • 你能解释一下标题与问题有什么关系吗?
  • 这很恶心。你为什么要这样做?
  • @gnibbler 是的。我试图在类被实例化之前做到这一点。因此,在引用 A 时调用的元类已经适用于那些动态创建的字段。
  • @katrielalex 我认为它比使用 django.forms.BaseForm 和调用 type() 更具可读性......但这个问题本身似乎很有趣。我一开始以为我无法在A 的body 中执行for 循环,但是当我发现可以时,我想更进一步。

标签: python dynamic


【解决方案1】:
>>> values = 'one', 'two', 'three'
>>> A = type('A', (object,), {i: i + ' value' for i in values})
>>> A.one
'one value'
>>> A.three
'three value'

【讨论】:

    【解决方案2】:

    使用the setattr function

    class A(object):
      pass
    
    for field in ['one', 'two', 'three']:
      setattr(A, field, field + ' value')
    

    【讨论】:

    • 我应该重复主题的要求......虽然现在我不确定它是否适用于exec的版本。
    • 我对其进行了测试,exec 的版本在__metaclass__.__new__() 之前执行...我还尝试在发布之前将 setattr() 放入A,但它没有像@987654328 一样工作@ 尚未定义。
    【解决方案3】:

    我只是从元类继承并在那里做

    class MyMetaClass(MetaClass):
        def __new__(meta, classname, baseclasses, classdict):
            fields = classdict['__myfields__']
            for field in fields:
                classdict[field] = field + ' value'
            del classDict['__myfields__']
            MetaClass.__new__(meta, classname, baseclasses, classdict)
    

    那么你可以这样做:

    class A(object):
        __metaclass__ = MyMetaClass
        __myfields__ = ['one', 'two', 'three']
    

    【讨论】:

      【解决方案4】:

      你说你想动态生成元类可以处理的东西。有一种非常简单的方法可以实现这一目标,而无需借助像 exec 这样的黑客技术。您所要做的就是以不同的方式考虑这一点:修改元类以便在那里生成名称。

      class AutoFieldMeta(type):
          def __new__(mcs, name, bases, d):
              for field in d.get('AUTOFIELDS', ()):
                  d[field] = field + ' value'
              return type.__new__(mcs, name, bases, d)
      
      class A(object, metaclass=AutoFieldMeta):
          AUTOFIELDS = ('one', 'two', 'three')
      
      >>> A.one
      'one value'
      >>> 
      

      如果您不想修改现有的元类,您可以将其子类化。

      【讨论】:

      • 领先我 33 秒,但我会花额外的时间来提高质量;)
      • 这完成了这项工作,值得 +1,但我仍然在这里等待,希望您可以像原始代码一样在三个清晰的行中完成它,而无需 exec。如果有办法打电话给=(声明...)。
      • 如果忽略元类定义,那么它只有两行,而且比原来的更清晰。您可以在不使用 exec 的情况下以与原始行数相同的行数轻松完成此操作,但您真的不想走这条路 [在您可以分配给 vars()/locals() 的类正文中,并且与在函数中分配给locals() 不同,它可能会在本周真正起作用]。 vars()[field] = "%s value" % field
      • 从提供者的角度来看,元类非常适合声明式编程。然后你是在解释类内容(对我来说它看起来仍然很麻烦,但这不是你作为用户必须关心的事情)。我想说的是,也许类主体应该并且还没有)成为在创建时进行修改的一个明显地方。您应该能够为此使用语言能力。这就像语法和语义之间的区别。使用元类,你对语义采取行动,我需要对语法采取行动。语义已经由 Django 提供,我不想干涉。
      • 你当然是对的,这不是标准化的,不保证下周可以工作。
      【解决方案5】:

      这也有效,我认为这是一个快乐的结局......

      class A(object):
          for a in ['one', 'two', 'three']:
              locals().update({a: a + ' value'})
      

      对于任何在 Python 中搜索赋值表达式的人来说,这与 aaronasterling 答案的心情相同:):

      http://code.activestate.com/recipes/202234-assignment-in-expression/

      【讨论】:

      • 或者,如果你想要一个班轮:vars().update((field, field + " value") for field in ('one', 'two', 'three'))ick
      【解决方案6】:

      让我先说这是非常骇人听闻的,但消除了执行官。当类语句中的代码正在执行时,我们只是将属性粘贴在当前堆栈帧上。它只保证在 CPython 上工作,并且在手册中有警告不要这样做。我强烈建议您使用 duncans 元类解决方案。

      import sys
      
      class A(object):
          for field in ['one', 'two', 'three']:
              sys._getframe().f_locals[field] = field + ' value'
      

      【讨论】:

        猜你喜欢
        • 2017-06-28
        • 1970-01-01
        • 2018-08-24
        • 1970-01-01
        • 1970-01-01
        • 2011-05-08
        • 1970-01-01
        • 2012-03-13
        相关资源
        最近更新 更多