类范围内的动态实例变量

Posted

技术标签:

【中文标题】类范围内的动态实例变量【英文标题】:Dynamic Instance variables within Class Scope 【发布时间】:2011-08-09 17:02:23 【问题描述】:

我有一个类是 SQLAlchemy 声明性基础的后代。我需要编写一个桥对象,它将在声明性基础和我正在运行的另一个系统之间进行转换,但我希望桥对象不必确切知道我的记录中有哪些列。在这种情况下,我想要一种方法来列出记录中的所有列,避免在两个仅含糊相关的地方维护属性列表,同时最大限度地减少复制。这意味着我可以创建列属性,然后维护一个单独的列名列表,但同样,复制是一个因素——如果我以后想改变这个东西的结构,我必须在两个地方都改变它。

既然如此,我考虑将列定义粘贴在一个字典中,然后遍历该字典以使用 setattr 创建每个列定义,如下所示:

    for k,v in self.__column_dict.items():
        setattr(self,k,
                sqlalchemy.Column(v['column_type'],**v.get('opts',)),
                )

现在,问题来了:如果我将这段代码粘贴到类级别,self 尚未定义,我会收到一个错误(这是有道理的)。如果我把它放在__init__ 中,self 被定义了,但是 SQLAlchemy 的声明性基础给出了一个错误,因为在类完成定义时表上没有主键(这是有道理的,因为这些属性不会设置到运行时)。

所以,说了这么多,我如何在不使用 eval 的情况下获得我想要的东西,或者在这里使用 eval 是我唯一的选择?


编辑:我接受了下面的解决方案,但没有 100% 使用它(尽管它绝对有助于让我得到答案)。这就是我最终做的事情: (注意DeclarativeMeta是sqlalchemy.ext.declarative的一部分,和declarative_base是同一个包)

class MyMeta(DeclarativeMeta):
    def __new__(meta, classname, bases, dict_):
        klass = type.__new__(meta, classname, bases, dict_)
        if dict_.has_key('__classinit__'):
            klass.__classinit__ = staticmethod(klass.__classinit__.im_func)
        klass.__classinit__(klass, dict_)
        return klass

然后,在我的派生类中,

class DerivedClass(declarative_base()):
    __tablename__ = 'yaddayadda'
    __metaclass__ = MyMeta

    def __classinit__(klass, dict_):
        klass.__column_dict = <column dict here>

        for k,v in klass.__column_dict.items():
            setattr(klass,k,
                <fancy column stuff>
                )
   ...<rest of class definition>...

【问题讨论】:

【参考方案1】:

这可以通过向 declarative_base 提供一个元类来完成 在那个元类中

from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta

class MyMeta(DeclarativeMeta): 

    def __init__(klass, classname, bases, dict_): 

        for k, v in dict_.items():

            if k.endswith('__column_dict'):

                for name, datatype, is_pk in v:

                    setattr(klass, name, Column(name, datatype, primary_key=is_pk))

        return DeclarativeMeta.__init__(klass, classname, bases, dict_)

Base = declarative_base(metaclass=MyMeta)

class Bob(Base):
    __tablename__ = 'bob'
    __column_dict = [('a', Integer, True), ('b', Integer, False)]

bob = Bob()
print bob.a
print bob.b

【讨论】:

非常感谢。可以肯定的是,我是否也应该在最后调用 super 以防万一?例如,返回 super(RecordMeta, klass).__init__(classname, bases, dict_)? 回来,当我实现这个时,我得到“类型对象'MyDerivedObject'没有属性'_MyMeta__column_dict”。它似乎将 column_dict 的搜索绑定到 RecordMeta,我如何告诉它实际查看的位置? 对不起,这是我提供的一个草率的例子。添加了一个新的和完整的。 谢谢,非常漂亮,虽然在属性名称上使用“endswith”会让人感到模棱两可。

以上是关于类范围内的动态实例变量的主要内容,如果未能解决你的问题,请参考以下文章

当私有实例变量超出范围时Java终止线程

Python类变量范围不符合文档[重复]

静态变量与实例变量:区别?

实例方法内的私有变量为什么不会出现线程安全问题

是否可以从动态变量实例化一个类?

本地 和 实例 变量之间有啥区别