1

I would like to be able to initialize my Vector object with either an angle or an x and a y in python. I know I can figure out the angle using math.atan2(x, y) and I can use figure out the x and the y from the angle but I don't know how to make these inputs optional.

I would like to be able to call either:

Vector(x, y, speed)

or:

Vector(angle, speed)

Thanks!

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
picklebobdogflog
  • 179
  • 2
  • 11

4 Answers4

2
class Vector(object):
    """
    >>> Vector(angle, speed)  # initialize with angle and speed
    >>> Vector(x, y, speed)   # or initialize with x, y, and speed
    """
    def __init__(first, second, third=None):
        if third is None:
            angle, speed = first, second
        else:
            x, y, speed = first, second, third

If called with two arguments, third will be None by default. So the first two arguments will be assigned to angle and speed. Otherwise, the arguments will be assigned to x, y and speed.

(Edit: Added a doc string so the Vector's call signature is clear.)

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
2

I think the most pythonic way would be to add a class method:

class Vector:
    def __init__(x, y, speed):
        ...

    @classmethod
    def from_angle(cls, angle, speed):
        # figure out x and y from angle
        return cls(x, y, speed)

And call either Vector(x, y, speed) or Vector.from_angle(angle, speed).

 

Various variants like

def __init__(first, second, third=None)
   ...

or

def __init__(*args):
   ...

lose too much clarity. People using your code (including future you) will lose the ability to glance over the method signatures and see their options.

Pavel Anossov
  • 60,842
  • 14
  • 151
  • 124
  • Is there any documentation on class methods? – picklebobdogflog Mar 04 '13 at 20:21
  • [Of course](http://docs.python.org/2/library/functions.html#classmethod). Also look for some [nice answers on SO](http://stackoverflow.com/questions/12179271/python-classmethod-and-staticmethod-for-beginner) for examples. – Pavel Anossov Mar 04 '13 at 20:24
  • Especially the naming of "first" "second" and "third" is not so nice, which this solution solves elegantly. Just imagine seeing the necessary and optional parameters in some IDE and only reading first, second and third. It wouldn't give you any clue on what is expected. – Zelphir Kaltstahl Aug 13 '15 at 15:14
1

As an alternative, I would do it using class methods, so the constructor would only take fields as arguments.

class Vector(object):
    __slots__ = ['x', 'y']
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __repr__(self):
        return 'Vector({}, {})'.format(self.x, self.y)

    @classmethod
    def polarVector(cls, angle, mag):
        return cls(math.cos(angle) * mag, math.sin(angle) * mag)

    @classmethod
    def magVector(cls, x, y, mag):
        a = mag / math.sqrt(x**2 + y**2)
        return cls(x * a, y * a)

Problems with optional arguments

The major problem with optional arguments is code clarity.

a = Vector(1, 2)             # Is 1 "angle" or "x"?
a = Vector(3, 4, 10)         # Huh?

# These functions do different things,
# so it makes sense that they have different names.
a = Vector.polarVector(1, 2)
a = Vector.magVector(3, 4, 10)
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • ALthough this is better when you use a=Vector(blah, blah, blah), it is harder to read in the Vector class. I think the optional arguments would work better if you just did inline comments like Vector(1, 2) #angle, magnitude and Vector(3, 6, 1) #x, y, magnitude – picklebobdogflog Mar 04 '13 at 20:31
  • @picklebobdogflog: The point is that you're not supposed to *need* inline comments to understand code. Have fun reading your source code six weeks from now. – Dietrich Epp Mar 04 '13 at 20:33
0

Both answers by unutbu and Pavel Anossov are very good.

I personally would prefer to pass the coordinates as a tuple (as they are a vector after all and belong together). Thus, you will always have two arguments:

#! /usr/bin/python3.2

import math

class Vector:
    def __init__ (self, direction, speed):
        self.speed = speed
        try: self.angle = math.atan2 (direction [0], direction [1] )
        except: self.angle = direction

v1 = Vector (math.pi / 4, 100)
print (v1.angle)

v2 = Vector ( (1, 1), 100)
print (v2.angle)
Hyperboreus
  • 31,997
  • 9
  • 47
  • 87