6
\$\begingroup\$
class Pool(type):
    pool = dict()
    def __new__(clas, *a, **k):
        def __del__(self):
            Pool.pool[self.__class__] = Pool.pool.get(self.__class__, []) + [self]
        a[-1]['__del__'] = __del__
        return type.__new__(clas, *a, **k)

    def __call__(clas, *a, **k):
        if Pool.pool.get(clas):
            print('Pool.pool is not empty: return an already allocated instance')
            r = Pool.pool[clas][0]
            Pool.pool[clas] = Pool.pool[clas][1:]
            return r
        else:
            print('Pool.pool is empty, allocate new instance')
            return type.__call__(clas, *a, **k)

class Foo(metaclass=Pool):
    def __init__(self):
        print('Foo > .', self)
    def foo(self):
        print('Foo > foo:', self)

f1 = Foo()
f1.foo()

print('## now deleting f1')
del f1

print('## now create f2')
f2 = Foo()
f2.foo()

print('## now create f3')
f3 = Foo()
f3.foo()

what do you think about this piece of code?

\$\endgroup\$
2
  • \$\begingroup\$ Well, calling class variables "clas" annoys the heck out of me. klass or class_ is better IMO. But that's a matter of taste. \$\endgroup\$ – Lennart Regebro Mar 3 '11 at 16:54
  • \$\begingroup\$ i don't like k in place of c, but i'm agree with bad sound of 'clas' too : ) \$\endgroup\$ – nkint Mar 3 '11 at 22:35
3
\$\begingroup\$
class Pool(type):

The defaultdict class will automatically create my list when I access it

    pool = collection.defaultdict(list)

The python style guide suggests using the form class_ rather then clas It is also not clear why you are capturing the incoming arguments as variable. This being a metaclass it is going to have consistent parameters

    def __new__(class_, name, bases, classdict):
        def __del__(self):

Since pool now automatically creates the list, we can just append to it Pool.pool[self.class].append(self)

Getting rid of the variable arguments also makes this much clearer.

        classdict['__del__'] = __del__
        return type.__new__(class_, name, bases, classdict)

args and kwargs are often used as the names for variable parameters. I recommend using them to make code clearer

    def __call__(class_, *args, **kwargs):

Thanks to the use of defaultdict above we can make this code quite a bit cleaner. Also, this code doesn't pretend its using a functional programming language. Your code created lists by adding them together, slicing, etc. That is not a really efficient or clear way to use python.

        instances = Pool.pool[class_]
        if instances:

There is a subtle and dangerous problem here. When you create a new instance, you pass along your parameters. However, if an instance is already created, you return that ignoring what the parameters were doing. This means that you might get an object back which was created using different parameters then you just passed. I haven't done anything to fix that here

            print('Pool.pool is not empty: return an already allocated instance')
            return instances.pop()
        else:
            print('Pool.pool is empty, allocate new instance')
            return type.__call__(class_, *args, **kwargs)

Your technique is to install a __del__ method on the objects so that you can detect when they are no longer being used and keep them in your list for the next person who asks for them. the __del__ method is invoked when the object is about to be deleted. You prevent the deletion by storing a reference to the object in your list. This is allowed but the python documentation indicates that it is not recommended.

__del__ has a number of gotchas. If an exception is caused while running the __del__ method it will be ignored. Additionally, it will tend to cause objects in references cycles to not be collectable. If your code is ever run on Jython/IronPython then you can't be sure that __del__ will be called in a timely manner. For these reasons I generally avoid use of __del__.

You are using a metaclass so the fact that the given object is pooled is hidden from the user. I don't really think this is a good idea. I think it is far better to be explict about something like pooling. You also lose flexibility doing it this way. You cannot create multiple pools, etc.

The interface that I would design for this would be:

pool = Pool(Foo, 1, 2, alpha = 5) # the pool gets the arguments that will be used to construct Foo
with pool.get() as f1:
    # as long as I'm in the with block, I have f1, it'll be returned when I exit the block
    f1.foo()
with pool.get() as f2:
    f2.foo()
    with pool.get() as f3:
        f3.foo()

f4 = pool.get()
f4.foo()
pool.repool(f4)
\$\endgroup\$
6
  • \$\begingroup\$ yeah i would pooling to be transparent to the user because i thought it was a good thing. thanks to point me in right way. but i don't understand thw with usage \$\endgroup\$ – nkint Mar 4 '11 at 17:07
  • \$\begingroup\$ "This means that you might get an object back which was created using different parameters then you just passed" recalling init method could be help or it make pooling useless? \$\endgroup\$ – nkint Mar 4 '11 at 17:09
  • \$\begingroup\$ "Also, this code doesn't pretend its using a functional programming language. Your code created lists by adding them together, slicing, etc. That is not a really efficient or clear way to use python." i thought functional python was more pythonic. thanks to point this \$\endgroup\$ – nkint Mar 4 '11 at 17:12
  • \$\begingroup\$ @nkint, for with see: docs.python.org/reference/datamodel.html#context-managers basically, it allows you to provide code which is run before and after the with. That way you can write code that returns the Foo back to the pool as soon as the with block exits. \$\endgroup\$ – Winston Ewert Mar 4 '11 at 17:15
  • \$\begingroup\$ @nkint, if you recall init, you are probably not saving any noticeable amount of time by pooling. Why do we want to pool objects anyways? Typically, you pool objects because they are expensive to create. \$\endgroup\$ – Winston Ewert Mar 4 '11 at 17:17
2
\$\begingroup\$

Name your arguments. If you know enough about what you'll have passed in to take item -1 of the positional argument list, you know enough to give it a name.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.