comments? storing a function in an object

E

Esmail

Hello all,

I am trying to store a function and some associated information in an
object so that I can later have series of functions in a list that I can
evaluate one at a time.

Right now I am only storing the function itself, the number of
arguments it expects and its string representation. I may add other
information such as the acceptable range for the parameters or other
characteristics such as maximum or minimum.

I wonder if anyone could comment on my implementation and offer
suggestions or constructive criticisms?

The 'loading' of the functions is a bit tedious and of course care
has to be taken to make sure the string representation corresponds to
the actual function computed. It would be nice if there was an
automatic way to convert the function to its string representation.

Comments or problems with the approach I have taken?

Thanks,
Esmail

--

#!/usr/bin/env python

#
# store and represent functions
#

import math

class FunctionException(Exception):
""" custom exception """
def __init__(self, value):
self.parameter = value

def __str__(self):
return repr(self.parameter)



class Function(object):
""" class to represent a function """

def __init__(self, fn, fn_str, num_vars):
"""
the function, its string representation, and the number of variables
"""
self.fn = fn
self.fn_str = fn_str
self.num_vars = num_vars


def eval_fn(self, variables):
""" size of variables should equal num_vars .. else problem """
if len(variables) == self.num_vars:
result = self.fn(*variables)
return result
else:
raise FunctionException('invalid number of args provided - '+
'received %d, expected %d'
%(len(variables), self.num_vars))


def str(self):
""" return string representation of function """
return self.fn_str




def funct1(x):
''' small test function '''
return x * x


def funct2(x, y):
''' small test function '''
return x + y


def funct3(x):
''' small test function '''
return 1000 + (x*x + x) * math.cos(x)





def main():
""" main method """
print 'in main'

fn1 = Function(funct1, 'x * x', 1)
fn2 = Function(funct2, 'x + y', 2)
fn3 = Function(funct3, '1000 + (x*x + x) * cos(x)', 1)

for i in range(-10, 10):
try:

print 'f(', ,') =',
print fn3.str(), ' => ',
print fn3.eval_fn()

except FunctionException, (ex):
print ex.parameter



if __name__ == '__main__':
main()
 
F

Francesco Bochicchio

Hello all,

I am trying to store a function and some associated information in an
object so that I can later have series of functions in a list that I can
evaluate one at a time.

Right now I am only storing the function itself, the number of
arguments it expects and its string representation. I may add other
information such as the acceptable range for the parameters or other
characteristics such as maximum or minimum.

I wonder if anyone could comment on my implementation and offer
suggestions or constructive criticisms?

The 'loading' of the functions is a bit tedious and of course care
has to be taken to make sure the string representation corresponds to
the actual function computed. It would be nice if there was an
automatic way to convert the function to its string representation.

Comments or problems with the approach I have taken?

Thanks,
Esmail

--

#!/usr/bin/env python

#
# store and represent functions
#

import math

class FunctionException(Exception):
     """ custom exception """
     def __init__(self, value):
         self.parameter = value

     def __str__(self):
         return repr(self.parameter)

class Function(object):
     """ class to represent a function """

     def __init__(self, fn, fn_str, num_vars):
         """
         the function, its string representation, and the number of variables
         """
         self.fn = fn
         self.fn_str = fn_str
         self.num_vars = num_vars

     def eval_fn(self, variables):
         """ size of variables should equal num_vars .. else problem """
         if len(variables) == self.num_vars:
             result = self.fn(*variables)
             return result
         else:
             raise FunctionException('invalid number of args provided - '+
                                     'received %d, expected %d'
                                     %(len(variables), self.num_vars))

     def str(self):
         """ return string representation of function """
         return self.fn_str

def funct1(x):
     ''' small test function '''
     return x * x

def funct2(x, y):
     ''' small test function '''
     return x + y

def funct3(x):
     ''' small test function '''
     return 1000 + (x*x + x) * math.cos(x)

def main():
     """ main method """
     print 'in main'

     fn1 = Function(funct1, 'x * x', 1)
     fn2 = Function(funct2, 'x + y', 2)
     fn3 = Function(funct3, '1000 + (x*x + x) * cos(x)', 1)

     for i in range(-10, 10):
         try:

             print 'f(', ,') =',
             print fn3.str(), ' => ',
             print fn3.eval_fn()

         except FunctionException, (ex):
             print ex.parameter

if __name__ == '__main__':
     main()



I can offer some small suggestions: it is up to you to evaluate if
they make sense for your app:

1. use __doc__ function standard attribute for function description
(your fn_str); this meanst that
you do not need fm_str in the constructor and you have to do e.g. :

def funct2(x, y):
''' x + y '''
return x + y

then funct2.__doc__ becomes the string you want to associate to the
function

2. Use __call__ instead of eval_fn : this way, the instance of your
Function became a 'callable' and can be used
everywhere a function is needed. You can do:

f = Function(...)
f( some_args )

3. If you call a python function with wrong number of arguments, it
already raises a TypeError exception which contains
- with different wording - the same information of your
FunctionException : consider removing the check and using the
python error instead


HTH

Ciao
 
G

Gabriel Genellina

En Mon, 20 Jul 2009 14:44:16 -0300, Francesco Bochicchio
Hello all,

I am trying to store a function and some associated information in an
object so that I can later have series of functions in a list that I can
evaluate one at a time.

Right now I am only storing the function itself, the number of
arguments it expects and its string representation. I may add other
information such as the acceptable range for the parameters or other
characteristics such as maximum or minimum.

I wonder if anyone could comment on my implementation and offer
suggestions or constructive criticisms?

The 'loading' of the functions is a bit tedious and of course care
has to be taken to make sure the string representation corresponds to
the actual function computed. It would be nice if there was an
automatic way to convert the function to its string representation.

Comments or problems with the approach I have taken?

Thanks,
Esmail

--

#!/usr/bin/env python

#
# store and represent functions
#

import math

class FunctionException(Exception):
     """ custom exception """
     def __init__(self, value):
         self.parameter = value

     def __str__(self):
         return repr(self.parameter)

class Function(object):
     """ class to represent a function """

     def __init__(self, fn, fn_str, num_vars):
         """
         the function, its string representation, and the number of
variables
         """
         self.fn = fn
         self.fn_str = fn_str
         self.num_vars = num_vars

     def eval_fn(self, variables):
         """ size of variables should equal num_vars .. else problem """
         if len(variables) == self.num_vars:
             result = self.fn(*variables)
             return result
         else:
             raise FunctionException('invalid number of args provided -
'+
                                     'received %d, expected %d'
                                     %(len(variables), self.num_vars))

     def str(self):
         """ return string representation of function """
         return self.fn_str

def funct1(x):
     ''' small test function '''
     return x * x

def funct2(x, y):
     ''' small test function '''
     return x + y

def funct3(x):
     ''' small test function '''
     return 1000 + (x*x + x) * math.cos(x)

def main():
     """ main method """
     print 'in main'

     fn1 = Function(funct1, 'x * x', 1)
     fn2 = Function(funct2, 'x + y', 2)
     fn3 = Function(funct3, '1000 + (x*x + x) * cos(x)', 1)

     for i in range(-10, 10):
         try:

             print 'f(', ,') =',
             print fn3.str(), ' => ',
             print fn3.eval_fn()

         except FunctionException, (ex):
             print ex.parameter

if __name__ == '__main__':
     main()



I can offer some small suggestions: it is up to you to evaluate if
they make sense for your app:

1. use __doc__ function standard attribute for function description
(your fn_str); this meanst that
you do not need fm_str in the constructor and you have to do e.g. :

def funct2(x, y):
''' x + y '''
return x + y

then funct2.__doc__ becomes the string you want to associate to the
function

2. Use __call__ instead of eval_fn : this way, the instance of your
Function became a 'callable' and can be used
everywhere a function is needed. You can do:

f = Function(...)
f( some_args )

3. If you call a python function with wrong number of arguments, it
already raises a TypeError exception which contains
- with different wording - the same information of your
FunctionException : consider removing the check and using the
python error instead


If you follow the above suggestions, you'll see that your Function class
becomes almost useless: a normal function already IS an object, so you
don't have to wrap it inside ANOTHER object unless you need very special
features.
 
E

Esmail

Gabriel said:
>
If you follow the above suggestions, you'll see that your Function class
becomes almost useless: a normal function already IS an object, so you
don't have to wrap it inside ANOTHER object unless you need very special
features.

Hello Gabriel,

In general I would agree with you, but in my specific case
I want so store some additional meta-data with each function, such
as the valid range for input values, where the max or minimum are located,
the name/source for the function etc. I am creating list of functions
for use in testing optimization code, so it seems best to store this
data along with the function I am going to optimize in order to verify
the results for a given range (for instance).

Esmail
 
E

Esmail

Hi Francesco,

Those were great suggestions!

Re 1: I used the __doc__ attribute to eliminate the parameter in the
constructor as you suggested. Also, much easier to specify the
character string with the actual function than later to match it
up like I was.

class Function(object):
""" class to represent a function """

def __init__(self, fn, num_vars):
""" the function, its string representation, and the number of
variables """
self.fn = fn
self.fn_str = fn.__doc__
self.num_vars = num_vars


Re 2: I will need to read up on __call__ to see how to use it here ..


Re 3: I think I still need to have my own exception handler as I am
using the 'extended call syntax' now (I just learned about this
yesterday) and I will always just supply one argument, the
list. Or perhaps my custom exception will be superfluous once I
figure out __call__ ..?


Thank you for taking the time to help, always good to learn new things.

Esmail
 
C

Carl Banks

def funct1(x):
     ''' small test function '''
     return x * x

def funct2(x, y):
     ''' small test function '''
     return x + y

def funct3(x):
     ''' small test function '''
     return 1000 + (x*x + x) * math.cos(x)

def main():
     """ main method """
     print 'in main'

     fn1 = Function(funct1, 'x * x', 1)
     fn2 = Function(funct2, 'x + y', 2)
     fn3 = Function(funct3, '1000 + (x*x + x) * cos(x)', 1)


If you are defining all of the functions are in-line like this (and I
assume you are because you seem to need a function object), I'd just
exec the string representation. This is to support the DRY
principle. You could do something like this:


class Function(object):
def __init__(self,args,body):
ns = {}
exec '''def func(%s): return %s''' in ns
self.fn = ns['func']
self.fn_str = body
self.num_vars = args.count(',')+1


You have to be REALLY REALLY careful not to pass any user-supplied
data to it if this is a server running on your computer, of course.
(If it's an application running on the user's computer it doesn't
matter.)

Still wouldn't be a bad idea to pass it through some kind of validator
for extra protection.


Carl Banks
 
G

Gabriel Genellina

Hello Gabriel,

In general I would agree with you, but in my specific case
I want so store some additional meta-data with each function, such
as the valid range for input values, where the max or minimum are
located,
the name/source for the function etc. I am creating list of functions
for use in testing optimization code, so it seems best to store this
data along with the function I am going to optimize in order to verify
the results for a given range (for instance).

You can store all meta-data in the function itself:

py> def triangle(b, h):
.... "area of triangle: b*h/2"
.... return b*h/2
....
py> triangle.foo = "some value"
py> triangle.foo
'some value'

Python already knows some properties:

py> triangle.func_code.co_argcount
2
py> triangle.func_doc
'area of triangle: b*h/2'
py> triangle.__doc__
'area of triangle: b*h/2'

You may use this variant of Carl Banks proposal - this is a factory
function that returns new function objects:

def inline_function_factory(name, args, expr):
ns = {}
exec '''def %s(%s):
%r
return %s''' % (
name, args, expr, expr) in ns
function = ns[name]
return function

py> sqr = inline_function_factory("square", "x", "x*x")
py> sqr(3)
9
py> sqr.__doc__
'x*x'
py> sqr
<function square at 0x00B46770>

Wrapping a function object isn't necesarily bad, but perhaps you're doing
things more complicated than should be.
 
A

Aahz

You have to be REALLY REALLY careful not to pass any user-supplied
data to it if this is a server running on your computer, of course.

Unless, of course, your users are paying for this service.
 
C

Carl Banks

Unless, of course, your users are paying for this service.

Well, yes, but I assume that by the time you're deliberately letting
users pay to run their programs on your server, you will already have
deployed a full-blown, multi-tiered security strategy that includes
validation by the server process. That was sort of beyond the scope
of the OP's question.


Carl Banks
 
A

Aahz

Well, yes, but I assume that by the time you're deliberately letting
users pay to run their programs on your server, you will already have
deployed a full-blown, multi-tiered security strategy that includes
validation by the server process. That was sort of beyond the scope
of the OP's question.

That's not necessarily a good assumption.
--
Aahz ([email protected]) <*> http://www.pythoncraft.com/

"At Resolver we've found it useful to short-circuit any doubt and just
refer to comments in code as 'lies'. :)"
--Michael Foord paraphrases Christian Muirhead on python-dev, 2009-03-22
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,150
Members
46,696
Latest member
BarbraOLog

Latest Threads

Top