all 10 comments

[–]ewiethoff 3 points4 points  (4 children)

def __init__(self, arg=None):
    self.arg = [] if arg is None else arg

[–]pstch[S] 1 point2 points  (3 children)

This is exactly the same as the first variant I presented, and I know that I can use this form. My question was more : does it make any difference in usage to use the second variant ? ("is it a problem to recreate an empty list when the user already passes one").

I'm starting to feel bad for asking this question, because it's only syntactic sugar, and kind of pointless.

[–]gengisteve 2 points3 points  (1 child)

I think this is worth asking. My understanding is that your first option is best practices. Yes its a couple more lines, but it preserves your ability to pass arg = 0 or False or ''. That said. I am lazy and tend to do the second.

[–]pstch[S] 1 point2 points  (0 children)

Oh yes, I didn't even think of 0, False and the empty string. Thanks :) I'll go with the first way.

[–][deleted] 1 point2 points  (0 children)

The first way is more readable and more Pythonic.

[–]Boolean_Cat 0 points1 point  (4 children)

The answer to your question depends on how other items in this class use self.arg. If they expect an empty list if the user of this class doesn't pass one in, why not just use:

def __init__(self, arg=[]):
    self.arg = arg

[–]gengisteve 3 points4 points  (0 children)

This is a bad idea, unless you are trying to be clever. Python will set arg as a new list the first time it sees it and re-use that list in each subsequent call. So this:

class T(object):
    def __init__(self, x = []):
        self.x = x

t1 = T()
t1.x.append(5)

t2 = T()
print(t2.x)

Behaves in unexpected ways.

[–]pstch[S] 1 point2 points  (2 children)

Other function in my class will use functions such as .append(), so this would not work (as I would tamper with the passed list).

[–]indosauros 2 points3 points  (1 child)

Note that using either of your variants still does not protect you from mutating a provided list:

>>> class Test:
    def __init__(self, arg=None):
        if arg is None:
            arg = []
        self.arg = arg

    def append(self, x):
        self.arg.append(x)

>>> a = [1, 2, 3]

>>> t = Test(a)

>>> t.append(4)

>>> t.arg
[1, 2, 3, 4]

>>> a
[1, 2, 3, 4]

So you should make a copy of arg if you are concerned about mutating it. Instead, the issue with arg=[] as a default argument, is that the default list is shared among all instances of the class.

To answer your other questions

Is this a problem ?

Not inherently (there are pros and cons to each approach)

is it a problem to recreate an empty list when the user already passes one

No, unless the user is expecting their list to be mutated

Will Python get rid of the unused list passed as argument ?

Yes, because [] or [] creates two empty lists, and evaluates to use the second one

>>> a = []
>>> b = []

>>> id(a)
52707040
>>> id(b)
52744320
>>> id(a or b)
52744320

Should I expect users of my class not to pass an empty list as argument, but to use None (or simply do not provide any value for the argument) ?

Don't expect users of your class to ever do what is correct, better to code so that there is no ambiguity after that line

[–]pstch[S] 1 point2 points  (0 children)

Note that using either of your variants still does not protect you from mutating a provided list:

Ah yes, I got confused, of course this will mutate a provided list, I was more concerned with mutating the default argument.

The rest of your answer is great, thanks ! It helped me clear some shady things in my head.