Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add method using metaclass

How do I add an instance method to a class using a metaclass (yes I do need to use a metaclass)? The following kind of works, but the func_name will still be "foo":

def bar(self):
    print "bar"

class MetaFoo(type):
    def __new__(cls, name, bases, dict):
        dict["foobar"] = bar
        return type(name, bases, dict)

class Foo(object):
    __metaclass__ = MetaFoo

>>> f = Foo()
>>> f.foobar()
bar
>>> f.foobar.func_name
'bar'

My problem is that some library code actually uses the func_name and later fails to find the 'bar' method of the Foo instance. I could do:

dict["foobar"] = types.FunctionType(bar.func_code, {}, "foobar")

There is also types.MethodType, but I need an instance that does'nt exist yet to use that. Am I missing someting here?

like image 380
Knut Eldhuset Avatar asked Sep 15 '08 18:09

Knut Eldhuset


People also ask

What is the use of metaclass in Python?

A metaclass in Python is a class of a class that defines how a class behaves. A class is itself an instance of a metaclass. A class in Python defines how the instance of the class will behave. In order to understand metaclasses well, one needs to have prior experience working with Python classes.

What is __ add __ in Python?

The __add__() method in Python specifies what happens when you call + on two objects. When you call obj1 + obj2, you are essentially calling obj1. __add__(obj2). For example, let's call + on two int objects: n1 = 10.

How do you add a method to a class in Python?

The normal way to add functionality (methods) to a class in Python is to define functions in the class body. There are many other ways to accomplish this that can be useful in different situations. The method can also be defined outside the scope of the class.

How do you set the metaclass of class A to B?

In order to set metaclass of a class, we use the __metaclass__ attribute. Metaclasses are used at the time the class is defined, so setting it explicitly after the class definition has no effect.


2 Answers

Try dynamically extending the bases that way you can take advantage of the mro and the methods are actual methods:

class Parent(object):
    def bar(self):
        print "bar"

class MetaFoo(type):
    def __new__(cls, name, bases, dict):
        return type(name, (Parent,) + bases, dict)

class Foo(object):
    __metaclass__ = MetaFoo

if __name__ == "__main__":
    f = Foo()
    f.bar()
    print f.bar.func_name
like image 110
Aaron Maenpaa Avatar answered Sep 28 '22 10:09

Aaron Maenpaa


I think what you want to do is this:

>>> class Foo():
...   def __init__(self, x):
...     self.x = x
... 
>>> def bar(self):
...   print 'bar:', self.x
... 
>>> bar.func_name = 'foobar'
>>> Foo.foobar = bar
>>> f = Foo(12)
>>> f.foobar()
bar: 12
>>> f.foobar.func_name
'foobar'

Now you are free to pass Foos to a library that expects Foo instances to have a method named foobar.

Unfortunately, (1) I don't know how to use metaclasses and (2) I'm not sure I read your question correctly, but I hope this helps.

Note that func_name is only assignable in Python 2.4 and higher.

like image 32
Nathan Shively-Sanders Avatar answered Sep 28 '22 10:09

Nathan Shively-Sanders