我定义了一个简单的模型,并在类中添加了一些非SQL相关的属性。
如果属性是int或字符串类型,那么事情正在发挥作用。但如果它是字典或列表类型,那么神奇地在模型类的所有不同实例上使用相同的列表。
示例:
# Sample model class
class TestClass(Base):
# SQL Mappings
__tablename__ = 'test1'
pid = Column("id", Integer, primary_key=True)
name = Column('name', String)
# Non SQL related attributes
works_var = 0
works_not_var = []
# ...
在查询处理检索到的TestClass实例
之后的其他地方my_test_class.works_not_var.append("testval1")
my_test_class2.works_not_var.append("testval2")
两个附加值最终都在同一个列表中:
print id(my_test_class.works_not_var)
print id(my_test_class2.works_not.var)
Id是一样的。但它适用于“works_var”。我有不同的。
答案 0 :(得分:7)
这与SQLAlchemy完全无关,是标准的Python行为。问题的原因与Python处理类型和解析事物的方式有关。考虑一下这个有趣的例子:
class A(object):
print "Hello"
print "A is now defined"
a = A()
print "I now have an instance of A"
这当然完全无用,但在执行时,观察打印顺序时:
Hello
A is now defined
I now have an instance of A
你能指望这个吗?
# Sample model class
class TestClass(Base):
# SQL Mappings
__tablename__ = 'test1'
pid = Column("id", Integer, primary_key=True)
name = Column('name', String)
# ...
def __init__(self):
self.works_var = 0
self.works_not_var = []
经验法则:将__init__
中的默认参数放在课程级别上。
可能需要更详尽的解释:为什么会出现问题?我不会详细介绍Python如何处理变量。在eevee的Python FAQ: Passing文章中有一个简洁的解释。此外,这里有一个很好的解释:Other languages have "variables"。
凭借这些知识和上面的例子,我们现在知道当语句works_not_var = []
被执行时:它被导入的时刻(或者脚本被启动)。我们也知道为什么这是一个问题:像list
这样的对象是可变的,当你改变它时Python不会移动它的“标记”:而是你创建了一个实例变量。更常见的是,这个问题在函数默认参数上被注意到(并且更容易解释)。考虑这个问题:“Least Astonishment” in Python: The Mutable Default Argument。它解释了它的来源非常好。在一个简洁的例子中:
def f(a=[]):
if len(a) == 0:
print "Oh no, list is empty"
a.append(1)
f()
print "Function executed first time"
f()
print "Function executed second time"
输出:
Oh no, list is empty
Function executed first time
Function executed second time
列表是在解析它们时创建的,而不是执行时间。另一个例子说明这可能会失败并产生愚蠢的问题:
from datetime import datetime
from time import sleep
def f(time=datetime.now()):
print time
f()
sleep(1)
f()
f(datetime.now())
所以你创建了一个函数,它获得的时间默认为当前时间。好吧,不是那么多。它默认为程序启动的时间,但不是当前时间。如果你运行它,你会得到:
2013-08-20 16:14:29.037069
2013-08-20 16:14:29.037069
2013-08-20 16:14:30.038302
但是你会期望第二次和第三次几乎相等而且在“第二”级别没有差别。问题再次出现:解析函数未执行时执行默认参数datetime.now()
。
为此,还有一个简单的解决方案(尽管我可能会说我觉得它并不像我想的那样漂亮):
def f(now=None):
if now is None:
now = datetime.now()
我希望这个解释有所帮助。