0

I'm trying to make a memoziation system for my set of immutable classes for handling symbolic expressions. What I've got so far is below. What I don't like is the fact that I can't block the call to __init__ directly and have to stuff something into the object (the new_inst attribute) to track whether or not the __init__ method should do anything. Is there a more pythonic way of doing this?

(The new_inst argument is just a simple way for me to tell __new__ whether to "stop" the call to __init__ or not. In reality this would be based on whether the object exists in the object cache already and wouldn't be a parameter to either __new__ or __init__).

my_cache = {}

class Base(object):
    def __new__(cls, *args, **kwargs):
        signature = (cls, args)
        if signature in my_cache:
            self = my_cache[signature]
            self.new_inst = True
        else:
            self = object.__new__(cls, *args, **kwargs)
            my_cache[signature] = self
            self.new_inst = False
        return self

class A(Base):
    def __init__(self, a):
        if not self.new_inst:
            print "Initializing A object with a=%d"%(a,)
            self.a = a

class B(Base):
    def __init__(self, a, b):
        if not self.new_inst:
            print "Initializing B object with a=%d, b=%d"%(a,b)
            self.a = a
            self.b = b

a1 = A(1)
a2 = A(1)
a3 = A(3)
b1 = B(1, 2)
b2 = B(1, 2)
b3 = B(1, 3)
print id(a1), id(a2), id(a3)
print id(b1), id(b2), id(b3)

Output:

Initializing A object with a=1
Initializing A object with a=3
Initializing B object with a=1, b=2
Initializing B object with a=1, b=3
140246850102904 140246850102904 140246850102960
140246850103016 140246850103016 140246850103072

Edit: Apparently it wasn't clear what I was getting at, so here is a more concrete example. As you can see, the second A(1) doesn't cause a second initialization, and it returns the same id.

CrazyCasta
  • 26,917
  • 4
  • 45
  • 72
  • 1
    See http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init – Dan D. Sep 17 '15 at 23:43
  • I think that your first if statement reduces to self.new_inst = (new_inst is not None) – Prune Sep 18 '15 at 00:01
  • @Prune Like I mentioned in my P.S., it looks silly, but it's because it represents a more elaborate if statement that will exist. Under the if I'd put the `object.__new__` bit and under the else I'd retrieve the object from somewhere else. – CrazyCasta Sep 18 '15 at 15:11
  • So you are implementing the Singleton pattern where the object in question's `__new__` method also serves as the Factory pattern? – Josh J Sep 18 '15 at 15:32
  • Basically, except that every place off of SO that I seem to see "Singleton Pattern" it refers to restring a class to one instance, which is not what I want. It appears that what I want may be best done with the metaclass which I haven't really looked at enough (and will look at once I get the chance). – CrazyCasta Sep 18 '15 at 16:01
  • Yes, that's the definition of "Singleton". It's not what you want. You're implementing a class in which the elements are unique at instantiation. I still think that you can clean up the "if" logic slightly. For instance, there's a lesser-known Python method dict.get, which checks whether a key is in the requested entry or an "else" value: my_cache.get(signature, object.__new__(cls, *args, **kwargs)) – Prune Sep 18 '15 at 19:05
  • However, this doesn't properly set your "new_inst" field. self = my_cache.get(signature, None) is more Pythonic, but doesn't buy you much except saving a key lookup. – Prune Sep 18 '15 at 19:06

1 Answers1

-1

Here's a shorter version of Base.new

def __new__(cls, *args, **kwargs):
    signature = (cls, args)
    new_inst = signature in my_cache
    self = my_cache.get(signature, object.__new__(cls, *args, **kwargs))
    self.new_inst = new_inst
    return self

This doesn't save much processing time, but it's cleaner and more Pythonic. I haven't yet seen a more succinct way of detecting a new instance and setting it after allocation.

Prune
  • 76,765
  • 14
  • 60
  • 81
  • I'm not concerned at all about the clean-ness of the `__new__` method. What I'm looking for is a way to eliminate the if statement in the `__init__` method. – CrazyCasta Sep 20 '15 at 01:43