2008/12/3 Johan Hake <[EMAIL PROTECTED]>: > On Tuesday 02 December 2008 23:53:32 Anders Logg wrote: >> On Tue, Dec 02, 2008 at 11:39:40PM +0100, Johan Hake wrote: >> > On Tuesday 02 December 2008 23:15:34 Anders Logg wrote: >> > > On Tue, Dec 02, 2008 at 10:35:31PM +0100, Johan Hake wrote: >> > > > On Tuesday 02 December 2008 15:42:53 Anders Logg wrote: >> > > > > On Tue, Dec 02, 2008 at 12:12:14PM +0100, Anders Logg wrote: >> > > > > > I've tried adding a new class Constant in function.py: >> > > > > > >> > > > > > class Constant(ffc.Constant, dolfin.cpp_Function): >> > > > > > >> > > > > > def __init__(self, domain, value): >> > > > > > "Create constant-valued function." >> > > > > > print domain >> > > > > > print value >> > > > > > #ffc.Constant.__init__(self, domain) >> > > > > > #dolfin.cpp_Constant.__init__(self, value) >> > > > > > >> > > > > > But I get the following error message: >> > > > > > >> > > > > > File >> > > > > > >> > > > > > "/scratch/fenics/dolfin/dolfin-dev/local/lib/python2.5/site-packa >> > > > > >ges/ dolf in/function.py", line 411, in <module> class >> > > > > > Constant(ffc.Constant, dolfin.cpp_Function): >> > > > > > TypeError: Error when calling the metaclass bases >> > > > > > function() argument 1 must be code, not str >> > > > > > >> > > > > > How is this possible? There should be no metaclasses involved >> > > > > > here (except the built-in Python metaclass type that is always >> > > > > > there). >> > > > >> > > > Meta classes is always involed when you try to create a class. This >> > > > time it is not the FunctionCreator meta class, even if you first >> > > > think so. >> > > > >> > > > If you ever tried to subclass a python function, which you are >> > > > trying, you >> > > > >> > > > will get the same error: >> > > > >>> def jada():pass >> > > > >> > > > ... >> > > > >> > > > >>> class Jada(jada):pass >> > > > >> > > > ... >> > > > Traceback (most recent call last): >> > > > File "<stdin>", line 1, in <module> >> > > > TypeError: Error when calling the metaclass bases >> > > > function() argument 1 must be code, not str >> > > > >> > > > ffc.Constant is a function not a class... >> > > >> > > Sorry, my fault. It looks like someone did a clever trick when >> > > implementing Constant in FFC as a function and not a class... :-) >> > > >> > > > > I get this error even if I just try to create a class named >> > > > > anything that inherits from ffc.Constant. >> > > > > >> > > > > Does the metaclass construction in function.py have side-effects? >> > > > >> > > > Well, for it's use I think it works fine. The user should never >> > > > bother with the metaclass, as the newly added quote to the docstring >> > > > is mentioning. >> > > > >> > > > What we need to do is to assert the user use the Function class >> > > > correct. I have tried to put in some checks with hopefully >> > > > informative error messages. But I dont know if I have covered all >> > > > bases. We need user feedback for this. >> > > >> > > I'm sure the checks are very good, but could the logic be broken up >> > > to first split on the different types, so there's essentially one case >> > > for each of the different options in the Function docstring, and then >> > > a function is called for each case to do the work? >> > > >> > > > > I don't remember if we discussed this before, but would it be >> > > > > possible (at least simpler) to instead define a simple Python >> > > > > function that returns a "function" instance: >> > > > > >> > > > > class FooFunction(ffc.Function, ...): >> > > > > ... >> > > > > class BarFunction(dolfin.Function, ...): >> > > > > ... >> > > > > >> > > > > def Function(V, *arg): >> > > > > >> > > > > if foo: >> > > > > return FooFunction(...) >> > > > > elif bar: >> > > > > return BarFunction(...) >> > > > > >> > > > > This seems to be an easier solution. It would still be dynamic. >> > > > >> > > > This is mre or less what is going on right now. Instead of >> > > > predefining the different classes they are defined dynamically in the >> > > > __new__ function. We need to do this because the compiled functions >> > > > are created at runtime. >> > > > >> > > > I can try to untangle the code a bit as asked for previously. The >> > > > suggestion below which will extract the instantiation of compiled >> > > > functions and discrete functions is one more radical example. >> > > >> > > ok. >> > > >> > > > > The only drawback would be that we can't do >> > > > > >> > > > > isinstace(v, Function) >> > > > >> > > > This is not possible now either, as we are dynamically creating >> > > > classes in __new__, and returning instances of other classes than >> > > > Function >> > > > >> > > > Basically we are now dynamically creating Function classes that are >> > > > all having the name "Function" but they are not of the same class. >> > > > This can be illustrated by this: >> > > > >> > > > class Function(object): >> > > > def __new__(cls,cppexpr=None): >> > > > if cppexpr is None: >> > > > return object.__new__(cls) >> > > > class CompiledClass(object):pass >> > > > class Function(CompiledClass):pass >> > > > return Function() >> > > > >> > > > f = Function() >> > > > f2 = Function(cppexpr="") >> > > > >> > > > isinstance(f,Function) # This will return True >> > > > isinstance(f2,Function) # This will return False >> > > > >> > > > I cannot see how we can avoid this, as we need to create classes at >> > > > runtime. As you mention we could add a FunctionBase class that we >> > > > could check against. But I was convinced by Martins argument >> > > > previously that in PyDOLFIN code we never want to check if a function >> > > > is both a cpp_Function and a ffc.Function, but rather for one of >> > > > them. We could write this as a note for developers of PyDOLFIN, and >> > > > live with it. >> > > > >> > > > I am a bit more worried for advanced python users that do not know of >> > > > this. They could write user code that include isinstance(f,Function) >> > > > statements, and it wont behave as expected. >> > > > >> > > > We could add a >> > > > >> > > > CompileFunction(cppexpr=None, cppcode=None) >> > > > >> > > > which behave in the same manner as Function(cppexpr/cppcode) does >> > > > today. In this way we can dynamically create functions that inherits, >> > > > cpp_JitCompiledFunction, ffc.Function _and_ dolfin.Function, where >> > > > the latter is a class which is either an empty class or one that >> > > > holds the FunctionSpace. If we do this we also need to have an >> > > > additional function/class to instantiate discrete functions. >> > > > >> > > > This way to handle the isinstance problem can also be implemented for >> > > > all cases where Function is subclassed, i.e., both for Jit compiled >> > > > and ordinary python Functors. >> > > > >> > > > But I have a feeling we add more complexity than we remove... >> > > >> > > If I understand this correctly, it seems to be a simplification, or at >> > > least it makes it more explicit which we all know is better (import >> > > this). >> > > >> > > But I would like a Function class (or function) on top that delegates >> > > the creation of Function objects to these classes. >> > > >> > > > > This can be solved by creating an empty class FunctionBase that all >> > > > > the special types inherit from. >> > > > >> > > > This is the easiest way to fix the isinstance problem, but I think it >> > > > is a bit unintuitive to check for FunctionBase when you have created >> > > > a Function object. >> > > > >> > > > So I have scretched three possible ways to have it: >> > > > >> > > > 1) As it is now (with clearer code). Everything is handled with >> > > > the Function class. You cannot check for isinstance(f,Function) >> > > > >> > > > 2) Add CompiledFunction and DiscreteFunction. You can now >> > > > check for isinstance(f,Function) everywhere. >> > > > >> > > > 3) Add FunctionBase, you can now check for >> > > > isinstance(f,FunctionBase) >> > > > >> > > > I go for either 1 or 2. >> > > >> > > I'd like all of the above! :-) >> > > >> > > 1. One single Function class using the metaclass trick to return >> > > instances with dynamic inheritance. >> > >> > Ok. >> > >> > Just for repetition: >> > The metaclass is used to construct new classes that dynamically inherit >> > Jit compiled classes, not instances. The latter is done in the __new__ >> > function. >> > >> > > 2. Add classes CompiledFunction, DiscreteFunction etc that handle all >> > > the different ways in which the C++ part of a Function can behave. >> > >> > My point was to add these for the user when he/she want to instantiate >> > one fo these classes. This would be instead of the instantiation >> > functioanlity handled by __new__ in the present Function class, and the >> > whole point with introducing them would be to be able to use >> > >> > isinstance(u,Function) >> > >> > where u can be any of the possible Functions we want to have. >> >> Maybe we could add CompiledFunction since it's rather special, but I'd >> like to avoid adding DiscreteFunction in the user interface. It would >> be good if the Python interface could be kept very uniform with the >> C++ interface: >> >> Function v(V); >> >> v = Function(V) >> >> > > The created instances will inherit from ffc.Function (later >> > > ufl.Function) and one of these classes. >> > >> > I am not with you here, or you are not with me ;) >> > >> > I mean that the created Function need to inherit FFC.Function together >> > with what ever cpp_Function and a dummy dolfin.Function that the user >> > could use to check, as showed above. >> > >> > We need to do this because a function that is instantiated from a >> > dynamically created class using Function.__new__ cannot inherit Function, >> > we will get an inherit recursion. >> > >> > But if we split out the instantiation magic we can create classes that >> > all inherit Function (my 2), or we can keep the instantiation magic and >> > introduce a FunctionBase class (my 3). >> >> My only concerns are: >> >> 1. A simple interface that corresponds closely to the C++ >> interface. In C++, we have >> >> Function: can be either discrete or user-defined (subclassed) >> >> Constant: special case for constants to avoid too many different >> constructors in Function >> >> In Python, we could then have the two above, plus an extra version >> which does not exist in C++ (for obvious reasons): >> >> CompiledFunction >> >> Would this help? > > W.r.t. solving the isinstance(f,Function) problem, I think no. As the Function > class then always have to be a discrete function. If we let a compiled > function inherit Function to facilitate isinstance, we also force it to be a > discrete function, which probably would make the compiled function abit > schizofreniac. > > This goes to the core of the isinstance problem. Now Function is a versatile > class that can do many things but we cannot use it to check isinstance. If we > desides that this is important, we need to split out the instantiation of the > compiled _and_ discrete functions, if not we're fine. > > Btw, is there any special function that defines a class's isinstance protocol? > If there was we could define our own __isinstancecheck__?
You're insane. I'm happy to say there is no such thing. (I think Python 3 adds some way of saying "MyClass supports this protocol" without inheritance) >> 2. A very explicit implementation which clearly separates the >> different cases and makes the magic explicit in function.py. > > I will fix this. > > Johan -- Martin _______________________________________________ DOLFIN-dev mailing list [email protected] http://www.fenics.org/mailman/listinfo/dolfin-dev
