为什么这个 class 和相应的属性在请求之间没有被破坏?

Why is this class and corresponding attribute not being destroyed between requests?

我完全无法理解这种行为。我昨天或前一天问了一个问题,认为这是 bottle.py 的问题,但在尝试了各种可能的解决方案之后,甚至将我的应用程序转换为 Flask,我已经将行为确定为一个非常简单的行为,class,但我不知道为什么会这样。这让我很困惑,但如果 任何人 可以解释一下,我真的很想理解这一点。

好的,所以首先我有一个名为 Page 的 class,它稍微简化了模板的设置,这是令人讨厌的 class:

class Page:
    """The page object constructs the webpage and holds associated variables and templates"""
    def __init__(self, template=None, name = '', title='',template_vars={}, stylesheets=[], javascript=[]):
        # Accepts template with or without .html extension, but must be added for lookup
        self.stylesheet_dir = '/css'
        self.javascript_dir = '/js'

        self.template = template
        self.template_vars = {}
        self.name = name
        self.title = title

        self.stylesheets = stylesheets
        self.javascript = javascript

        self.template_vars['debug'] = _Config.debug
        self.template_vars['title'] = self.title
        self.template_vars['stylesheets'] = self.stylesheets
        self.template_vars['javascript'] = self.javascript


    def render(self):
        """Should be called after the page has been constructed to render the template"""
        if not self.template.endswith(_Config.template_extension):
            self.template += '.' + _Config.template_extension

        if not self.title:
            if self.name:
                self.title = _Config.website + _Config.title_delimiter + self.name
            else:
                # If title not given use template name
                self.title = _Config.website + _Config.title_delimiter + self.template.rstrip('.html')

        try:
            template = env.get_template(self.template)
        except AttributeError:
            raise (AttributeError, 'template not set')

        rendered_page = template.render(self.template_vars)

        return rendered_page

    def add_stylesheet(self, name, directory=None):
        # Sanitize name
        if not name.endswith('.css'):
            name += '.css'
        if name.startswith('/'):
            name = name.lstrip('/')

        if not directory:
            directory = self.stylesheet_dir
        self.template_vars['stylesheets'].append(directory + '/' + name)

    def add_javascript(self, name, directory=None):
        # Sanitize name
        if not name.endswith('.js'):
            name += '.js'
        if name.startswith('/'):
            name = name.lstrip('/')

        if not directory:
            directory = self.javascript_dir
        self.template_vars['javascript'].append(directory + '/' + name)

这是出现问题的路线示例:

@route('/create_account', method=['GET','POST'])
def create_account():
    dbsession = db.Session()
    page = Page('create account')
    page.add_javascript('create_account.js')
    if request.method == 'GET':
        page.template = 'create_account'
        page.template_vars['status'] = None

        document = page.render()
        dbsession.close()
        return document

    elif request.method == 'POST':
        # Omitted

问题出在方法 Page.add_javascript() 上。下次我转到 /create_account 页面时,它不会创建新的 Page 对象,而是重用旧的。我知道这一点,因为如果我两次访问该页面,我将在返回的 html 文档中有两个完整的 create_account.js(模板只是运行一个 for 循环并为传入的任何 js 文件创建一个标记那个清单)。如果我去 3 次,它会被列出 3 次,40 次,40 次等等。但是现在,如果我只是将其更改为使用初始化程序而不是 add_javascript 方法,问题就会消失。

@route('/create_account', method=['GET','POST'])
def create_account():
    dbsession = db.Session()
    page = Page('create account', javascript=['create_account.js'])
    if request.method == 'GET':
        page.template = 'create_account'
        page.template_vars['status'] = None

        document = page.render()
        dbsession.close()
        return document

    elif request.method == 'POST':

但是我怀疑还是有问题,为了我自己的理智,我需要了解这到底是怎么回事。 add_javascript 方法在同一个页面对象上被调用两次的幕后可能发生了什么?在创建页面对象的 new 实例后立即 调用该方法,它可能从哪里获取 template_vars 的旧内容?

问题是您为 Page.__init__ 函数使用了可变默认值。参见 http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments

所以你 do 在每个请求上得到一个新的 Page 实例,但是保存你的 javascript 等的 lists/dictionaries 是重新使用过。

将 list/dict 默认参数值替换为 None,检查 __init__ 中的 None