问题
I have the following setup:
def returnList(arg=["abc"]):
return arg
list1 = returnList()
list2 = returnList()
list2.append("def")
print("list1: " + " ".join(list1) + "\n" + "list2: " + " ".join(list2) + "\n")
print(id(list1))
print(id(list2))
Output:
list1: abc def
list2: abc def
140218365917160
140218365917160
I can see that arg=["abc"] returns copy of the same default list rather than create a new one every time.
I have tried doing
def returnList(arg=["abc"][:]):
and
def returnList(arg=list(["abc"])):
Is it possible to get a new list, or must I copy the list inside the method everytime I want to some kind of default value?
回答1:
The nicest pattern I've seen is just
def returnList(arg=None):
if arg is None: arg = ["abc"]
...
The only potential problem is if you expect None to be a valid input here which in which case you'll have to use a different sentinel value.
The problem with your approach is that args default argument is evaluated once. It doesn't matter what copying operators you do because it's simply evaluated than stored with the function. It's not re-evaluated during each function call.
Update:
I didn't want nneonneo's comment to be missed, using a fresh object as a sentinel would work nicely.
default = object()
def f(x = default):
if x is default:
...
回答2:
What you want is what you state yourself: You want a new copy of the value each time. Simply calling a function does not do anything automatically, so there is no way to have this accomplished just by a syntactic trick. But you can use a decorator to do what you want:
import copy
def keepDefaults(f):
defaults = f.func_defaults
def wrapped(*args, **kwargs):
f.func_defaults = copy.deepcopy(defaults)
return f(*args, **kwargs)
return wrapped
@keepDefaults
def f(a=[1]):
return a
f()[0].append(2)
print f() # will print "[1]" instead of "[1, 2]"
来源:https://stackoverflow.com/questions/17991492/how-to-make-a-new-default-argument-list-every-time