Opened 9 years ago

# Dynamic attributes for symbolic expressions — at Version 10

Reported by: Owned by: SimonKing major sage-5.11 symbolics symbolic expression dynamic attribute sd48 burcin, kcrisman, vbraun, eviatarbach Burcin Erocal, Simon King, Mike Hansen N/A segfaulting doctests

Let `e` be a symbolic expression. It may happen that `e.operator()` has a certain callable attribute, say, `foo`, that is not a method of `Function`. In this situation, one would like to use `e.foo()`, which is supposed to return `e.operator().foo(*e.operands())` - apparently this is useful for working with hypergeometric functions (#2516).

Example

```       sage: from sage.symbolic.function import BuiltinFunction
sage: class TFunc(BuiltinFunction):
....:     def __init__(self):
....:         BuiltinFunction.__init__(self, 'tfunc', nargs=1)
....:
....:     class EvaluationMethods:
....:         def argp1(self, x):
....:             '''
....:             Some documentation about a bogus function.
....:             '''
....:             return x+1
....:
....:         @property
....:         def foo(self):
....:             return 5
....:
sage: tfunc = TFunc()
sage: e = tfunc(x); e
tfunc(x)
sage: type(e)
<class '__main__.Expression_with_dynamic_methods'>
sage: e.argp1()
x + 1
sage: e.foo
5
sage: x.argp1()
Traceback (most recent call last):
...
AttributeError: 'sage.symbolic.expression.Expression' object has no
attribute 'argp1'
sage: t = (e + 1).op[0]; t
tfunc(x)
sage: t
tfunc(x)
sage: type(t)
<class '__main__.Expression_with_dynamic_methods'>
sage: t.argp1()
x + 1
sage: import sagenb.misc.support as s
sage: s.completions('t.argp', globals(), system='python')
['t.argp1']
sage: t.argp1.__doc__.strip()
'Some documentation about a bogus function.'
```

### Changed 9 years ago by SimonKing

The patch implements dynamic attributes, but some doctests segfault

### comment:1 Changed 9 years ago by SimonKing

• Owner changed from segfaulting doctests to (none)

### comment:2 Changed 9 years ago by SimonKing

• Status changed from new to needs_work
• Work issues set to segfaulting doctests

Sorry, accidentally I inserted the work issue "segfaulting doctests" into the owner field.

It would be nice to have a segfaulting example that works interactively.

### comment:3 follow-up: ↓ 7 Changed 9 years ago by mhansen

What is the typical use case for this? Are you just looking for a place to put the functions for evaluated function? Or do you really want the function to appear on both the function and its evaluations? It seems like the first case is what you really want.

We currently have all of the machinery to do this and it's used for the category code. Using this, you would have something like the following:

```class ExampleBuiltin(BuiltinFunction):
def __init__(self):
BuiltinFunction.__init__(self, 'ex_func', nargs=0) #arbitrary no of args

class EvaluationMethods:
def some_function_name(self):
return len(self.operands())
```

### comment:4 follow-up: ↓ 8 Changed 9 years ago by mhansen

I did a little mockup using this idea.

```sage: from sage.symbolic.function import BuiltinFunction
sage: class ExampleBuiltin(BuiltinFunction):
....:         def __init__(self):
....:             BuiltinFunction.__init__(self, 'ex_func', nargs=0) #arbitrary no of args
....:     class EvaluationMethods:
....:             def some_function_name(self):
....:                 return len(self.operands())
....:
sage: ex_func = ExampleBuiltin()
sage: f = ex_func(x, x+1, x+2)
sage: f.some_function_name()
3
sage: abs(f)
abs(ex_func(x, x + 1, x + 2))
```

### comment:5 Changed 9 years ago by mhansen

Note that pickling also works:

```sage: loads(dumps(ceil(x))).foo_bar()
4
```

One issue is that in order to get the dynamic features, you have to go through the call method of the function. Thus, you'd have to change Expression.abs

```        return new_Expression_from_GEx(self._parent, g_abs(self._gobj))
```

to return the dynamic class version. Similarly, unpickling old objects will just return an Expression object.

### comment:6 Changed 9 years ago by mhansen

Also, all tests pass with this.

### comment:7 in reply to: ↑ 3 Changed 9 years ago by SimonKing

What is the typical use case for this? Are you just looking for a place to put the functions for evaluated function? Or do you really want the function to appear on both the function and its evaluations?

Sorry that I was absent for some hours.

Unfortunately I don't know what the real use case is. It was a suggestion of Burcin, and I merely tried to implement what he suggested.

Burcin, could you comment on what is really expected?

### comment:8 in reply to: ↑ 4 Changed 9 years ago by SimonKing

I did a little mockup using this idea.

sage: f = ex_func(x, x+1, x+2) sage: f.some_function_name() 3 sage: abs(f) abs(ex_func(x, x + 1, x + 2))

... which seems like the correct answer to me. So, the fact that `ex_func` has an `_abs_` method helps to get the right answer for `f`.