Generating modul classes with eval

Discussion in 'Python' started by Axel Straschil, Feb 2, 2005.

  1. Hello!

    I was fooling around with creating classes for a module with eval,
    something like:

    MyModule.py:

    class Base:
    init(self, name):
    self._name = name

    for myclass in ['A', 'B', 'C']:
    code="class %s(Base):\n\tinit(self, name='%s')\n\t\tsuper(%s,
    self).__init(name=name)\n"%dict(myclass, myclass.lower(), myclass())
    ... codeop and eval stuff ...
    a=A()
    print a

    that gives: <class '__main__.A'>, but I want MyModule.A ;-)

    Can someone give me a hint how to create classes in a module with eval
    and codeop so that they exist like the code was written in?

    Thanks,
    AXEL.
    Axel Straschil, Feb 2, 2005
    #1
    1. Advertising

  2. Axel Straschil

    Steve Holden Guest

    Axel Straschil wrote:

    > Hello!
    >
    > I was fooling around with creating classes for a module with eval,
    > something like:
    >
    > MyModule.py:
    >
    > class Base:
    > init(self, name):
    > self._name = name
    >
    > for myclass in ['A', 'B', 'C']:
    > code="class %s(Base):\n\tinit(self, name='%s')\n\t\tsuper(%s,
    > self).__init(name=name)\n"%dict(myclass, myclass.lower(), myclass())
    > ... codeop and eval stuff ...
    > a=A()
    > print a
    >
    > that gives: <class '__main__.A'>, but I want MyModule.A ;-)
    >
    > Can someone give me a hint how to create classes in a module with eval
    > and codeop so that they exist like the code was written in?
    >
    > Thanks,
    > AXEL.
    >

    You could try just importing the module - then, when it runs, its name
    won't be "__main__" but "MyModule".

    regards
    Steve
    --
    Meet the Python developers and your c.l.py favorites March 23-25
    Come to PyCon DC 2005 http://www.python.org/pycon/2005/
    Steve Holden http://www.holdenweb.com/
    Steve Holden, Feb 2, 2005
    #2
    1. Advertising

  3. On Wed, 02 Feb 2005 20:49:07 +0000, Axel Straschil wrote:

    You are doing several things wrong.

    > I was fooling around with creating classes for a module with eval,


    You shouldn't create classes with eval, because you don't need to.

    "class" isn't a declaration, it is an executable statement that creates
    new classes. We'll get into that momentarily...

    > something like:
    >
    > MyModule.py:
    >
    > class Base:
    > init(self, name):
    > self._name = name


    Your "init" function needs to be spelled "__init__", or it will not be
    automatically called.

    You also did not correctly use "def" to create your function. When posting
    to the newsgroup, try to use real code that you have actually executed.

    > that gives: <class '__main__.A'>, but I want MyModule.A ;-)


    No, it won't, since your code has syntax errors in it. Post the code you
    actually ran.

    That said, "__main__" indicates you ran it in the interactive shell. That
    is correct, and won't change. Also, the name printing the class gives is
    only very rarely important; overall you shouldn't be using that.

    I'll start with giving you this:

    -------

    import sys
    module = sys.modules[__name__]

    class Base:
    def __init__(self, name):
    self._name = name

    for myclass in ['A', 'B', 'C']:
    class Tmp(Base):
    myname = myclass
    def __init__(self):
    Base.__init__(self, self.myname)

    setattr(module, myclass, Tmp)

    -------

    Note that we don't need eval anywhere.

    But I'd suggest that this is more likely what you want:

    -------

    class Base:
    def __init__(self, name):
    self._name = name

    myClasses = {}

    for className in ['A', 'B', 'C']:
    class Tmp(Base):
    myname = className
    def __init__(self):
    Base.__init__(self, self.myname)

    myClasses[className] = Tmp

    -------

    Adding things straight to modules is rarely worth it; you're better off
    just collecting them somewhere.

    There are too many differences to go over here between my code and yours,
    so if you have questions, please ask. One of the reasons you don't want
    eval is that I had to give up trying to read your class code!

    Finally, while such generated classes do have their use, I'd ask what you
    are planning to do with this; odds are, you don't need it.

    In general, unless you are using "eval" to literally execute user supplied
    input, you *almost* certainly don't need it.

    A downside of my approach is that printing all three classes will say the
    class name is "Tmp". Since, as I said, you really shouldn't care about
    that, I don't care to try to fix it :) If you can provide a compelling
    reason why you need that, somebody here can help you with that.
    Jeremy Bowers, Feb 2, 2005
    #3
  4. On Wed, 02 Feb 2005 16:20:41 -0500, Jeremy Bowers wrote:
    > That said, "__main__" indicates you ran it in the interactive shell.


    Or ran it directly on the command line. Duh. I thought that clause really
    loudly, but I guess I never actually typed it.
    Jeremy Bowers, Feb 2, 2005
    #4
  5. Hello!

    > Note that we don't need eval anywhere.


    Uuups, that looks realy cool! Thanks for that!

    Im fooling around with generating html-tags. As there are only two kind
    of html tags, one who can nest chields, and one who cant, i wantet to
    play arround with something like:

    I've got two base classes, _Tag and _ContainerTag (for tags which can
    nest tags). Instead of getting an htmltag with _Tag(name='html'), I
    want to have a class for each html-tag. So, I thought of creating that
    classes dynamicly.

    my now (nearly) working code is:

    class _Tag(object):
    def __init__(self, name, flags=None, **props):
    [...]

    class _ContainerTag(_Tag):
    def __init__(self, name, contents=None, flags=None, **props):
    super(_ContainerTag, self).__init__(name=name, flags=flags, **props)
    self._contents = coalesce(contents, [])


    _module_name = sys.modules[__name__]

    class_dic = {}
    class_dic['Br'] = _Tag
    class_dic['Hr'] = _Tag
    class_dic['Html'] = _ContainerTag
    class_dic['Table'] = _ContainerTag

    for class_name, class_base in class_dic.items():
    class TmpClass(class_base):
    def __init__(self, **props):
    name = class_name.lower()
    #super(TmpClass, self).__init__(name=name, **props)
    class_base.__init__(self, name=name, **props)
    setattr(_module_name, class_name, TmpClass)

    br = Br()
    print br
    table = Table()
    print table

    br is printed OK, but for table, I get:
    AttributeError: 'TmpClass' object has no attribute '_contents'
    so, it seems that __init__ of _Tag is not called.
    If I try to do the commented line
    super(TmpClass, self).__init__(name=name, **props)
    instead of
    class_base.__init__(self, name=name, **props)
    I get:
    TypeError: super(type, obj): obj must be an instance or subtype of
    type
    for print table, print br ist processed OK.


    Thanks for help and your perfekt examples,
    AXEL.
    Axel Straschil, Feb 3, 2005
    #5
  6. Axel Straschil

    Peter Otten Guest

    Axel Straschil wrote:

    > class_dic = {}
    > class_dic['Br'] = _Tag
    > class_dic['Hr'] = _Tag
    > class_dic['Html'] = _ContainerTag
    > class_dic['Table'] = _ContainerTag
    >
    > for class_name, class_base in class_dic.items():
    > class TmpClass(class_base):
    > def __init__(self, **props):
    > name = class_name.lower()
    > #super(TmpClass, self).__init__(name=name, **props)
    > class_base.__init__(self, name=name, **props)
    > setattr(_module_name, class_name, TmpClass)


    While your workaround doesn't balk immediately, it doesn't do the right
    thing either.

    After the loop has finished, the global variable TmpClass will be bound to
    whatever class was created last, and the variable class_base will be bound
    to that the base class of that same TmpClass. Therefore only this last class
    is guaranteed to work as expected.

    A simplified example to demonstrate the binding problem:

    >>> classes = []
    >>> for text in ["alpha", "beta"]:

    .... class T:
    .... def __init__(self): print text
    .... classes.append(T)
    ....
    >>> classes[0] is classes[1]

    False # two distinct classes, as expected
    >>> classes[0]()

    beta
    <__main__.T instance at 0x402a9e2c>
    >>> classes[1]()

    beta
    <__main__.T instance at 0x402a9f8c>

    And now the proof that you are actually accessing the global variable:

    >>> text = "gamma"
    >>> classes[0]()

    gamma
    <__main__.T instance at 0x402a9f8c>

    One way to fix this is to introduce a factory function:

    >>> def make_class(text):

    .... class T:
    .... def __init__(self): print text
    .... return T
    ....
    >>> classes = []
    >>> for text in ["alpha", "beta"]:

    .... classes.append(make_class(text))
    ....
    >>> classes[0]()

    alpha
    <__main__.T instance at 0x402a9e4c>
    >>> classes[1]()

    beta
    <__main__.T instance at 0x402a9f8c>
    >>>



    Peter
    Peter Otten, Feb 3, 2005
    #6
  7. Hello!

    > After the loop has finished, the global variable TmpClass will be bound to
    > whatever class was created last, and the variable class_base will be bound
    > to that the base class of that same TmpClass. Therefore only this last class
    > is guaranteed to work as expected.


    Great, now it workes!

    _module_name = sys.modules[__name__]
    def _tag_class_factory(name, base):
    class T(base):
    def __init__(self, **props):
    super(T, self).__init__(name=name.lower(), **props)
    setattr(_module_name, name, T)
    class_dic = {}
    class_dic['Br'] = _Tag
    class_dic['Hr'] = _Tag
    class_dic['Html'] = _ContainerTag
    class_dic['Table'] = _ContainerTag
    class_dic['Td'] = _ContainerTag
    class_dic['Tr'] = _ContainerTag
    for name, base in class_dic.items():
    _tag_class_factory(name, base)
    print Table(contents=[Tr(contents=[Br()])])

    gives: <table><tr><br/></tr></table>

    Thanks,
    AXEL.
    Axel Straschil, Feb 3, 2005
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Tobel
    Replies:
    0
    Views:
    362
    Tobel
    Jan 27, 2006
  2. Replies:
    1
    Views:
    2,434
  3. spooky
    Replies:
    2
    Views:
    293
    spooky
    Jul 7, 2006
  4. Tony Johansson

    what is a modul

    Tony Johansson, Aug 16, 2005, in forum: C++
    Replies:
    2
    Views:
    399
    E. Robert Tisdale
    Aug 16, 2005
  5. Matej Cepl
    Replies:
    2
    Views:
    243
    Matej Cepl
    May 26, 2007
Loading...

Share This Page