1

I currently have this code:

class Generator(object):
    def __getattr__(self, name):    
        def f(self):
            return ("Result of"+name, self)
        f.__name__ = name
        return f

    def foo(self):
        pass

g = Generator()
print g.foo
print Generator.foo
print g.bar
print Generator.bar

Which gives:

<bound method Generator.foo of <__main__.Generator object at 0x00B62D30>>
<unbound method Generator.foo>
<function bar at 0x00A9DE70>
AttributeError: type object 'Generator' has no attribute 'bar'

What do I have to do to make it give:

<bound method Generator.foo of <__main__.Generator object at 0x00B62D30>>
<unbound method Generator.foo>
<bound method Generator.bar of <__main__.Generator object at 0x00B62D30>>
<unbound method Generator.bar>
1
  • 1
    mov_i? Isn't that from some other code? Your code produces AttributeError: type object 'Generator' has no attribute 'bar' for me, which makes more sense. Commented Nov 2, 2012 at 11:23

4 Answers 4

2

Here's a metaclass that adds the __getattr__ function from the class definition back to the metaclass itself. This avoids having to define the function in multiple places, or as a separate global function defined beforehand and added individually to the metaclass and class.

class Meta(type):

    def __new__(mcls, name, bases, dikt):
        fgetattr = dikt.get('__getattr__')
        if fgetattr is not None:
            setattr(mcls, '__getattr__', fgetattr)
        return super(Meta, mcls).__new__(mcls, name, bases, dikt)

class Generator(object):
    __metaclass__ = Meta

    def __getattr__(obj, name):

        def f(self):
            return "Result of %s for %r" % (name, self)
        f.__name__ = name

        if isinstance(obj, type):
            setattr(obj, name, f)
        else:
            setattr(type(obj), name, f)
        return getattr(obj, name)

Rather than directly create the method via the dynamic function's __get__ descriptor method, I think it's better to store the function in the class dict and rely on getattr to return the proper bound/unbound method. Subsequent attribute access will use the function from the class. Since the same __getattr__ function is used for both the class and the instance, an isinstance check is required to ensure the dynamic function gets stored to the class and not the instance.

In Python 3, getting the function as an attribute of the class merely returns the function since unbound methods were removed from the language. Also, the metaclass syntax has changed to a keyword argument in the class definition line.

Test:

>>> g = Generator()
>>> g.foo
<bound method Generator.foo of <__main__.Generator object at 0xb7248c2c>>
>>> Generator.foo
<unbound method Generator.foo>
>>> g.bar
<bound method Generator.bar of <__main__.Generator object at 0xb7248c2c>>
>>> Generator.bar
<unbound method Generator.bar>
>>> g.foo()
'Result of foo for <__main__.Generator object at 0xb7248c2c>'
>>> Generator.foo(g)
'Result of foo for <__main__.Generator object at 0xb7248c2c>'
>>> 'foo' in vars(Generator), 'bar' in vars(Generator)
(True, True)
Sign up to request clarification or add additional context in comments.

Comments

2

__getattr__() only works on instances, you will need to make the function on a metaclass on Generator to get the behavior your want.

2 Comments

That only swaps which of print g.bar and print Generator.bar error. This still doesn't convert functions to bound/unbound methods
@Eric You would need to have the function in both places to have as it work in your final case and the other cases. I missed the change from bound to function there, but that's now been answered by thg435, I believe.
2

You have two problems.

  1. You only set the dynamic function on the instance g (not the class Generator), so Generator.bar is not defined.
  2. You don't wrap it in a MethodType, so you get a function instead of a method.

For 1, if you always call g.foo before you call Generator.foo, you can just add the line

setattr(self.__class__, name, f)

inside __getattr__, which will bind the name as a method on the class. Otherwise, you will need a custom __getattr__ on the type object, which means that you have to make it an instance of a custom class i.e. write your own metaclass.


For 2, see @thg435's answer. Note that this is icky because of backwards compatibility in Python 2, and has been neatened considerably in Py3k -- now what you are doing would basically work. It's because of the automatic injection of self.

1 Comment

Edit: I'm not sure what you mean regarding Python 3. Unbound methods were removed, but binding regular methods wasn't changed.
1

For the first case (g.bar), replace return f with return MethodType(f, self).

5 Comments

That outputs <bound method ?.bar of ... rather than <bound method Generator.bar of ... for some reason
MethodType(f, self, Generator), or f.__get__(self, Generator). Replace self with None to make an unbound method.
@eryksun: Any way I can write one __getattr__ in the metaclass and have it apply to both classes and instances?
@Eric: Yes, by overriding type.__new__ and manually setting the __getattr__ function in the dict. That way you'd only have to write it once. If clself is an instance of type, return an unbound method, else return a bound method.
@eryksun: Put those two comments as an answer, and I'll accept it - thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.