简体   繁体   English

将__slots__与SQLAlchemy模型一起使用

[英]Using __slots__ with SQLAlchemy model

I am using SQLAlchemy on my new project and would like to use __slots__ with models (in beta version without alchemy, __slots__ were necessary because a large number of objects was created). 我在我的新项目中使用SQLAlchemy,并希望将__slots__用于模型(在没有炼金术的测试版中, __slots__是必要的,因为创建了大量对象)。 But I am unable to combine them with SQLAlchemy declarations and get the following error for code: 但我无法将它们与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']

Error 错误

ValueError: 'name' in __slots__ conflicts with class variable

Which is expected as __slots__ modify class to create descriptors for fields defined in them, so one workaround is to make hidden fields (_name) and make model fields act as properties as shown here: 这应该是__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

But this code can be a bit tedious to write and you need to add corresponding property for each new field. 但是这段代码编写起来有点乏味,你需要为每个新字段添加相应的属性。 I am wondering, is there a better way to do without properties, while still using declarative base (without using Classical Mappings). 我想知道,有没有更好的方法没有属性,同时仍然使用声明基(不使用经典映射)。

You can use simple Python statements on the class body to automate the creation of simple properties, based on the __slots__ attribute itself: 您可以在类主体上使用简单的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

Note that while the dictionary returned y locals inside a function or method is somewhat special, in which modifications to it do not affect the actual variables, it works as a plain dictionary in the class body itself. 请注意,虽然字典在函数或方法中返回了y locals有些特殊,但是对它的修改不会影响实际变量,它在类主体本身中作为普通字典工作。

If you have several models,or find this too hacky, it can be put in a metaclass or __init_subclass__ method as well (Python 3.6+): 如果你有几个模型,或者发现它太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):
    ...

For the metaclass case (python < 3.6), just put the same three lines in the metaclass __init__ : 对于元类案例(python <3.6),只需在元类__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):
    ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM