Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have to define a lot of values for a Python library, each of which will represent a statistical distribution (like the Normal distribution or Uniform distribution). They will contain describing features of the distribution (like the mean, variance, etc). After a distribution is created it doesn't really make sense to change it.

I also expect other users to make their own distributions, simplicity is an extra virtue here.

Scala has really nice syntax for classes in cases like this. Python seems less elegant, and I'd like to do better.

Scala:

class Uniform(min : Double, max : Double) {
    def logp(x : Double) : Double = 
        1/(max - min) * between(x, min, max)

    def mean = (max + min)/2
}

Python:

class Uniform(object):
    def __init__(self, min, max):
        self.min = min
        self.max = max
        self.expectation = (max + min)/2

    def logp(self, x):
        return 1/(self.max - self.min) * between(x, self.min, self.max)

It's not terrible, but it's not great. There are a lot of extra 'self's in there and the constructor part seems pretty boilerplate.

One possibility is the following nonstandard idiom, a function which returns a dictionary of locals:

def Uniform(min, max):
    def logp(x): 
        return 1/(max - min) * between(x, min, max)

    mean = (max + min)/2
    return locals()

I like this quite a bit, but does it have serious drawbacks that aren't obvious?

It returns a dict instead of an object, but that's good enough for my purposes and would be easy to fix with a that made it return an object.

Is there anything better than this? Are there serious problems with this?

share|improve this question

migrated from stackoverflow.com Feb 13 '13 at 16:12

This question came from our site for professional and enthusiast programmers.

3 Answers 3

You might prefer to use namedtuple for the attributes, and the property decorator for mean. Note that mean is now computed every time it's accessed - maybe you don't want this, or maybe it makes sense since max and min are mutable.

from collections import namedtuple

class Uniform(namedtuple('Uniform', 'min max')):

    def logp(self, x):
        return 1/(self.max - self.min) * between(x, self.min, self.max)

    @property
    def mean(self):
        return 0.5*(self.min + self.max)
share|improve this answer
    
+1 but it's nicer to pass in the field names as a list of names though, not as a space separated string :) in fact I don't even know why the latter "syntax" is allowed in Python in the first place. –  Erik Allik Apr 14 at 17:17

I'm not sure how Pythonic this is, but how about something like this:

class DynaObject(object):
    def __init__(self, *args, **kwargs):
        for (name, value) in kwargs.iteritems():
            self.__setattr__(name, value)

class Uniform(DynaObject):
    __slots__ = ["min", "max"]

    def logp(self, x): 
        return 1/(self.max - self.min) * between(x, self.min, self.max)

    def mean(self): return (self.max + self.min)/2

You would then construct a Uniform object like:

u = Uniform(min=1, max=2)

UPDATE

Since answering I saw a post on Reddit for a GitHub project that offers a similar solution using a function decorator instead of sub-classing. It would allow you to have a class like:

class Uniform(object):
    @instancevars
    def __init__(self, min, max): 
        pass

    def logp(self, x): 
        return 1/(self.max - self.min) * between(x, self.min, self.max)

    @property
    def mean(self): return (self.max + self.min)/2
share|improve this answer
    
Recommendation: Use @property before mean so that mean acts like a (read-only) property. –  nneonneo Feb 13 '13 at 6:18

This mostly comes down to a difference of style. Python really prefers explicit rather than implicit, hence explicit self, assignment in __init__, etc. It will never support a syntax like you see in Scala. This is the same reason locals() isn't much liked in Python--it is implicit state rather than explicit parseable syntax.

Getting your def mean behavior is easy with the @property decorator, however. That's a non-issue. I'll focus on the initialization and implicit self.

The drawbacks of your approach are that it is non-standard, it is less explicit about self, and the object it creates lacks a normal class. We can make it look more standard, though. The namedtuple approach from @detly is perfect if you want to make an immutable object. If you need mutable objects or a more complex initialization signature, you can use a class decorator that wraps the __init__ call with a function that updates the instance dictionary. This is made really easy by inspect.getcallargs.

import inspect

def implicit_init(cls):
    def pass_(self):
        pass
    original_init = getattr(cls, '__init__', pass_)
    def assigning_init(self, *args, **kwds):
        # None stands in place of "self"
        callargs = inspect.getcallargs(original_init, None, *args, **kwds)
        self.__dict__.update(callargs)
        original_init(self, *args, **kwds)

    cls.__init__ = assigning_init

    return cls


@implicit_init
class Uniform(object):
    # an init is still necessary for the creation signature
    # you can 
    def __init__(self, min, max):
        # you can put stuff here, too.
        # self.min etc will already be set
        pass

    @property #makes readonly
    def mean(self):
        return (self.max + self.min) / 2

    def logp(self, x):
        return 1/(self.max - self.min) * between(x, self.min, self.max)

Notice there is still a bunch of selfs in there. Python style really likes those, so I recommend you go no further. If you really want to get rid of them, you have to be a little ugly. The problem is that your approach conflates class creation with instance creation, approaching a prototype-style. It is not possible (without reading internal details of function objects and bytecode) to know which vars are class vars and which are instance vars. Python has support for class metaprogramming because there are various metaclass hooks where one can customize class creation and where the class namespace is separated neatly into its name, bases and dict (see the three-argument form of type()). But it's not easy to do the same for functions.

We can get around this by delaying initialization of the class (i.e., dynamically modifying the class) until right before the first instance is created. (I'm not going to write this because it's quite an involved bit of code, but it requires a decorator.) You might also be able to come up with a semantic you like by inspecting the Uniform function's code object and building a class dynamically from there.

>>> def Uniform(min, max):
...     def logp(x):
...             return 1/(max-min) * between(x,min,max)
...     mean = (max+min)/2
...     return locals()
... 
>>> uco = Uniform.func_code
>>> uco.co_varnames
('min', 'max', 'logp', 'mean')
>>> uco.co_consts
(None, <code object logp at 0x108f8a1b0, file "<stdin>", line 2>, 2)
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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