34

I was wondering if anyone had any good ways of quickly explaining how to efficiently and pythonically create user defined objects with optional arguments. For instance, I want to create this object:

class Object:
    def __init__(self, some_other_object, i, *j, *k):
        self.some_other_object = some_other_object
        self.i = i
        # If j is specified, assume it is = i
        if(j==None):
            self.j = i
        else:
            self.j = j
        # If k is given, assume 0
        if(k==None):
            self.k = 0
        else:
            self.k = k

Is there a better way to do this?

EDIT: I changed the code so that it is more broad and more easily understood.

Braiam
  • 1
  • 11
  • 47
  • 78
chase
  • 3,592
  • 8
  • 37
  • 58
  • 2
    This isn't valid, you can't have multiple `*` arguments. The argument prefixed with a `*` becomes a *list* of *all non-keyword arguments that come after that point*. – Gareth Latty Mar 20 '13 at 22:12
  • Thanks @Lattyware , that helps. I saw the *args and *keywordsargs, etc. and thought they may have been referring to different arguments passed. – chase Mar 20 '13 at 22:17
  • toxotes's answer is what you want, it just has one major flaw (see my comment on it). – Gareth Latty Mar 20 '13 at 22:17

2 Answers2

57

You can set default parameters:

class OpticalTransition(object):
    def __init__(self, chemical, i, j=None, k=0):
        self.chemical = chemical
        self.i = i
        self.k = k
        self.j = j if j is not None else i

If you don't explicitly call the class with j and k, your instance will use the defaults you defined in the init parameters. So when you create an instance of this object, you can use all four parameters as normal: OpticalTransition('sodium', 5, 100, 27)

Or you can omit the parameters with defaults with OpticalTransition('sodium', 5), which would be interpreted as OpticalTransition('sodium', 5, None, 0)

You can use some default values but not all of them as well, by referencing the name of the parameter: OpticalTransition('sodium', 5, k=27) uses j's default but not k's.

Python won't allow you to do j=i as a default parameter (i isn't an existing object that the class definition can see), so the self.j line handles this with an if statement that in effect does the same thing.

toxotes
  • 1,386
  • 14
  • 20
  • 4
    *Generally, by the way, don't bother with if x == None, just do if x.* is bad advice. Many values can evaluate to `False` - if you want to check for `None`, do `if x is None:`, not `if x:`. In this example, if `j` is `0`, it will do something unexpected. -1, until this is fixed. – Gareth Latty Mar 20 '13 at 22:14
  • You're right -- I was thinking the OP was doing that to see if it existed at all, not necessarily to see if they had the value of `None`. – toxotes Mar 20 '13 at 22:18
  • Fix that and I'll flip that up to a +1, this is the best answer possible apart from that. (Edit: I made the edit myself). – Gareth Latty Mar 20 '13 at 22:18
  • 1
    Thanks @toxotes , this makes sense and looks very simple. – chase Mar 20 '13 at 22:19
  • @Lattyware You beat me to it :) – toxotes Mar 20 '13 at 22:22
  • 1
    @Lattyware Shouldn't it be 'if j is not None:'? since it is already 'None' by default? – chase Mar 20 '13 at 22:24
  • 1
    @chase Good catch, fixed - it was actually a little more complicated a fault than that, but should be good now. I felt the ternary operator is a good choice here. – Gareth Latty Mar 20 '13 at 22:31
  • Why not do `j or i` instead of `j if j is not None else i`? – Laurent Aug 24 '21 at 19:27
5

I am too new to stackoverflow to up-vote or comment, but I approve of ronak's answer. Gareth Latty's comment about default parameters is addressed by using the [default] option of the get method.

dictionary.get(key[, default])

using **args allows for less/ more readable code when there are many optional variables to be passed. It is simply passing passing dictionary key-value pairs as parameters.

mertzkeaton
  • 61
  • 1
  • 2