Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bind a method to class during its metaclass's __init__ method?

Tags:

python

Final Edit

My problem was that I was trying to bind a partial to a class. Not sure if I should delete this question or, change its title.

Original Question

I know that the "correct" way to bind methods to a class inside its metaclass is to add it to the classdict argument in the metaclass's __new__ method, and that in general you can bind an instance method to a class by simply assigning it to the class.

However, in my use case, I need to bind a method to a class during the metaclass's __init__ method. The problem is that the usual method of assignment does not work. The function is assigned to the class and it can be access via ClassName.function_name or class_instance.function_name, but it is not bound to the class, and the first argument is not set to self.

I guess that this is due to the class not being fully initialized during the metaclass __init__, but is there any way around this problem?

EDIT: Code Example

Given:

def fun(self):
    pass


class MetaClass(type):
    def __init__(cls, name, bases, dct):
        pass

class Class(object):
    __metaclass__ = MetaClass

I want to bind fun to Class inside MetaClass.__init__, so that calling

instance = Class()
instance.fun()

will pass instance to fun as its first argument.

Edit The II

So, apparently the problem arose because I was trying to bind a partial. Not sure why that would cause an issue.

Will make sure later, and update/remove this question if necessary.

like image 910
Roman Levin Avatar asked Feb 27 '14 15:02

Roman Levin


People also ask

What does __ call __ do in Python?

The __call__ method enables Python programmers to write classes where the instances behave like functions and can be called like a function. When the instance is called as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x. __call__(arg1, arg2, ...) .

What is bound method in Django?

A bound method is the one which is dependent on the instance of the class as the first argument. It passes the instance as the first argument which is used to access the variables and functions.

What is __ new __ in Python?

The __new__() is a static method of the object class. It has the following signature: object.__new__(class, *args, **kwargs) Code language: Python (python) The first argument of the __new__ method is the class of the new object that you want to create.

What is__ bases__ in Python?

The __bases__ property of the class contains a list of all the base classes that the given class inherits. The above output shows that the Human class has object as a base class. We can also look at the attributes and methods defined by the object class using the dir function.


1 Answers

There is little difference between metaclass.__new__() and metaclass.__init__(); they both deal with the class object.

__new__ produces the new object, __init__ then gets to alter it still. The chain of events is basically:

new_class_obj = YourMeta.__new__(YourMeta, name, bases, bodydict)
YourMeta.__init__(new_class_obj, name, bases, bodydict)

When it comes to adding functions on the new class object (through the bodydict or directly by assignment on the class object) there is no difference at all.

If you are having problems setting functions on the class object, then you need to make sure you are setting actual function objects on that object.

If you are using bound methods for example, you need to unwrap the method and extract just the function:

new_class_obj.name = method_obj.__func__

If you are adding regular function objects on the class, then they'll be bound when accessed on the instance, no work required on your part.

Bottom line is that you need to add something that'll act a descriptor, specifically a descriptor that'll return a method object from the descriptor.__get__ method. This is what plain function objects do.

A functools.partial() object is not such a descriptor. Easiest would be to wrap the partial in a function (a lambda will do here):

new_class_obj.name = lambda *args, **kw: partial_obj(*args, **kw)
like image 191
Martijn Pieters Avatar answered Nov 14 '22 23:11

Martijn Pieters