Play with classes

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Zunbeltz Izaola

    Play with classes

    Hi to all!

    I wonder if it possible (i'm sure python can do :-) ) to define classes on
    runtime. My problem (schematically) is the folowwin.

    The User can choise betwenn 3 property of an object.

    Mode, Type and Subtype.

    I have the following classes defined

    ModeA, ModeB, TypeA, TypeB, TypeC, SubtypeA, SubtypeB.

    Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
    something like

    class UserClass(ModeA , TypeB, SubtypeB):
    pass

    I can define all the posibilitys different classes and the using nested
    if/else I can use the correct class, but I want to know if there is a way
    generate in the fly and in this way there is no necesarity to change code whe
    new Modes or Types are created.

    I hope it is clear enough :-)

    Thanks in advance

    Zunbeltz
  • Peter Otten

    #2
    Re: Play with classes

    Zunbeltz Izaola wrote:
    [color=blue]
    > Hi to all!
    >
    > I wonder if it possible (i'm sure python can do :-) ) to define classes on
    > runtime. My problem (schematically) is the folowwin.
    >
    > The User can choise betwenn 3 property of an object.
    >
    > Mode, Type and Subtype.
    >
    > I have the following classes defined
    >
    > ModeA, ModeB, TypeA, TypeB, TypeC, SubtypeA, SubtypeB.
    >
    > Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
    > something like
    >
    > class UserClass(ModeA , TypeB, SubtypeB):
    > pass
    >
    > I can define all the posibilitys different classes and the using nested
    > if/else I can use the correct class, but I want to know if there is a way
    > generate in the fly and in this way there is no necesarity to change code
    > whe new Modes or Types are created.
    >
    > I hope it is clear enough :-)
    >
    > Thanks in advance
    >
    > Zunbeltz[/color]

    How about assigning to __bases__?
    [color=blue][color=green][color=darkred]
    >>> class A:[/color][/color][/color]
    .... def alpha(self): print "alpha"
    ....[color=blue][color=green][color=darkred]
    >>> class B:[/color][/color][/color]
    .... def beta(self): print "beta"
    ....[color=blue][color=green][color=darkred]
    >>> class C: pass[/color][/color][/color]
    ....[color=blue][color=green][color=darkred]
    >>> C.__bases__[/color][/color][/color]
    ()[color=blue][color=green][color=darkred]
    >>> C.__bases__ = (A,B)
    >>> c = C()
    >>> c.alpha()[/color][/color][/color]
    alpha[color=blue][color=green][color=darkred]
    >>> c.beta()[/color][/color][/color]
    beta[color=blue][color=green][color=darkred]
    >>>[/color][/color][/color]

    Trying the same thing with newstyle class resulted in a TypeError:
    [color=blue][color=green][color=darkred]
    >>> E.__bases__ = (A, B)[/color][/color][/color]
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    TypeError: __bases__ assignment: 'A' deallocator differs from 'object'

    Peter

    Comment

    • Jacek Generowicz

      #3
      Re: Play with classes

      Zunbeltz Izaola <zunbeltz@wm.lc .ehu.es.XXX> writes:
      [color=blue]
      > I have the following classes defined
      >
      > ModeA, ModeB, TypeA, TypeB, TypeC, SubtypeA, SubtypeB.
      >
      > Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
      > something like
      >
      > class UserClass(ModeA , TypeB, SubtypeB):
      > pass
      >
      > I can define all the posibilitys different classes and the using nested
      > if/else I can use the correct class, but I want to know if there is a way
      > generate in the fly and in this way there is no necesarity to change code whe
      > new Modes or Types are created.[/color]

      Is the following what you want ?

      =============== =============== =============== =========
      class TypeA: pass
      class TypeB: pass
      class TypeC: pass
      class SubtypeA: pass
      class SubtypeB: pass

      collect_bases = [(Mode, Type, Subtype)
      for Mode in [ModeA, ModeB]
      for Type in [TypeA, TypeB, TypeC]
      for Subtype in [SubtypeA, SubtypeB]]

      count = 0
      for bases in collect_bases:
      name = "UserClass% d" % count
      the_class = type(name, bases, {})
      globals()[name] = the_class
      count += 1
      =============== =============== =============== =========

      Now you can try
      [color=blue][color=green][color=darkred]
      >>> UserClass0.__ba ses__[/color][/color][/color]
      (<class '__main__.ModeA '>, <class '__main__.TypeA '>, <class
      '__main__.Subty peA'>)
      [color=blue][color=green][color=darkred]
      >>> UserClass1.__ba ses__[/color][/color][/color]
      (<class '__main__.ModeA '>, <class '__main__.TypeA '>, <class
      '__main__.Subty peB'>)[color=blue][color=green][color=darkred]
      >>> UserClass2.__ba ses__[/color][/color][/color]
      (<class '__main__.ModeA '>, <class '__main__.TypeB '>, <class
      '__main__.Subty peA'>)

      And so on, until
      [color=blue][color=green][color=darkred]
      >>> UserClass11.__b ases__[/color][/color][/color]
      (<class '__main__.ModeB '>, <class '__main__.TypeC '>, <class
      '__main__.Subty peB'>)

      Comment

      • Jacek Generowicz

        #4
        Re: Play with classes

        Jacek Generowicz <jacek.generowi cz@cern.ch> writes:
        [color=blue]
        > =============== =============== =============== =========
        > class TypeA: pass
        > class TypeB: pass
        > class TypeC: pass
        > class SubtypeA: pass
        > class SubtypeB: pass
        >
        > collect_bases = [(Mode, Type, Subtype)
        > for Mode in [ModeA, ModeB]
        > for Type in [TypeA, TypeB, TypeC]
        > for Subtype in [SubtypeA, SubtypeB]]
        >
        > count = 0
        > for bases in collect_bases:
        > name = "UserClass% d" % count
        > the_class = type(name, bases, {})
        > globals()[name] = the_class
        > count += 1
        > =============== =============== =============== =========[/color]

        Oooops, the first 4 lines got lost:

        =============== =============== =============== =========
        __metaclass__ = type

        class ModeA: pass
        class ModeB: pass
        class TypeA: pass
        class TypeB: pass
        class TypeC: pass
        class SubtypeA: pass
        class SubtypeB: pass

        collect_bases = [(Mode, Type, Subtype)
        for Mode in [ModeA, ModeB]
        for Type in [TypeA, TypeB, TypeC]
        for Subtype in [SubtypeA, SubtypeB]]

        count = 0
        for bases in collect_bases:
        name = "UserClass% d" % count
        globals()[name] = type(name, bases, {})
        count += 1
        =============== =============== =============== =========

        Comment

        • Zunbeltz Izaola

          #5
          Re: Play with classes

          Jacek Generowicz <jacek.generowi cz@cern.ch> writes:

          Thank for the help.[color=blue]
          >
          > Oooops, the first 4 lines got lost:
          >
          > =============== =============== =============== =========
          > __metaclass__ = type
          >[/color]

          I am not an expert and i don't understand very well things like
          __metaclass__, but it not redundat this line? From the Language Reference

          """
          __metaclass__
          This variable can be any callable accepting arguments for name, bases,
          and dict. Upon class creation, the callable is used instead of the
          built-in type(). New in version 2.2.
          """

          so I think __metaclass__ = type is the same as not defining __metaclass__

          Regards,

          Zunbeltz
          [color=blue]
          > class ModeA: pass
          > class ModeB: pass
          > class TypeA: pass
          > class TypeB: pass
          > class TypeC: pass
          > class SubtypeA: pass
          > class SubtypeB: pass
          >
          > collect_bases = [(Mode, Type, Subtype)
          > for Mode in [ModeA, ModeB]
          > for Type in [TypeA, TypeB, TypeC]
          > for Subtype in [SubtypeA, SubtypeB]]
          >
          > count = 0
          > for bases in collect_bases:
          > name = "UserClass% d" % count
          > globals()[name] = type(name, bases, {})
          > count += 1
          > =============== =============== =============== =========[/color]

          Comment

          • Terry Reedy

            #6
            Re: Play with classes


            "Zunbeltz Izaola" <zunbeltz@wm.lc .ehu.es.XXX> wrote in message
            news:cth4qte878 u.fsf@lcpxdf.wm .lc.ehu.es...[color=blue]
            > Hi to all!
            >
            > I wonder if it possible (i'm sure python can do :-) ) to define classes[/color]
            on[color=blue]
            > runtime. My problem (schematically) is the folowwin.[/color]

            The class statement, like all statements except the global directive, it a
            runtime executable statement. So, in a sense, all class objects are
            defined (created) at runtime. So you are perhaps asking, "Can I write a
            class statement at runtime (using user input)?" If so, yes. Create a
            string with the code you want executed, then exec it with an exec
            statement. Or you can use the approach others suggested of interpreting
            user input to build up a class object. Your choice.

            Terry J. Reedy




            Comment

            • Michele Simionato

              #7
              Re: Play with classes

              Peter Otten <__peter__@web. de> wrote in message news:<c1kmj0$28 f$04$1@news.t-online.com>...[color=blue]
              > Trying the same thing with newstyle class resulted in a TypeError:
              >[color=green][color=darkred]
              > >>> E.__bases__ = (A, B)[/color][/color]
              > Traceback (most recent call last):
              > File "<stdin>", line 1, in ?
              > TypeError: __bases__ assignment: 'A' deallocator differs from 'object'
              >[/color]

              You can't do that with new style classes! I guess because of some
              subtle issue with metaclasses, but I don't really know.

              The OP needs "type", the custom metaclass:

              UserClass=type( "UserClass",(Mo deA, TypeB, SubtypeB),{})


              Michele Simionato

              Comment

              • David M. Cook

                #8
                Re: Play with classes

                In article <cth4qte878u.fs f@lcpxdf.wm.lc. ehu.es>, Zunbeltz Izaola wrote:
                [color=blue]
                > Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
                > something like
                >
                > class UserClass(ModeA , TypeB, SubtypeB):[/color]


                You can use the type builtin (2.2 and above) to create a class type
                dynamically. Syntax is

                type(name_strin g, bases_tuple, methods_dict)

                For example:

                In [68]: foo = type('Foo', (object,), {})
                In [69]: foo.mro()
                Out[69]: [<class '__main__.Foo'> , <type 'object'>]

                Dave Cook

                Comment

                • Mike C. Fletcher

                  #9
                  Re: Play with classes

                  As another poster noted, all classes are created at run-time. There's
                  even a hook that let's you intercept the creation of a class called the
                  "metaclass hook", however, to deal with the question directly before I
                  go off on a tangent...

                  There are two major approaches you can take to elegantly composing
                  classes at run-time; Factory functions and metaclasses. Here's an
                  example of the factory function approach, (which is probably most
                  appropriate in your case (user is only creating new classes via GUI
                  interactions, no need for ability to sub-class in Python code, no need
                  for addition of newly-created methods/functions, i.e. straight
                  composition)):

                  def createClass( name, baseClasses, dictionary ):
                  """Create a new class object and return it"""
                  # reshuffle baseClasses here
                  # manipulate dictionary here
                  # check that name is unique here
                  # register with some global registry here
                  # register pickle helpers here
                  if definitions.has _key( uniqueFingerpri nt):
                  return that
                  else:
                  # if you were paying attention, you'll notice
                  # that save for the manipulation comments we
                  # just call this...
                  return type( name, baseClasses, dictionary )

                  If, however, your users may *also* want to define these classes as part
                  of Python modules (for an extension mechanism), you may want to subclass
                  "type" to encode your registration/reshuffling/manipulation/etc.
                  directly and then use that metaclass (sub-class of type) for each of
                  your classes:

                  class MetaFoo( type ):
                  """Metaclas s for the example code"""
                  definitions = {}
                  def __new__( metacls, name, bases, dictionary ):
                  # reshuffle baseClasses here
                  # manipulate dictionary here
                  # check that name is unique here
                  uniqueFingerpri nt = name, bases
                  if metacls.definit ions.has_key( uniqueFingerpri nt ):
                  # Note: this likely *isn't* a good idea, as it can really
                  # surprise your users to discover that their classes
                  # are silently unified with another class! Just a demo...
                  return metacls.definit ions.get( uniqueFingerpri nt )
                  else:
                  result = super(MetaFoo,m etacls).__new__ (
                  metacls, name, bases, dictionary
                  )
                  metacls.definit ions[ uniqueFingerpri nt ] = result
                  # register with some global registry here
                  # register pickle helpers here
                  return result

                  __metaclass__ = MetaFoo
                  class ModeA:
                  pass

                  class ModeB:
                  pass

                  class ModeC( ModeA, ModeB ):
                  pass
                  class ModeD( ModeA, ModeB ):
                  pass

                  print MetaFoo( 'A', (), {} ) is MetaFoo( 'A', (), {} )

                  As noted in the comments above, likely you don't even want the effect of
                  having the class-cache (too confusing for users, mostly, but I wanted
                  some sort of manipulation to stick in to say "something happens here" :)
                  ), so there's no particular value to the metaclass version for your
                  case. I just wanted an opportunity to work on an example for my talk...
                  I'm sick, I know...

                  Some things to keep in mind:

                  * Your new class instances will *not* be pickle-able (using either
                  method) unless they are registered explicitly somewhere in an
                  importable module. You will need to figure out how to ensure
                  that, on unpickling, your newly-created classes are available
                  (e.g. by storing their definitions in a database somewhere and
                  hooking import to treat the DB as a module).
                  * metaclasses are more fun :) , but they take some getting used to,
                  and are probably overkill for this simple excursion into
                  metaprogramming
                  * The "new" module has an (ironically) older API for creating class
                  objects

                  Have fun,
                  Mike

                  Zunbeltz Izaola wrote:
                  [color=blue]
                  >Hi to all!
                  >
                  >I wonder if it possible (i'm sure python can do :-) ) to define classes on
                  >runtime. My problem (schematically) is the folowwin.
                  >
                  >The User can choise betwenn 3 property of an object.
                  >
                  >Mode, Type and Subtype.
                  >
                  >I have the following classes defined
                  >
                  >ModeA, ModeB, TypeA, TypeB, TypeC, SubtypeA, SubtypeB.
                  >
                  >Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
                  >something like
                  >
                  >class UserClass(ModeA , TypeB, SubtypeB):
                  > pass
                  >
                  >I can define all the posibilitys different classes and the using nested
                  >if/else I can use the correct class, but I want to know if there is a way
                  >generate in the fly and in this way there is no necesarity to change code whe
                  >new Modes or Types are created.
                  >
                  >[/color]
                  ....


                  Comment

                  • Jacek Generowicz

                    #10
                    Re: Play with classes

                    Zunbeltz Izaola <zunbeltz@wm.lc .ehu.es.XXX> writes:
                    [color=blue]
                    > Jacek Generowicz <jacek.generowi cz@cern.ch> writes:
                    >
                    > Thank for the help.[/color]

                    I hope that it is what you were asking for.

                    Of course, my posting such a solution should in no way be interpreted
                    as a suggestion that it is appropriate to your _real_ problem. You may
                    well want a completely different approach, but I can't tell without
                    knowing more about your application.
                    [color=blue][color=green]
                    > > __metaclass__ = type[/color]
                    >
                    > I am not an expert and i don't understand very well things like
                    > __metaclass__, but it not redundat this line? From the Language Reference
                    >
                    > """
                    > __metaclass__
                    > This variable can be any callable accepting arguments for name, bases,
                    > and dict. Upon class creation, the callable is used instead of the
                    > built-in type(). New in version 2.2.
                    > """
                    >
                    > so I think __metaclass__ = type is the same as not defining __metaclass__[/color]

                    It is indeed tempting to conclude that from what you have quoted.

                    One of the beauties of Python is its highly interactive nature, and
                    the ease with which you can try things out. Your hypothesis that
                    "__metaclas s__ = type is the same as not defining __metaclass__" can
                    be refuted by the Python interpreter itself within about 10 seconds of
                    work. Take the code I sent, remove the binding of __metaclass__ and
                    run the code. You will find that Python replies:

                    TypeError: a new-style class can't have only classic bases

                    As written in my example, ("class ModeA: pass" etc.) all the classes
                    are classic, because they do not inherit from object.
                    [color=blue][color=green][color=darkred]
                    >>> class classic: pass[/color][/color][/color]
                    ....[color=blue][color=green][color=darkred]
                    >>> class newstyle(object ): pass[/color][/color][/color]
                    ....[color=blue][color=green][color=darkred]
                    >>> type(classic)[/color][/color][/color]
                    <type 'class'>[color=blue][color=green][color=darkred]
                    >>> type(newstyle)[/color][/color][/color]
                    <type 'type'>

                    I want them to be new-style classes, because I am going to create new
                    classes which inherit from them, using type. This means of creating
                    classes creates new-style classes, and new style classes, as the error
                    message above suggests, "can't have only classic bases".

                    So, I could either make ModeA & co inherit from object, or I could
                    make all classes new-style ones by default, by binding __metaclass__
                    to type.

                    Alternatively, I could not use type to create the UserClasses, but
                    types.ClassType (types is a module). Alternatively I could use use
                    type(ModeA) which would pick the appropriate metaclass for creation of
                    your UserClasses depending on the situation.
                    [color=blue]
                    > """
                    > __metaclass__
                    > This variable can be any callable accepting arguments for name, bases,
                    > and dict. Upon class creation, the callable is used instead of the
                    > built-in type(). New in version 2.2.
                    > """[/color]

                    Hmm. Is that a documentation bug? I suspect that it should read "
                    .... instead of types.ClassType "


                    [ So, here's a classic class version of the original:

                    class ModeA: pass
                    class ModeB: pass
                    class TypeA: pass
                    class TypeB: pass
                    class TypeC: pass
                    class SubtypeA: pass
                    class SubtypeB: pass

                    collect_bases = [(Mode, Type, Subtype)
                    for Mode in [ModeA, ModeB]
                    for Type in [TypeA, TypeB, TypeC]
                    for Subtype in [SubtypeA, SubtypeB]]

                    count = 0
                    for bases in collect_bases:
                    name = "UserClass% d" % count
                    globals()[name] = type(ModeA)(nam e, bases, {})
                    # or you could "import types" and do the following
                    # globals()[name] = types.ClassType (name, bases, {})
                    count += 1

                    ]

                    Comment

                    Working...