问题
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 arg
s 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