Magic function

Carl Banks pavlovevidence at gmail.com
Sat Jan 12 01:32:53 EST 2008


On Fri, 11 Jan 2008 08:29:18 -0800, dg.google.groups wrote:

> Hi all,
> 
> I'm part of a small team writing a Python package for a scientific
> computing project. The idea is to make it easy to use for relatively
> inexperienced programmers. As part of that aim, we're using what we're
> calling 'magic functions', and I'm a little bit concerned that they are
> dangerous code. I'm looking for advice on what the risks are (e.g.
> possibility of introducing subtle bugs, code won't be compatible with
> future versions of Python, etc.).
> 
> Quick background: Part of the way our package works is that you create a
> lot of objects, and then you create a new object which collects together
> these objects and operates on them. We originally were writing things
> like:
> 
> obj1 = Obj(params1)
> obj2 = Obj(params2)
> ...
> bigobj = Bigobj(objects=[obj1,obj2])
> bigobj.run()
> 
> This is fine, but we decided that for clarity of these programs, and to
> make it easier for inexperienced programmers, we would like to be able
> to write something like:
> 
> obj1 = Obj(params1)
> obj2 = Obj(params2)
> ...
> run()
> 
> The idea is that the run() function inspects the stack, and looks for
> object which are instances of class Obj, creates a Bigobj with those
> objects and calls its run() method.
> 
> So, any comments on that approach?


1. Even if you implement magic functions, don't get rid of the 
straightforward "hard way".  

Magic functions should be for convenience only.  The user should be free 
to choose to do it the straightforward, explicit "hard way", and not rely 
on the magic.  In your example, Bigobj should still be available to 
users, and should be documented at least as well as the magic run() 
function.

The main reason for this (aside from the philosophical question) is that 
users often have different needs that you can anticipate, and your magic 
might not meet those unanticipated needs, forcing the user to resort to 
hacks and workarounds.


2. If your intention is to perform this operation on all Objs, then it 
might be a good idea to arrange your code so that Objs are already 
registered by the time the user gets them.

One way to do this has already been mentioned: by having the Obj class 
track all its instances.

Another way that might be preferable is to have Bigobj create Objs on 
behalf of the user.  Here's a stripped down example:


class Bigobj(object):
    def __init__(self):
        self.tracked_objs = set()
    def create_object(self,*args):
        obj = Obj(*args)
        self.tracked_objs.add(obj)
        return obj
    def run(self):
        for obj in self.tracked_objs:
            # do something with obj

bigobj = Bigobj()

obj1 = bigobj.create_object(params1)
obj2 = bigobj.create_object(params2)

# maybe do something with obj1 and obj2 here

bigobj.run()


Carl Banks



More information about the Python-list mailing list