Python 方法中的变量和对象创建

1 投票
1 回答
503 浏览
提问于 2025-04-16 23:40

我知道在Python中,方法里声明的变量在外面是看不见的。那么这些变量的生命周期是怎样的呢?我觉得每次调用这个方法的时候,里面声明的变量应该都是新创建的。

我之所以问这个,是因为我遇到了以下情况。我有一个方法,下面是我用的自定义Filter类(这个是用在cherrypy里的,可能不太相关,但我还是加上,以防有什么影响)。

首先是我使用的自定义Filter类:

class Filter():
"""
Class used to filter tables displayed in UI, based on few criteria.
Initialize filter with a list of fields, and correspondinf values.
"""
def __init__(self, display_name= "", fields= [], values= [], operations= []):
    self.display_name = display_name
    self.fields = fields
    self.values = values
    self.selected = False 
    self.operations = operations

接下来是我想说的方法:

@cherrypy.expose
@logged() 
def getfiltereddatatypes(self, name, filters, datatype):
    ...some mumbo jumbo...

    default_filter = self._get_default_filters(inputTree, name)

    #default_filter here can be a object of type Filter if defined or None otherwise

    print "RECIEVED " + str(filters)
    if default_filter is not None:
        print "CREATING NEW"
        new_filter = copy.deepcopy(default_filter)
    else:
        print "CREATING NEW"
        new_filter = Filter()
    [new_filter.fields.append(value) for value in filters['fields']]
    [new_filter.operations.append(value) for value in filters['operations']]
    [new_filter.values.append(value) for value in filters['values']]
    print "LENGTH =" + str(len(new_filter.fields))

    ...Some other mumbo jumbo....

简单来说,我想把作为参数传入的过滤器值添加到filters变量里的默认值中。但我希望每次调用这个方法的时候都能这样做。为了更好地解释这个问题,我加了打印语句。第一次调用方法时:

    RECIEVED {'operations': ['!='], 'fields': ['model.DataType.subject'], 'values': ['w']}
    CREATING NEW
    LENGTH =1

如预期的那样,创建了一个新的Filter对象,并且从接收到的参数中添加了操作、字段和数值,length(fields) = 1。但是在第二次调用时:

    RECIEVED {'operations': ['!='], 'fields': ['model.DataType.subject'], 'values': ['']}
    CREATING NEW
    LENGTH =2

这个行为我就无法解释了。正如你所看到的,if语句中的CREATING NEW分支又被调用了,filters变量里是一个包含一个项目的列表,但结果的长度却是2。如果我不断运行它,结果会一直增加。就好像new_filter = Filter()这个调用返回的是上一次方法调用的new_filter。是什么导致了这种行为呢?我在想,也许如果我用new_filter = copy.deepcopy(Filter()),可能会解决这个问题,但为什么我必须这样做呢?

祝好,
Bogdan

1 个回答

5
def __init__(self, display_name= "", fields= [], values= [], operations= [])

这些列表只会创建一次。所以如果你修改了它们,下次调用的时候就会得到修改后的列表。如果你不想要这种行为,只需把默认值设置为 None,然后加上类似下面的代码:

if fields is None:
    fields = []
self.fields = fields

另一种解决办法是无论传入什么,都创建一个新的列表:

self.fields = list(fields)

关于这种行为,还有一篇详细的文章,可以在这里查看:http://effbot.org/zone/default-values.htm


顺便说一下,你对列表推导式的使用不太恰当。

[new_filter.fields.append(value) for value in filters['fields']]

应该是

new_filter.fields += filters['fields']

撰写回答