0

I'm writing a function, that translates tuple or a pair of arguments to a namedtuple with int fields.

from collections import namedtuple

EPS = 0.00000001

def point(*cont: 'tuple or pair of args') -> namedtuple('Point', 'x y'):
    """Make an int named pair (Point)"""
    if len(cont) == 1:
        res = namedtuple('Point', 'x y')
        if abs(int(cont[0][0])-cont[0][0]) < EPS:
            res.x = int(cont[0][0])
        else:
            res.x = cont[0][0]
        if abs(int(cont[0][1])-cont[0][1]) < EPS:
            res.y = int(cont[0][1])
        else:
            res.y = cont[0][1]
    else:
        res = namedtuple('Point', 'x y')
        if abs(int(cont[0])-cont[0]) < EPS:
            res.x = int(cont[0])
        else:
            res.x = cont[0]
        if abs(int(cont[1])-cont[1]) < EPS:
            res.y = int(cont[1])
        else:
            res.y = cont[1]
    return res

Is there a nicer way to do that?

ANDREYDEN
  • 96
  • 10
  • Welcome to SO please take the time to read [ask] and the other links on that page. If you have a working solution and want a critique, post over at https://codereview.stackexchange.com/ – wwii Mar 13 '18 at 19:13

1 Answers1

1

One major flaw with your code is that it's not actually using namedtuple, you're just setting attributes on the namedtuple class. Your code would not behave any differently if you were to replace namedtuple('Point', 'x y') with any other object that allows you to add attributes:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', 'x y')
>>> Point.x = 5
>>> Point.y = 5
>>> Point.z = 5  # uh oh
>>> Point
<class '__main__.Point'>
>>> point = Point(1, 2)  # how it should work
>>> point
Point(x=1, y=2)
>>> isinstance(Point, tuple)
False
>>> isinstance(point, tuple)
True

Once you fix that, here's how I'd simplify your code a little:

Point = namedtuple('Point', 'x y')

def maybe_floor_int(n, epsilon=0.00000001):
    if abs(int(n) - n) < epsilon:
        return int(n)
    else:
        return n

def create_point(*args: 'tuple or pair of args') -> Point:
    """Make an int named pair (Point)"""

    if len(args) == 1:
        x, y = args
    else:
        x, y = args[0]

    return Point(maybe_floor_int(x), maybe_floor_int(y))

Since your point function is creating an instance of a Point tuple and you may eventually want to override point's operators (like being able to scale them with multiplication), you may as well just create a dedicated Point class.

Blender
  • 289,723
  • 53
  • 439
  • 496