使用从Base
类继承的SQLAlchemy对象,我可以将参数传递给类,而不是在构造函数中定义变量:
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')
当然,如果我尝试将这样的参数传递给另一个类,以同样的方式设置类变量我会收到错误:
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__
方法)?
答案 0 :(得分:11)
你实际上在这里问了两个问题。
首先,你问“SQLAlchemy在做什么样的诡计”。幕后使用名为Metaclasses的概念动态创建Base
类,还有更多内容。
但实际上你需要知道的是,SQLAlchemy在动态设置元素的Base
类中定义了一个构造函数(尽管是以迂回的方式)。 Here实际上是该方法的实现(至少在答案时存在):
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])
基本上,这是dynamically determining the keyword arguments,并自动设置新对象的属性。您可以将Base
类看作如下所示,但请记住它实际上有点复杂(您可以阅读代码以了解更多信息):
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__
方法,就像任何其他方法一样。
你的第二个问题是“他们为什么不使用构造函数(即__init__
方法)?”好吧,正如我们上面所描述的那样,他们做到了!他们将_declarative_constructor
方法设置为__init__
类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 __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')