这绝对与 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文章中有一个简洁的解释。此外,这里有一个很好的解释:其他语言有“变量”。
有了这些知识和上面的例子,我们现在知道语句何时works_not_var = []
执行:它被导入(或脚本启动)的那一刻。而且我们也知道为什么这是一个问题:像这样的对象list
是可变的,当您更改它时,Python 不会移动它的“标签”:相反,您创建了一个实例变量。这个问题在函数默认参数上更常见(并且更容易解释)。考虑这个 SO 问题:Python 中的“Least Astonishment”:可变默认参数。它很好地解释了它的来源。在一个简洁的例子中:
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()
我希望这个解释有所帮助。