python - 控制python对象的实例化

标签 python sqlalchemy metaprogramming

我的问题实际上与 sqlalchemy 没有太大关系,而是与纯 python 相关。

我想控制 sqlalchemy 模型实例的实例化。这是我的代码片段:

class Tag(db.Model):

    __tablename__ = 'tags'
    query_class = TagQuery
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(), unique=True, nullable=False)

    def __init__(self, name):
        self.name = name

我想实现的是,每当实例化一个条目( Tag('django') )时,只有在没有另一个名为 django 的标签时才应创建一个新实例。数据库里面。否则,不应初始化新对象,而是应通过 ( Tag('django') ) 返回对数据库中已存在行的引用。

到目前为止,我正在确保 Post 内标签的唯一性型号:

class Post(db.Model):

        # ...
        # code code code
        # ...

        def _set_tags(self, taglist):
            """Associate tags with this entry. The taglist is expected to be already
            normalized without duplicates."""
            # Remove all previous tags
            self._tags = []
            for tag_name in taglist:
                exists = Tag.query.filter(Tag.name==tag_name).first()
                # Only add tags to the database that don't exist yet
                # TODO: Put this in the init method of Tag (if possible)
                if not exists:
                    self._tags.append(Tag(tag_name))
                else:
                    self._tags.append(exists)

它完成了它的工作,但我仍然想知道如何确保 Tag 类本身内标签的唯一性,以便我可以编写 _set_tags方法如下:

def _set_tags(self, taglist):
    # Remove all previous tags
    self._tags = []
    for tag_name in taglist:
        self._tags.append(Tag(tag_name))


在编写这个问题和测试时,我了解到我需要使用 __new__方法。这就是我想出的(它甚至通过了单元测试,而且我没有忘记更改 _set_tags 方法):

class Tag(db.Model):

    __tablename__ = 'tags'
    query_class = TagQuery
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(), unique=True, nullable=False)

    def __new__(cls, *args, **kwargs):
        """Only add tags to the database that don't exist yet. If tag already
        exists return a reference to the tag otherwise a new instance"""
        exists = Tag.query.filter(Tag.name==args[0]).first() if args else None
        if exists:
            return exists
        else:
            return super(Tag, cls).__new__(cls, *args, **kwargs)

困扰我的是两件事:

首先:我收到警告:

DeprecationWarning: object.__new__() takes no parameters

第二:当我这样写时,我收到错误(我还尝试将参数 name 重命名为 n 但它没有改变任何东西):

def __new__(cls, name):
    """Only add tags to the database that don't exist yet. If tag already
    exists return a reference to the tag otherwise a new instance"""
    exists = Tag.query.filter(Tag.name==name).first()
    if exists:
        return exists
    else:
        return super(Tag, cls).__new__(cls, name)

错误(或类似错误):

TypeError: __new__() takes exactly 2 arguments (1 given)

希望你能帮助我!

最佳答案

我使用类方法。

class Tag(Declarative):
    ...
    @classmethod
    def get(cls, tag_name):
        tag = cls.query.filter(cls.name == tag_name).first()
        if not tag:
            tag = cls(tag_name)
        return tag

然后

def _set_tags(self, taglist):
    self._tags = []
    for tag_name in taglist:
        self._tags.append(Tag.get(tag_name))

至于__new__,您不应该将其与__init__混淆。它预计会在没有参数的情况下被调用,因此即使您自己的构造函数需要一些参数,您也不应该将它们传递给 super /对象,除非您知道您的 super 需要它们。典型的调用是:

def __new__(cls, name=None): 
    tag = cls.query.filter(cls.name == tag_name).first()
    if not tag:
        tag = object.__new__(cls)
    return tag

但是,在您的情况下,这不会按预期工作,因为如果 __new__ 返回 cls 的实例,它会自动调用 __init__。您需要使用元类或在 __init__ 中添加一些检查。

关于python - 控制python对象的实例化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3506498/

相关文章:

c++ - 推导成员函数参数/返回类型

metaprogramming - 将表达式插入到表达式中

macros - Julia:矢量别名宏

python - 为什么 Elixir/SQLAlchemy 的 session.bind 在线程内设置为 None?

python - 一种优化每天更新一次的数据存储读取的方法

python - 是否可以使用 python 和 matplotlib 在用户定义的函数中绘图?

python - 在python中查找二进制矩阵的零空间

sqlalchemy - 如何使用声明式创建一对一关系

SQLAlchemy - 有什么方法可以用用户输入连接字符串反射(reflect)数据库模式?

python - 与 ctypes 的联合运算不起作用