3

I'm trying to define a class that has an instance of itself as a class variable so I can reference a common instance of it all over the place.

How can I get something like this to work?

class Point():
  ORIGIN = Point()

  def __init__(self, x=0, y=0):
    self.x = x
    self.y = y

p0 = Point.ORIGIN
p1 = Point(3,4)

distance = (p1.x*p1.x + p1.y*p1.y) ** .5
print(distance)
Steve Geluso
  • 139
  • 1
  • 2
  • 7

3 Answers3

6

You can add the class attribute after the class has been created:

class Point():
  def __init__(self, x=0, y=0):
    self.x = x
    self.y = y

Point.ORIGIN = Point()

You can probably also make it work so that the origin is created lazily via descriptors, or you can probably do something funky using a metaclass -- but that seems unlikely to be worth your while.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Thanks for the practical solution! I'm disappointed attaching ORIGIN has to happen at the very bottom after a potentially long class definition, but it seems that's the best way to go without getting more complicated. – Steve Geluso Jan 20 '17 at 02:35
1

You could use a meta class:

>>> class SingletonMeta(type):
...     def __init__(cls, name, bases, dct):
...         cls.ORIGIN = cls()
...
>>> class Point(metaclass=SingletonMeta):
...     def __init__(self, x=0, y=0):
...         self.x = x
...         self.y = y
...
>>> p0 = Point.ORIGIN
>>> p1 = Point(3,4)
>>> p0
<__main__.Point object at 0x110b7e7b8>
>>> p0.x, p0.y
(0, 0)
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • This looks like a valuable thing to know. Can you tell us more about how a metaclass works, or when its constructor gets called? – JoshOrndorff Jan 20 '17 at 02:25
  • 2
    @JoshOrndorff -- Metaclass constructors are called when a class is being created. e.g. in this case, `SingletonMeta.__new__` is called to _create_ the `Point` class. `SingletonMeta.__init__` is called immediately after `Point` is created. [You can read a lot more about metaclasses in this question/answers](http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python). – mgilson Jan 20 '17 at 06:36
1

Simply create class variables that represent the values you want instead of encapsulating those values in an instance:

class Point:
    x = 0
    y = 0
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

x,y = Point.x, Point.y
p1 = Point(3,4)
distance = ((p1.x-x)**2 + (p1.y-y)**2) ** .5
print(distance) # prints 5.0

Or, better yet:

class Point:
    x = 0
    y = 0
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
    def distance(self, other=None):
        if other is None:
            x,y = Point.x, Point.y
        else:
            x,y = other.x, other.y
        return ((self.x-x)**2 + (self.y-y)**2) ** .5

And then you can do this:

>>> p1 = Point(3,4)
>>> p1.distance()
5.0
>>> p1.distance(Point(3,5))
1.0
TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97