2017-03-27 36 views
2

我在我的新項目上使用SQLAlchemy,並且想要在模型中使用__slots__(在沒有鍊金術的測試版中,__slots__是必要的,因爲創建了大量對象)。但我無法將它們與SQLAlchemy的聲明相結合,收到以下錯誤代碼:在SQLAlchemy模型中使用__slots__

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 

但是,這段代碼可能有點繁瑣寫,你需要爲每個新字段添加相應的屬性。我想知道,有沒有更好的方式做沒有屬性,而仍然使用聲明基礎(不使用古典映射)。

+0

不聲明'名稱=文本(長度= 64,convert_unicode = TRUE)'已經建立正確的描述?所以你有'__slots__ =()'來表示沒有額外的實例變量需要存儲? –

+0

'__slots__'旨在減少內存消耗,並且可能使對象更加靜態,但是(我發現在檢查了您的註釋之後),因爲Base沒有定義它們,所以它們並不完全有用。 '__dict__'仍然存在,你仍然可以添加新的字段,所以它們最多隻能用作指導。 – jOOsko

回答

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 

注意的是,雖然字典返回Ÿlocals函數或方法中是有點特別,其中的修改不影響實際變量,它在類體中本身就是一個普通的字典。

如果你有幾種模式,還是覺得這太哈克,它可以被放在一個元類或__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): 
    ... 

對於元類案件(蟒蛇< 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): 
    ...