如何在普通类中使用等效于 __post_init__ 的方法?

Hap*_*nja 7 python sqlalchemy

我想存储在我的代码中使用的实体并避免多次出现。因此,我的想法是使用一种__init__方法来收集我的类的主要数据,然后使用一种__post_init__方法从我的类对象中计算 id。这是代码:

class Worker(Base):
    __tablename__='worker'
    id = Column(Integer,primary_key=True)
    profile=Column(String(100),nullable=False)
    useragent=Column(String(100),nullable=False)
    def __init__(self,useragent,profile):
        """ specify the main information"""
        print('init')
        self.profile= profile
        self.useragent=useragent
    def __post_init__(self):
        """ compute an id based on self, the worker"""
        self.id=id(self)
        print('dans post init')
Run Code Online (Sandbox Code Playgroud)

在此示例中,__init__可以使用该方法,但它不会__post_init__像我们对 dataclass 所期望的那样运行该方法,例如。

如何在执行该方法后立即运行此__init__方法?

小智 7

我使用以下方法实现了类似的行为__init_subclass__

class Parent:
    def __init_subclass__(cls, **kwargs):
        def init_decorator(previous_init):
            def new_init(self, *args, **kwargs):
                previous_init(self, *args, **kwargs)
                if type(self) == cls:
                    self.__post_init__()
            return new_init

        cls.__init__ = init_decorator(cls.__init__)

    def __post_init__(self):
        pass


class Child(Parent):
    def __init__(self):
        print('Child __init__')

    def __post_init__(self):
        print('Child __post_init__')


class GrandChild(Child):
    def __init__(self):
        print('Before calling Child __init__')
        Child.__init__(self)
        print('After calling Child __init__')

    def __post_init__(self):
        print('GrandChild __post_init__')


child = Child()
# output:
#  Child __init__
#  Child __post_init__


grand_child = GrandChild()
# output:
#  Before calling Child __init__
#  Child __init__
#  After calling Child __init__
#  GrandChild __post_init__
Run Code Online (Sandbox Code Playgroud)


Mar*_*ers 6

__post_init__方法是特定于dataclasses库的,因为类__init__上的方法dataclass是生成的,覆盖它首先会完全破坏生成它的目的。

另一方面,SQLAlchemy 提供__init__了基础模型类的实现(使用 为您生成declarative_base())。设置默认值后,您可以通过super().__init__(). 考虑到SQLAlchemy-provided__init__方法只接受关键字参数:

def __init__(self, useragent, profile):
    """specify the main information"""
    id = generate_new_id(self)
    super().__init__(id=id, useragent=useragent, profile=profile)
Run Code Online (Sandbox Code Playgroud)

如果您需要先等待其他列获得更新的值(因为它们可能将 Python 函数定义为 a default),那么您也可以在调用 之后运行函数super().__init__(),然后将其分配给self

def __init__(self, useragent, profile):
    """specify the main information"""
    super().__init__(useragent=useragent, profile=profile)
    self.id = generate_new_id(self)
Run Code Online (Sandbox Code Playgroud)

注意:您不希望使用内置id()函数为 SQL 插入的数据生成 id,函数返回的值不保证是唯一的。它们仅对于所有活动 Python 对象的集合是唯一的,并且仅在当前进程中。下次运行 Python 时,或者当对象从内存中删除时,值可以并且将被重用,并且您无法控制它下次或完全不同的过程中生成什么值。

如果您只想创建具有useragentprofile列的唯一组合的行,那么您需要UniqueConstraint表参数中定义 a 。不要尝试在 Python 级别检测唯一性,因为您不能保证另一个进程不会同时进行相同的检查。数据库可以更好地确定您是否有重复值,而不会冒竞争条件的风险:

class Worker(Base):
    __tablename__='worker'
    id = Column(Integer, primary_key=True, autoincrement=True)
    profile = Column(String(100), nullable=False)
    useragent = Column(String(100), nullable=False)

    __table_args__ = (
        UniqueConstraint("profile", "useragent"),
    )
Run Code Online (Sandbox Code Playgroud)

或者您可以使用基于两列的复合主键;主键(复合或其他)必须始终是唯一的:

class Worker(Base):
    __tablename__='worker'
    profile = Column(String(100), primary_key=True, nullable=False)
    useragent = Column(String(100), primary_key=True, nullable=False)
Run Code Online (Sandbox Code Playgroud)