如何在对象上要求python的`with`语句

时间:2017-03-18 18:37:23

标签: python sqlalchemy

我正在使用python的SQLAlchemy工具包编写数据库访问层。我一直在研究使层线程安全和资源负责的最佳方法。为了实现这一点,我偶然发现了在创建数据库引擎期间可以传递的NullPool对象。

为了让DAO“资源负责”,我考虑在try / catch / finally块结束时关闭会话,但是我不想采用这条路由,因为我希望连接保持打开状态消费者需要DAO进行数据库操作。作为在对象生命周期结束时关闭连接的一种方法,我想使用with语句__enter____exit__方法。 enter方法将返回对象的实例,exit方法将在对象使用结束时执行session.close()

但是,我想要做的是要求使用python with语句对对象进行实例化,使得如果以任何其他方式创建对象,则实例化将失败。

有没有办法实现这个目标?我已经在下面列出了类和用法声明:

class RegionDAO(declarative_base()):                                             

     __tablename__ = 'region'                                                     

     region_id = Column(Integer, primary_key=True)                                
     country_id = Column(Integer, primary_key=True)                               
     description = Column(String, nullable=False)                                 
     code = Column(String, nullable=False)                                        
     region_type = Column(Integer)                                                
     sort_order = Column(Integer)                                                 

     def __init__(self):                                                          
         connection, meta = PG.get_connection()                                   
         Session = sessionmaker(bind=connection)                                  
         self.session = Session()                           

     def __enter__(self):                                                         
         return self                                                              

     def select_all(self):                                                        
         for row in self.session.query(RegionDAO).all():                          
             print("[" + row.description + "] - [" + row.code + "]")              

     def __exit__(self, exc_type, exc_value, traceback):                          
         print("Exit being called, cleaning up session")                          
         self.session.close()                                                     

 #                                                                                
 # main                                                                           
 #                                                                                

 if __name__ == "__main__":

     # This should succeed                                                       
     with RegionDAO() as r:                                                      
         r.select_all()                                                          

     # This should fail                                                                                  
     r = RegionDAO()                                                              
     r.select_all()  

编辑 - 在阅读了一些评论之后,我想我可能会引起一些困惑。我的最终目标是 - 当对象完成使用时,连接关闭。 (在这种情况下连接是由于使用NullPool对象而导致的会话)

1 个答案:

答案 0 :(得分:1)

我并不是建议你这样做,因为这与OOP中的典型假设相反,但是如果你确实需要来实现这一点,你可以确保你不能调用实例化对象的方法在之外,但根本没有办法禁止实例创建。

由于上下文管理器确实为构造函数提供了任何内容,而只是调用输入退出,我能看到的唯一方法就是确保所有方法都是你的类首先检查是否先调用输入,此外,我会将所有逻辑移到输入而不是构造函数(因为如果你想禁止任何其他用法,那么是什么?在init中做任何事情的点,总是后面输入?)

 def __init__(self):                                                          
     self.session = None                       

 def __enter__(self): 
     connection, meta = PG.get_connection()                                   
     Session = sessionmaker(bind=connection)                                  
     self.session = Session()    
     return self                                                              

 def select_all(self):
     if self.session is None:
       raise Exception('This class can only be used as a context manager')                                                        
     for row in self.session.query(RegionDAO).all():                          
         print("[" + row.description + "] - [" + row.code + "]")  

或者如果您想保留以前的方法,只需在输入

中添加新标记
 def __init__(self):                                                          
     connection, meta = PG.get_connection()                                   
     Session = sessionmaker(bind=connection)                                  
     self.session = Session()    
     self._is_context_manager = False

 def __enter__(self): 
     self._is_context_manager = True
     return self                                                              

 def select_all(self):
     if not self._is_context_manager:
       raise Exception('This class can only be used as a context manager')                                                        
     for row in self.session.query(RegionDAO).all():                          
         print("[" + row.description + "] - [" + row.code + "]")