instance + classmethod question

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Laszlo Zsolt Nagy

    #1

    instance + classmethod question


    Hello,

    Is it possible to tell, which instance was used to call the classmethod
    that is currently running?

    Background: I have a class called DatabaseConnect ion and it has a
    classmethod called process_create_ tables. This method should create some
    database tables defined by a database definition object. The
    DatabaseConnect ion has many descendants, for example
    PostgreSQLConne ction. Descendants know how to create tables in a given
    RDBMS type. I also use subclasses of the 'SQLProcessor' class, that
    processes SQL commands in different ways (print to stdout, write to
    file, execute directly in the database etc.) I would like to use the
    process_create_ tables classmethod as is, because sometimes I only need
    to save a SQL script. However, I also want to use the same classmethod
    to create tables directly into an existing database. That database is
    presented as a DatabaseConnect ion instance. In that case, I only want to
    create the tables that do not exists yet. Examples:

    processor = SQLProcessors.S tdOutProcessor( ) # Print to stdout
    PostgreSQLConne ction.process_c reate_tables(pr ocessor,dbdef) # This
    sould create all tables, using the processor

    processor = SQLProcessors.D irectProcessor( conn) # Execute directly
    conn.process_cr eate_tables(pro cessor,dbdef) # This should create
    non-existing tables only, using the processor

    Is this possible? Maybe there is a better way to achieve this, I'm not
    sure. I was thinking about this construct:

    @classsmethod
    def process_create_ tables(cls,proc essor,dbdef,con n=None)

    and then calling it as

    conn.process_cr eate_tables(pro cessor,dbdef,co nn)

    but this looks very ugly to me. It would be much easier if I could tell
    which instance (if any) was used to call the classmethod.

    Thanks,

    Les

  • Steven Bethard

    #2
    Re: instance + classmethod question

    Laszlo Zsolt Nagy wrote:[color=blue]
    >
    > Hello,
    >
    > Is it possible to tell, which instance was used to call the classmethod
    > that is currently running?
    >[/color]
    [snip][color=blue]
    >
    > processor = SQLProcessors.S tdOutProcessor( ) # Print to stdout
    > PostgreSQLConne ction.process_c reate_tables(pr ocessor,dbdef) # This
    > sould create all tables, using the processor
    >
    > processor = SQLProcessors.D irectProcessor( conn) # Execute directly
    > conn.process_cr eate_tables(pro cessor,dbdef) # This should create
    > non-existing tables only, using the processor
    >
    > Is this possible?[/color]

    It looks like you want a method that accepts either a class or an
    instance. I would typically create two methods, one for the class-based
    table-creating, and one for the instance-based table-creating. However,
    if you *have* to do it this way, you can introduce your own descriptor
    which should give you the behavior you want::
    [color=blue][color=green][color=darkred]
    >>> class ClassOrInstance Method(object):[/color][/color][/color]
    .... def __init__(self, func):
    .... self.func = func
    .... self.classfunc = classmethod(fun c)
    .... def __get__(self, obj, objtype=None):
    .... func = obj is None and self.classfunc or self.func
    .... return func.__get__(ob j, objtype)
    ....[color=blue][color=green][color=darkred]
    >>> class C(object):[/color][/color][/color]
    .... @ClassOrInstanc eMethod
    .... def f(*args):
    .... print args
    ....[color=blue][color=green][color=darkred]
    >>> C.f()[/color][/color][/color]
    (<class '__main__.C'>,)[color=blue][color=green][color=darkred]
    >>> C().f()[/color][/color][/color]
    (<__main__.C object at 0x00E73D90>,)

    Basically, if the descriptor is called from a type (and thus ``obj is
    None``), we return a bound classmethod, and if the descriptor is called
    from an instance, we return a bound instance method. Of course this now
    means you should write your code something like::

    @ClassOrInstanc eMethod
    def process_create_ tables(cls_or_s elf, processor, dbdef):
    ...

    whose "cls_or_sel f" parameter gives me a bad code smell. YMMV.

    STeVe

    Comment

    • Laszlo Zsolt Nagy

      #3
      Re: instance + classmethod question


      Hello Steven,

      I already implemented this using the form

      @classmethod
      def methodname(cls, other_params,se lf=None)

      but your example code looks so neat! This is exactly what I needed. :-)
      In my methods, most code is about string manipulation and calling other
      classmethods.
      There are only a few places where I can use an instance, but it is not
      required.
      I would like to reuse as most code as possible, so I do not want to
      create two different
      methods. That would result in duplicating code. Now the only problem is
      how I name this.
      It is not a classmethod, but it is also not a normal method. All right,
      it is a
      "ClassOrInstanc eMethod". Amazing! Probably Python is the only language
      that is
      flexible enough to do this. :-)

      Thanks again!

      Laszlo


      Steven Bethard wrote:
      [color=blue]
      >Laszlo Zsolt Nagy wrote:
      >
      >[color=green]
      >> Hello,
      >>
      >>Is it possible to tell, which instance was used to call the classmethod
      >>that is currently running?
      >>
      >>[color=darkred]
      > >>> class ClassOrInstance Method(object):[/color][/color]
      >... def __init__(self, func):
      >... self.func = func
      >... self.classfunc = classmethod(fun c)
      >... def __get__(self, obj, objtype=None):
      >... func = obj is None and self.classfunc or self.func
      >... return func.__get__(ob j, objtype)
      >...
      >
      >[/color]

      Comment

      • Steven Bethard

        #4
        Re: instance + classmethod question

        Laszlo Zsolt Nagy wrote:[color=blue]
        > In my methods, most code is about string manipulation and calling other
        > classmethods.
        > There are only a few places where I can use an instance, but it is not
        > required.
        > I would like to reuse as most code as possible, so I do not want to
        > create two different
        > methods. That would result in duplicating code.[/color]

        I would tend to do this by creating a wrapper method for the instance
        that did the appropriate stuff for the instance, and then called the
        classmethod, e.g.:

        class C(object):
        ...
        @classmethod
        def do_stuff(cls, *args):
        ...
        def do_instance_stu ff(self, *args):
        # instance stuff
        ...
        self.do_stuff(* args)
        # more instance stuff
        ...

        But it does require some factoring of the classmethod so that it makes
        sense to call it in this manner.

        STeVe

        Comment

        • Mike Meyer

          #5
          Re: instance + classmethod question

          Laszlo Zsolt Nagy <gandalf@design aproduct.biz> writes:[color=blue]
          > Is it possible to tell, which instance was used to call the
          > classmethod that is currently running?[/color]

          Ok, I read through what got to my nntp server, and I'm still
          completely confused.

          A class method isn't necessarilry called by an instance. That's why
          it's a class method. What should happen in that case?

          You provided an example where you passed self as an optional
          argument. If it's going to have self, shouldn't it be an instance
          method?

          I think I agree with Steven - you should use two methods. You deal
          with the issue of duplicated code by pulling the code that would be
          duplicated out into private methods. This would be a straightforward
          refactoring problem.

          <mike
          --
          Mike Meyer <mwm@mired.or g> http://www.mired.org/home/mwm/
          Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.

          Comment

          • Laszlo Zsolt Nagy

            #6
            Re: instance + classmethod question

            Mike Meyer wrote:
            [color=blue]
            >Laszlo Zsolt Nagy <gandalf@design aproduct.biz> writes:
            >
            >[color=green]
            >>Is it possible to tell, which instance was used to call the
            >>classmethod that is currently running?
            >>
            >>[/color]
            >
            >Ok, I read through what got to my nntp server, and I'm still
            >completely confused.
            >
            >A class method isn't necessarilry called by an instance. That's why
            >it's a class method. What should happen in that case?
            >
            >[/color]
            Here is the answer (an example):

            @ClassOrInstanc eMethod
            def process_create_ table(cls_or_se lf,tabledef,pro cessor):
            """Process the CREATE TABLE command.

            @param tabledef: a L{TableDefiniti on} instance.
            @param processor: a L{SQLProcessor} instance."""
            hname = cls_or_self.has hident(tabledef .name)
            if (isinstance(cls _or_self,type)) or (not
            cls_or_self.ist ableexists(hnam e)):
            processor.addli ne("create table %s \n ("% hname)
            for field in tabledef.fields :
            if not (field() is None):

            cls_or_self.pro cess_create_tab le_field(field( ),processor)
            processor.addli ne(",")
            processor.trunc ate_last_comma( )
            processor.addli ne("")
            processor.addli ne(")")
            cls_or_self.add tablespaceclaus e(tabledef,proc essor)
            processor.proce ssbuffer()

            So if the method was called with an instance, it will check if the table
            exists and create the table only if it did not exist before.
            But if the method was called with a class, it will create the table anyway.

            The above method is just a short example. I have many methods for
            creating sequences, triggers, constraings etc.
            The full pattern is:

            def process_XXXXXXX (cls_or_self,de fobject,process or):
            <longer code>
            <a condition, depending on the class or the instance>
            <longer code>
            <another condition, depending on the class or the instance>
            <longer code>

            There are two reasons why I do not want to create two methods (one
            instance and one class method).

            1. If you look the above pattern, it is clear that the method does the
            same thing, just there are some conditions when I call it with an
            instance. I do not want to call "process_create _table_with_cla ss" and
            "process_create _table_with_ins tance", because the name of the method
            should reflect what it does primarily. (BTW, I also looked at
            multimethods, but they are not exactly for this kind of problem.)

            2. The pattern above makes it clear that I just can't easily split the
            method into elementary parts. Steven wrote this pattern:
            [color=blue]
            >class C(object):
            > ...
            > @classmethod
            > def do_stuff(cls, *args):
            > ...
            > def do_instance_stu ff(self, *args):
            > # instance stuff
            > ...
            > self.do_stuff(* args)
            > # more instance stuff
            >
            >[/color]
            But I cannot do this, because primarily I do class stuff, and in some
            cases, I can make use of an instance (but do not require it).
            Comments welcome

            Les

            Comment

            • Mike Meyer

              #7
              Re: instance + classmethod question

              Laszlo Zsolt Nagy <gandalf@design aproduct.biz> writes:[color=blue]
              > Mike Meyer wrote:[color=green]
              >>Laszlo Zsolt Nagy <gandalf@design aproduct.biz> writes:[color=darkred]
              >>>Is it possible to tell, which instance was used to call the
              >>>classmetho d that is currently running?[/color]
              >>Ok, I read through what got to my nntp server, and I'm still
              >>completely confused.
              >>A class method isn't necessarilry called by an instance. That's why
              >>it's a class method. What should happen in that case?[/color]
              > Here is the answer (an example):
              >
              > @ClassOrInstanc eMethod
              > def process_create_ table(cls_or_se lf,tabledef,pro cessor):
              > """Process the CREATE TABLE command.
              >
              > @param tabledef: a L{TableDefiniti on} instance.
              > @param processor: a L{SQLProcessor} instance."""
              > hname = cls_or_self.has hident(tabledef .name)
              > if (isinstance(cls _or_self,type)) or (not
              > cls_or_self.ist ableexists(hnam e)):
              > processor.addli ne("create table %s \n ("% hname)
              > for field in tabledef.fields :
              > if not (field() is None):
              > cls_or_self.pro cess_create_tab le_field(field( ),processor)
              > processor.addli ne(",")
              > processor.trunc ate_last_comma( )
              > processor.addli ne("")
              > processor.addli ne(")")
              > cls_or_self.add tablespaceclaus e(tabledef,proc essor)
              > processor.proce ssbuffer()[/color]

              I assume that hashident, istableexists, process_create_ table_field and
              addtablespacecl ause all do this screwy ClassOrInstance Method thing?
              [color=blue]
              > There are two reasons why I do not want to create two methods (one
              > instance and one class method).
              >
              > 1. If you look the above pattern, it is clear that the method does the
              > same thing, just there are some conditions when I call it with an
              > instance. I do not want to call "process_create _table_with_cla ss" and
              > "process_create _table_with_ins tance", because the name of the method
              > should reflect what it does primarily. (BTW, I also looked at
              > multimethods, but they are not exactly for this kind of problem.)[/color]

              Well, I'd call them process_create_ table_for_insta nce and
              create_table_fo r_class, but that's just me.
              [color=blue]
              > 2. The pattern above makes it clear that I just can't easily split the
              > method into elementary parts. Steven wrote this pattern:[/color]

              I didn't say it would be easy - just well understood, and on seeing
              this, maybe not that. On the other hand, being hard doesn't mean it's
              not worth doing.
              [color=blue]
              > But I cannot do this, because primarily I do class stuff, and in some
              > cases, I can make use of an instance (but do not require it).
              > Comments welcome[/color]

              Proposing an alternative design is pretty much impossible, because I
              don't know how the class hierarchy fits together. If there isn't much
              of a hierarchy, maybe you'd be better off with an ADT model than an
              object model?

              <mike
              --
              Mike Meyer <mwm@mired.or g> http://www.mired.org/home/mwm/
              Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.

              Comment

              Working...