3

Let's say I have a class Circle with the following definition:

class Circle:
    def __init__(self, r, _id):
        self.r = r
        self.id =  _id

    def area(self):
        return math.pi * (self.r ** 2)

I want to write a function that compares two circles and returns the id of the smallest one

def compare_circles(circle_1: Circle, circle_2: Circle) -> str:
    if circle_1.r < circle_2.r:
        return circle_1.id
    else:
        return circle_2.id

I would like to place this method as a static method on the class. (Is this a bad idea?)

class Circle:
    
    def __init__(self, r, _id):
        self.r = r
        self.id =  _id

    def area(self):
        return math.pi * (self.r ** 2)

    @staticmethod
    def compare_circles(circle_1: Circle, circle_2: Circle) -> str:
        if circle_1.r < circle_2.r:
            return circle_1.id
        else:
            return circle_2.id

However this does not work because the Circle object is not available to be type hinted. I have to remove the type hints in the method. Using a classmethod also does not work, as the cls argument is not available to the other method arguments.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Alan
  • 1,746
  • 7
  • 21

1 Answers1

4

A possible workaround in this case would be to monkey-patch the method after defining the class:

class Circle:
    def __init__(self, r, _id):
        self.r = r
        self.id =  _id

    def area(self):
        return math.pi * (self.r ** 2)


def compare_circles(circle_1: Circle, circle_2: Circle) -> str:
    if circle_1.r < circle_2.r:
        return circle_1.id
    else:
        return circle_2.id

Circle.compare_circles = staticmethod(compare_circles)
del compare_circles

The usual way would be to provide a string with the type name:

class Circle:
    def __init__(self, r, _id):
        self.r = r
        self.id =  _id

    def area(self):
        return math.pi * (self.r ** 2)


    @staticmethod
    def compare_circles(circle_1: 'Circle', circle_2: 'Circle') -> str:
        if circle_1.r < circle_2.r:
            return circle_1.id
        else:
            return circle_2.id

As an aside, you might also consider turning compare_circles into a method:

def compare(self, other: 'Circle') -> str:
    if self.r < other.r:
        return self.id
    else:
        return other.id
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • 1
    Monkey-patching seems like a hack to me - I personally would skip type hints altogether before monkey-patching. Making the function a method is good idea though; I'd even go a step further and use the [`__lt__`](https://docs.python.org/3/library/operator.html#operator.__lt__) magic method. – 0x5453 May 13 '21 at 15:57
  • 1
    `from __future__ import annotations` should also work. – Axe319 May 13 '21 at 15:58
  • @0x5453. I don't normally use type hinting, so not too familiar with it. This method does not seem like a good fit for `__lt__`, though it does have a superficial resemblance at first glance – Mad Physicist May 13 '21 at 16:22
  • Wow, great answer! Monkey patching is definitely a hack, but +1 for the example; I didn't know you could do that. The string type hint, again, not the best fit for me, but I [embarrassingly] didn't know you could do it. The 3rd one works the charm. Thanks! – Alan May 13 '21 at 16:56