Question on metaclasses

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Steffen Glückselig

    #1

    Question on metaclasses

    Hello,

    I've been experimenting with metaclasses a bit (even though I am quite
    a newbie to python) and stumpled over the following problem in my code:

    class Meta(type):
    def __init__(cls, name, bases, dct):
    for attr, value in dct.items():
    if callable(value) :
    dct[attr] = wrapper(value)

    wrapper adds debugging-information to methods of the class (at least
    that is my plan).

    Using dct[attr] = wrapper(value) does not result in wrapped methods,
    though. Using setattr(cls, attr, wrapper(value)) creates the desired
    effect, though.

    Why are the changes to dct not visible in the instantiated class? Is
    dct not the namespace of the class currently instantiated?


    best regards
    Steffen

  • Diez B. Roggisch

    #2
    Re: Question on metaclasses

    Steffen Glückselig wrote:
    [color=blue]
    > Hello,
    >
    > I've been experimenting with metaclasses a bit (even though I am quite
    > a newbie to python) and stumpled over the following problem in my code:
    >
    > class Meta(type):
    > def __init__(cls, name, bases, dct):
    > for attr, value in dct.items():
    > if callable(value) :
    > dct[attr] = wrapper(value)
    >
    > wrapper adds debugging-information to methods of the class (at least
    > that is my plan).
    >
    > Using dct[attr] = wrapper(value) does not result in wrapped methods,
    > though. Using setattr(cls, attr, wrapper(value)) creates the desired
    > effect, though.
    >
    > Why are the changes to dct not visible in the instantiated class? Is
    > dct not the namespace of the class currently instantiated?[/color]

    You don't use metaclasses correctly I believe. Usage should look like this:

    class Foo(type):
    def __new__(cls, name, bases, dict):

    for k,v in [(k, v) for k,v in dict.items() if callable(v)]:
    cls.wrap(k,v,cl s.get_directive s(v), dict)

    return super(Foo, self).__new__(s elf, name, bases, dict)

    Notice the __new__ instead of __init__, and the call to (actually, through
    super) type.__new__

    --
    Regards,

    Diez B. Roggisch

    Comment

    • Diez B. Roggisch

      #3
      Re: Question on metaclasses

      > class Foo(type):[color=blue]
      > def __new__(cls, name, bases, dict):
      >
      > for k,v in [(k, v) for k,v in dict.items() if callable(v)]:
      > cls.wrap(k,v,cl s.get_directive s(v), dict)
      >
      > return super(Foo, self).__new__(s elf, name, bases, dict)[/color]

      There is a confusion of self and cls above - rename self with cls.
      --
      Regards,

      Diez B. Roggisch

      Comment

      • Reinhold Birkenfeld

        #4
        Re: Question on metaclasses

        Diez B. Roggisch wrote:[color=blue]
        > Steffen Glückselig wrote:
        >[color=green]
        >> Hello,
        >>
        >> I've been experimenting with metaclasses a bit (even though I am quite
        >> a newbie to python) and stumpled over the following problem in my code:
        >>
        >> class Meta(type):
        >> def __init__(cls, name, bases, dct):
        >> for attr, value in dct.items():
        >> if callable(value) :
        >> dct[attr] = wrapper(value)
        >>
        >> wrapper adds debugging-information to methods of the class (at least
        >> that is my plan).
        >>
        >> Using dct[attr] = wrapper(value) does not result in wrapped methods,
        >> though. Using setattr(cls, attr, wrapper(value)) creates the desired
        >> effect, though.
        >>
        >> Why are the changes to dct not visible in the instantiated class? Is
        >> dct not the namespace of the class currently instantiated?[/color]
        >
        > You don't use metaclasses correctly I believe. Usage should look like this:
        >
        > class Foo(type):
        > def __new__(cls, name, bases, dict):
        >
        > for k,v in [(k, v) for k,v in dict.items() if callable(v)]:
        > cls.wrap(k,v,cl s.get_directive s(v), dict)
        >
        > return super(Foo, self).__new__(s elf, name, bases, dict)[/color]
        ^^^^ ^^^^
        self is not bound in your method; use

        return super(Foo, cls).__new__(na me, bases, dict)

        Reinhold

        Comment

        • Reinhold Birkenfeld

          #5
          Re: Question on metaclasses

          Diez B. Roggisch wrote:[color=blue][color=green]
          >> class Foo(type):
          >> def __new__(cls, name, bases, dict):
          >>
          >> for k,v in [(k, v) for k,v in dict.items() if callable(v)]:
          >> cls.wrap(k,v,cl s.get_directive s(v), dict)
          >>
          >> return super(Foo, self).__new__(s elf, name, bases, dict)[/color]
          >
          > There is a confusion of self and cls above - rename self with cls.[/color]

          And remove the first argument to __new__.

          Reinhold

          Comment

          • Steffen Glückselig

            #6
            Re: Question on metaclasses

            Are wrap and get_directives somehow built-in? I couldn't find
            references to them.

            I've noticed, too, that using __new__ I can manipulate the dictionary
            resulting in the behavior I intented.

            I'd rather like to know: Why does it work in __new__ but not in
            __init__?

            And, stimulated by your response: Why is using __new__ superior to
            __init__?


            best regards
            Steffen

            Comment

            • Reinhold Birkenfeld

              #7
              Re: Question on metaclasses

              Steffen Glückselig wrote:[color=blue]
              > Are wrap and get_directives somehow built-in? I couldn't find
              > references to them.
              >
              > I've noticed, too, that using __new__ I can manipulate the dictionary
              > resulting in the behavior I intented.
              >
              > I'd rather like to know: Why does it work in __new__ but not in
              > __init__?
              >
              > And, stimulated by your response: Why is using __new__ superior to
              > __init__?[/color]

              At short, __new__ is called on the class and must return the instance; that
              means that the instance isn't created yet.

              __init__, on the other hand, is called on the instance which is already created
              at that point. So changing dct in __init__ is pointless, because the instance
              (which is a class actually) is already created.

              Reinhold

              Comment

              • Diez B. Roggisch

                #8
                Re: Question on metaclasses

                Reinhold Birkenfeld wrote:
                [color=blue]
                > Diez B. Roggisch wrote:[color=green][color=darkred]
                >>> class Foo(type):
                >>> def __new__(cls, name, bases, dict):
                >>>
                >>> for k,v in [(k, v) for k,v in dict.items() if callable(v)]:
                >>> cls.wrap(k,v,cl s.get_directive s(v), dict)
                >>>
                >>> return super(Foo, self).__new__(s elf, name, bases, dict)[/color]
                >>
                >> There is a confusion of self and cls above - rename self with cls.[/color]
                >
                > And remove the first argument to __new__.[/color]

                Ehm, no, I don't think so. The code was copied and pasted from a working
                metaclass (at least I hope so...), and in the original code a underscore
                was used for the first argument. I've been following that bad habit for a
                while but recently started to be more following to the established
                conventions. So I rewrote that code on the fly.

                This is the full metaclass:

                class TransactionAwar e(type):
                TAS_REX = re.compile("tas ::([^, ]+(, *[^, ]+)*)")


                WRAPPERS = {
                "create" : w_create,
                "active" : w_active,
                "bind" : w_bind,
                "sync" : w_sync,
                "autocommit " : w_autocommit,
                }
                def __new__(_, name, bases, dict):

                for k,v in [(k, v) for k,v in dict.items() if callable(v)]:
                _.wrap(k,v,_.ge t_directives(v) , dict)

                return super(Transacti onAware, _).__new__(_, name, bases, dict)

                def wrap(_, name, fun, ds, dict, level=0):
                if ds:
                d, rest = ds[0], ds[1:]
                key = "_taw_%s_%i " % (name, level)
                try:
                w_fun = _.WRAPPERS[d](key)
                dict[key] = fun
                _.wrap(name, w_fun, rest, dict, level+1)
                except KeyError, e:
                print "No transaction aware property named %s" % d
                raise e
                else:

                dict[name] = fun

                wrap = classmethod(wra p)

                def get_directives( _, v):
                try:
                doc = v.__doc__
                res = []
                if doc:
                for l in doc.split("\n") :
                m = _.TAS_REX.match (l.strip())
                if m:
                res += [s.strip() for s in m.group(1 ).split(",")]
                res.reverse()
                return res
                except KeyError:
                return []
                get_directives = classmethod(get _directives)



                --
                Regards,

                Diez B. Roggisch

                Comment

                • Steffen Glückselig

                  #9
                  Re: Question on metaclasses

                  So dct is something like a template rather than the __dict__ of the
                  actual class?

                  I'd assume that changing the content of a dict would be possible even
                  after it has been assigned to some object (here, a class).


                  thanks and best regards
                  Steffen

                  Comment

                  • Reinhold Birkenfeld

                    #10
                    Re: Question on metaclasses

                    Diez B. Roggisch wrote:[color=blue]
                    > Reinhold Birkenfeld wrote:
                    >[color=green]
                    >> Diez B. Roggisch wrote:[color=darkred]
                    >>>> class Foo(type):
                    >>>> def __new__(cls, name, bases, dict):
                    >>>>
                    >>>> for k,v in [(k, v) for k,v in dict.items() if callable(v)]:
                    >>>> cls.wrap(k,v,cl s.get_directive s(v), dict)
                    >>>>
                    >>>> return super(Foo, self).__new__(s elf, name, bases, dict)
                    >>>
                    >>> There is a confusion of self and cls above - rename self with cls.[/color]
                    >>
                    >> And remove the first argument to __new__.[/color]
                    >
                    > Ehm, no, I don't think so. The code was copied and pasted from a working
                    > metaclass (at least I hope so...), and in the original code a underscore
                    > was used for the first argument. I've been following that bad habit for a
                    > while but recently started to be more following to the established
                    > conventions. So I rewrote that code on the fly.[/color]

                    Oh yes. I forgot that __new__ is a staticmethod anyhow.

                    Reinhold

                    Comment

                    Working...