【问题标题】:Using __slots__ with SQLAlchemy model将 __slots__ 与 SQLAlchemy 模型一起使用
【发布时间】:2017-08-20 08:55:01
【问题描述】:

我在我的新项目中使用 SQLAlchemy,并希望将 __slots__ 与模型一起使用(在没有炼金术的 beta 版本中,__slots__ 是必需的,因为创建了大量对象)。但我无法将它们与 SQLAlchemy 声明结合起来,并得到以下代码错误:

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class NotWorking(Base):
     __tablename__ = 'table1'
     pk = Column(Integer, primary_key=True)
     name = Text(length=64, convert_unicode=True)

     __slots__ = ['name', 'pk']

错误

ValueError: 'name' in __slots__ conflicts with class variable

这应该是__slots__ 修改类来为其中定义的字段创建描述符,因此一种解决方法是使隐藏字段 (_name) 并使模型字段充当属性,如下所示:

class Working(Base):
    __tablename__ = 'table2'
    pk = Column(Integer, primary_key=True)
    name = Text(length=64, convert_unicode=True)

    __slots__ = ['_name', '_pk']

     # Workaround
    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

但是这段代码编写起来可能有点乏味,您需要为每个新字段添加相应的属性。我想知道,有没有更好的方法可以不用属性,同时仍然使用声明性基础(不使用经典映射)。

【问题讨论】:

  • 语句name = Text(length=64, convert_unicode=True) 不是已经创建了正确的描述符吗?所以你有__slots__ = () 表示没有额外的实例变量需要存储?
  • __slots__ 旨在减少内存消耗,并且可能使对象更加静态,但是(我发现在检查您的评论后)因为 Base 没有定义它们,它们并不完全有用. __dict__ 仍然存在,您仍然可以添加新字段,因此它们最多只能用作指导。

标签: python sqlalchemy slots


【解决方案1】:

您可以在类主体上使用简单的 Python 语句来自动创建简单属性,基于 __slots__ 属性本身:

class Working(Base):
    ...

    __slots__ = ['_name', '_pk']

    for _tmp_name in __slots__:
        locals()[_tmp_name[1:]] = property(lambda self, name=_tmp_name: getattr(self, name))
    del _tmp_name

请注意,虽然在函数或方法中返回 y locals 的字典有些特殊,其中对它的修改不会影响实际变量,但它在类主体本身中用作普通字典。

如果您有多个模型,或者觉得这太 hacky,也可以将其放入元类或 __init_subclass__ 方法中(Python 3.6+):

class MyBase(Base):
    def __init_subclass__(cls, *args, **kwargs):
        for name in cls.__slots__:
            setattr(cls, name[1:], property(lambda self, name=name: getattr(self, name)))

class Working(MyBase):
    ...

对于元类情况(python __init__中放入相同的三行:

class MetaBase(type):
    def __init__(cls, name, bases, namespace):
         super().__init__(name, bases, namespace)
         for name in cls.__slots__:
             setattr(cls, name[1:], property(lambda self, name=name: getattr(self, name)))


class Working(Base, metaclass=MetaBase):
    ...

【讨论】:

    猜你喜欢
    • 2013-10-07
    • 1970-01-01
    • 2022-01-05
    • 2015-07-30
    • 2020-06-14
    • 2015-01-05
    • 2022-01-12
    • 2013-09-28
    • 2012-10-30
    相关资源
    最近更新 更多