Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在对象上要求python的`with`语句_Python_Sqlalchemy - Fatal编程技术网

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

如何在对象上要求python的`with`语句,python,sqlalchemy,Python,Sqlalchemy,我正在使用python的SQLAlchemy工具包编写一个数据库访问层。我一直在研究使层线程安全和资源负责的最佳方法。为了实现这一点,我偶然发现了在db引擎创建期间可以传递的NullPool对象 为了让DAOs资源负责,我考虑过在try/catch/finally块结束时关闭会话,但是我不想采用这种方法,因为我希望连接保持打开状态,只要使用者需要DAO进行数据库操作。作为在对象生命周期结束时关闭连接的一种方法,我想使用with语句uuuu enter和uuu exit方法。enter方法将返回对

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

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

然而,我想做的是,要求用pythonwith语句实例化对象,如果以任何其他方式创建对象,实例化就会失败

有没有办法做到这一点?我已将课程和使用说明包括在下面:

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对象,本例中的连接是会话,我不建议您这样做,因为这与OOP中的典型假设相反,但是如果您确实需要完成这一点,您可以确保不能调用在外部实例化的对象的方法,但根本没有办法禁止创建这样的实例

因为context manager确实向构造函数提供了任何东西,而只是调用enter和exit,所以我能看到的唯一方法是确保您的类提供的所有方法都只需首先检查enter之前是否被调用,我会将所有逻辑移到enter而不是constructor,因为如果您想禁止任何其他用法,那么在init中执行总是跟在enter后面的任何操作有什么意义

 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 + "]")  
或者,如果您想保留以前的方法,只需在enter中添加一个新标志


我不确定这是个好主意。如果要将该类封装到另一个类中该怎么办?该类的实例化将如何返回给调用方?您可以在uuu enter_uuu中设置一个标志,如果未设置该标志,则在select_uALL和所有其他数据访问方法中引发一个异常。使用一个在uu enter_uuuu中实例化真实对象的包装器。但是真的:不要在with之后的表达式是一个任意的表达式,并且没有明显的方法让它“知道”它正在with中使用。但是,即使有,这似乎是一个坏主意,因为它违反了实例创建的默示契约,实际上,还违反了与的默示契约。这在某种程度上是一种高度动态语言的本质,试图对其进行黑客攻击或设计可能弊大于利,因为除此之外,它会违背此类代码用户的基本期望。海报似乎希望防止在,尽管如此。@pvg问题中提供的示例并没有说明实例化,只说明在外部创建的实例上调用方法。是吗?我想我看不出有什么能阻止它returning@pvg这就是self._is_context_manager部分所做的:此处与_init__无关,海报希望对select_all方法进行保护。在这种情况下,第一个解决方案是移动所有要输入的内容,这是您正在寻找的-如果调用enter,将创建会话,这发生在block的内部。
 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 + "]")