Skip to main content
 首页 » 编程设计

python之正确自动生成 __str__() 实现也适用于 sqlalchemy 类

2025年05月04日12unruledboy

我想干净整洁地显示/打印我的 sqlalchemy 类。

在Is there a way to auto generate a __str__() implementation in python? 答案You can iterate instance attributes using vars, dir, ...:...在简单类的情况下有帮助。

当我尝试将它应用于 Sqlalchemy类(如来自 Introductory Tutorial of Python’s SQLAlchemy 的那个 - 见下文),我得到 - 除了成员变量之外,还有以下条目作为成员变量:

_sa_instance_state=<sqlalchemy.orm.state.InstanceState object at 0x000000004CEBCC0>

如何避免此条目出现在 __str__ 中?代表?

为了完整起见,我也将链接的 stackoverflow 问题的解决方案放在下面。

import os 
import sys 
from sqlalchemy import Column, ForeignKey, Integer, String 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import relationship 
from sqlalchemy import create_engine 
 
Base = declarative_base() 
 
class Person(Base): 
    __tablename__ = 'person' 
    # Here we define columns for the table person 
    # Notice that each column is also a normal Python instance attribute. 
    id = Column(Integer, primary_key=True) 
    name = Column(String(250), nullable=False) 

如前所述,这是来自 Is there a way to auto generate a __str__ () implementation in python? 的解决方案:

def auto_str(cls): 
    def __str__(self): 
        return '%s(%s)' % ( 
            type(self).__name__, 
            ', '.join('%s=%s' % item for item in vars(self).items()) 
        ) 
    cls.__str__ = __str__ 
    return cls 
 
@auto_str 
class Foo(object): 
    def __init__(self, value_1, value_2): 
        self.attribute_1 = value_1 
         self.attribute_2 = value_2 

应用:

>>> str(Foo('bar', 'ping')) 
'Foo(attribute_2=ping, attribute_1=bar)' 

请您参考如下方法:

这是我用的:

def todict(obj): 
    """ Return the object's dict excluding private attributes,  
    sqlalchemy state and relationship attributes. 
    """ 
    excl = ('_sa_adapter', '_sa_instance_state') 
    return {k: v for k, v in vars(obj).items() if not k.startswith('_') and 
            not any(hasattr(v, a) for a in excl)} 
 
class Base: 
 
    def __repr__(self): 
        params = ', '.join(f'{k}={v}' for k, v in todict(self).items()) 
        return f"{self.__class__.__name__}({params})" 
 
Base = declarative_base(cls=Base) 

任何继承自 Base 的模型都将定义默认的 __repr__() 方法,如果我需要做一些不同的事情,我可以覆盖该特定类上的方法.

它排除了用前导下划线表示的任何私有(private)属性的值、SQLAlchemy 实例状态对象以及字符串中的任何关系属性。我排除了关系属性,因为我通常不希望 repr 导致关系延迟加载,并且关系是双向的,包括关系属性会导致无限递归。

结果如下:ClassName(attr=val, ...)。

--编辑--

我上面提到的 todict() 函数是一个助手,我经常调用它从 SQLA 对象构造一个 dict,主要用于序列化。我在这种情况下懒洋洋地使用它,但它不是很有效,因为它正在构造一个 dict (在 todict() 中)来构造一个 dict(在 __repr__() 中)。从那以后,我修改了模式以调用生成器:

def keyvalgen(obj): 
    """ Generate attr name/val pairs, filtering out SQLA attrs.""" 
    excl = ('_sa_adapter', '_sa_instance_state') 
    for k, v in vars(obj).items(): 
        if not k.startswith('_') and not any(hasattr(v, a) for a in excl): 
            yield k, v 

然后基础Base看起来是这样的:

class Base: 
 
    def __repr__(self): 
        params = ', '.join(f'{k}={v}' for k, v in keyvalgen(self)) 
        return f"{self.__class__.__name__}({params})" 

todict() 函数也利用了 keyvalgen() 生成器,但不再需要构建 repr。