简体   繁体   English

控制python对象的实例化

[英]Controlling the instantiation of python object

My question does not really have much to do with sqlalchemy but rather with pure python. 我的问题实际上与sqlalchemy无关,而与纯python有关。

I'd like to control the instantiation of sqlalchemy Model instances. 我想控制sqlalchemy Model实例的实例化。 This is a snippet from my code: 这是我的代码的一部分:

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

I want to achieve that whenever an entry is instantiated ( Tag('django') ) that a new instance should be created only if there is not yet another tag with the name django inside the database. 我想实现的是,每当实例化一个条目( Tag('django') )时,仅当数据库中还没有另一个名称为django标签时,才应创建一个新实例。 Otherwise, instead of initializing a new object, a reference to the already existent row inside the database should be returned by ( Tag('django') ). 否则,代替初始化新对象,应该由( Tag('django') )返回对数据库中已经存在的行的引用。

As of now I am ensuring the uniqueness of tags inside the Post Model: 到目前为止,我正在确保Post Model中标签的唯一性:

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)

It does its job but still I'd like to know how to ensure the uniqueness of tags inside the Tag class itself so that I could write the _set_tags method like this: 它可以完成工作,但是我仍然想知道如何确保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))


While writing this question and testing I learned that I need to use the __new__ method. 在编写此问题和测试时,我了解到我需要使用__new__方法。 This is what I've come up with (it even passes the unit tests and I didn't forget to change the _set_tags method): 这是我想出的(它甚至通过了单元测试,并且我没有忘记更改_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)

What bothers me are two things: 让我困扰的是两件事:

First: I get a warning: 首先:我收到警告:

DeprecationWarning: object.__new__() takes no parameters

Second: When I write it like so I get errors (I also tried to rename the paramater name to n but it did not change anything) : 第二:当我这样写时遇到错误(我也尝试将参数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)

Errors (or similar): 错误(或类似错误):

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

I hope you can help me! 我希望你能帮帮我!

I use class method for that. 我为此使用类方法。

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

And then 接着

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

As for __new__ , you should not confuse it with __init__ . 至于__new__ ,您不应将其与__init__混淆。 It is expected to be called w/out args, so even if your own constructor asks for some, you should not pass them to super/object unless you know that your super needs them. 它应该被称为w / out args,因此,即使您自己的构造函数需要一些参数,也不应将它们传递给super / object,除非您知道您的super需要它们。 Typical invocation would be: 典型的调用为:

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

However this will not work as expected in your case, since it calls __init__ automatically if __new__ returns instance of cls . 但是,这在您的情况下将无法正常工作,因为如果__new__返回cls实例,它将自动调用__init__ You would need to use metaclass or add some checks in __init__ . 您将需要使用元类或在__init__添加一些检查。

Don't embed this within the class itself. 不要将其嵌入类本身。

Option 1. Create a factory that has the pre-existing pool of objects. 选项1.创建一个工厂,该工厂具有预先存在的对象池。

tag_pool = {}
def makeTag( name ):
    if name not in tag_pool:
        tag_pool[name]= Tag(name)
    return tag_pool[name]

Life's much simpler. 生活要简单得多。

tag= makeTag( 'django' )

This will create the item if necessary. 如有必要,这将创建项目。

Option 2. Define a "get_or_create" version of the makeTag function. 选项2.定义makeTag函数的“ get_or_create”版本。 This will query the database. 这将查询数据库。 If the item is found, return the object. 如果找到该项目,则返回该对象。 If no item is found, create it, insert it and return it. 如果未找到任何项目,请创建它,将其插入并返回。

Given the OP's latest error msg: 鉴于OP的最新错误消息:

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

it seems that somewhere the class is getting instantiated without the name parameter, ie just Tag() . 似乎该类在没有 name参数的情况下被实例化,即只是Tag() The traceback for that exception should tell you where that "somewhere" is (but we're not shown it, so that's how far as we can go;-). 该异常回溯应该告诉说:“某处”(但我们并没有显示它,所以这是多远,我们可以去;-)。

That being said, I agree with other answers that a factory function (possibly nicely dressed up as a classmethod -- making factories is one of the best uses of classmethod , after all;-) is the way to go, avoiding the complication that __new__ entails (such as forcing __init__ to find out whether the object's already initialized to avoid re-initializing it!-). 话虽这么说,我与一个工厂函数(可能很好地把自己打扮成一个其他的答案一致classmethod -做工厂是最好的用途之一classmethod ,毕竟;-)是要走的路,避免了并发症的发生__new__ (例如,强制__init__查明对象是否已初始化,以避免重新初始化它!)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM