为什么从 Base 继承的 SQLAlchemy 类不需要构造函数? [英] Why do SQLAlchemy classes inheriting from Base not need a constructor?

查看:29
本文介绍了为什么从 Base 继承的 SQLAlchemy 类不需要构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用从 Base 类继承的 SQLAlchemy 对象,我可以将参数传递给未在构造函数中定义的变量的类:

With SQLAlchemy objects inheriting from the Base class I can pass arguments to a class for variables which aren't defined in a constructor:

from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    name = Column(String(50))
    fullname = Column(String(50))
    password = Column(String(12))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (
                                self.name, self.fullname, self.password)

ed_user = User(name='ed', fullname='Ed Jones', password='edspassword')

当然,如果我尝试将这样的参数传递给另一个类,以同样的方式设置类变量,我会得到一个错误:

of course, if I tried passing arguments like that to another class, to set class variables in the same way I'd get an error:

In [1]: class MyClass(object):
   ...:     i = 2
   ...:     

In [2]: instance = MyClass(i=9)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-6d3ec8445b00> in <module>()
----> 1 instance = MyClass(i=9)

TypeError: object.__new__() takes no parameters

SQLalchemy 做了什么诡计?他们为什么不直接使用构造函数(即 __init__ 方法)?

What sort of trickery is SQLalchemy doing? Why don't they just use a constructor (i.e an __init__ method)?

推荐答案

您实际上在这里问了两个问题.

You actually asked two questions here.

首先,您问SQLAlchemy 正在做什么类型的诡计".使用一个名为 Metaclasses 的概念在幕后还有更多的事情要做动态创建 Base 类.

First, you asked "What sort of trickery is SQLAlchemy doing". There is a bit more going on behind the scenes using a concept called Metaclasses to dynamically create the Base class.

但实际上,您只需要知道 SQLAlchemy 正在动态设置元素的 Base 类中定义一个构造函数(尽管是以一种迂回的方式).

But in reality all you need to know is that SQLAlchemy is defining a constructor (albeit in a roundabout way) in the Base class that dynamically sets the elements. Here is actually the implementation of that method (at least as it exists at the time of this answer):

def _declarative_constructor(self, **kwargs):
    """A simple constructor that allows initialization from kwargs.

    Sets attributes on the constructed instance using the names and
    values in ``kwargs``.

    Only keys that are present as
    attributes of the instance's class are allowed. These could be,
    for example, any mapped columns or relationships.
    """
    cls_ = type(self)
    for k in kwargs:
        if not hasattr(cls_, k):
            raise TypeError(
                "%r is an invalid keyword argument for %s" %
                (k, cls_.__name__))
        setattr(self, k, kwargs[k])

基本上,这是动态确定关键字参数,并自动设置新对象的属性.您可以将Base 类想象成如下所示,但请记住,它实际上有点复杂(您可以阅读代码以了解更多信息):

Basically, this is dynamically determining the keyword arguments, and setting the attributes on the new object automatically. You can imagine theBase class as looking like the following, although keep in mind it is actually a bit more complex (you can read the code to find out more):

class Base(object):
    def __init__(self, **kwargs):
        cls_ = type(self)
        for k in kwargs:
            if not hasattr(cls_, k):
                raise TypeError(
                    "%r is an invalid keyword argument for %s" %
                    (k, cls_.__name__))
            setattr(self, k, kwargs[k])

如果您创建了上述代码,则您创建的任何继承自 Base 的类都将自动获得自动填充属性的属性的能力,只要该属性已在班级.这是因为典型的 Python 面向对象的继承结构:如果您没有在 User 对象上定义方法,Python 会查找在基类上定义的方法(在这种情况下,Base 方法)并将使用它.这适用于 __init__ 方法,就像任何其他方法一样.

If you created the above code, any class that you create that inherits from Base would automatically get the ability to have the attributes of the property auto-filled as long as the attribute was already defined on the class. This is because of the typical Python Object-Oriented inheritance structure: if you don't define a method on your User object, Python looks for the method being defined on a base class (in this case the Base method) and will use that instead. This goes for the __init__ method, just like any other method.

您的第二个问题是他们为什么不只使用构造函数(即 __init__ 方法)?"好吧,正如我们上面所描述的,他们做到了!他们将_declarative_constructor 方法设置为Base 类的__init__ 属性,从而有效地设置了对象构造的默认逻辑.当然,这只定义了默认值;如果您愿意,您可以随时覆盖它...

Your second question is "Why don't they just use a constructor (i.e an __init__ method)?" Well, as we've described above, they do! They set the _declarative_constructor method to the __init__ attribute the Base class, effectively setting the default logic for object construction. Of course, this only defines the default; you can always override this if you want to...

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    name = Column(String(50))
    fullname = Column(String(50))
    password = Column(String(12))

    def __init__(self, name):
        self.name = name
        self.fullname = name
        self.password = generate_new_password()

# The following will now work...
ed_user = User('ed')
mark_user = User(name='mark')

# ...but this will not...
problem_user = User(name='Error', fullname='Error M. McErrorson', password='w00t')

这篇关于为什么从 Base 继承的 SQLAlchemy 类不需要构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆