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.
def mean behavior is easy with the
@property decorator, however. That's a non-issue. I'll focus on the initialization and implicit
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
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)
original_init(self, *args, **kwds)
cls.__init__ = assigning_init
# 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
@property #makes readonly
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
('min', 'max', 'logp', 'mean')
(None, <code object logp at 0x108f8a1b0, file "<stdin>", line 2>, 2)