Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to intercept class creation and add attribute using a metaclass?

Tags:

python

In the following code, I want metaclass NameMeta to add attribute gender to MyName class in case this class does not declare that attribute.

class NameMeta(type):
    def __new__(cls, name, bases, dic):
        if 'gender' not in dic:
            setattr(name, 'gender', 'Male')
        return super().__new__(cls, name, bases, dic)

class MyName(metaclass=NameMeta):
    def __init__(self, fname, lname):
        self.fname = fname
        self.lname = lname       
    def fullname(self):
        self.full_name = self.fname + self.lname
        return self.full_name 
inst = MyName('Joseph ', 'Vincent')
print(MyName.gender)

This is the output that I am getting:

<ipython-input-111-550ff3cfae41> in __new__(cls, name, bases, dic)
      2     def __new__(cls, name, bases, dic):
      3         if 'gender' not in dic:
----> 4             setattr(name, 'gender', 'Male')
      5         return super().__new__(cls, name, bases, dic)
      6 

AttributeError: 'str' object has no attribute 'gender'

I know this error makes sense since name is a string. My question is, how can I access MyName class as an object in the metaclass so that I can add the attribute?

like image 866
Vincent J. Michuki Avatar asked Aug 31 '17 20:08

Vincent J. Michuki


People also ask

What is __ 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 metaclass in OOP?

In object-oriented programming, a metaclass is a class whose instances are classes. Just as an ordinary class defines the behavior of certain objects, a metaclass defines the behavior of certain classes and their instances. Not all object-oriented programming languages support metaclasses.

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.

What is the metaclass of type?

type is a metaclass, of which classes are instances. Just as an ordinary object is an instance of a class, any new-style class in Python, and thus any class in Python 3, is an instance of the type metaclass.


Video Answer


2 Answers

You were close. Your problem is that you were trying to add your attribute to the name of the meta-class using name, which is a string. You need to assign the attribute to the class object you're creating. This can be done using dic:

class NameMeta(type):
    def __new__(cls, name, bases, dic):
        if 'gender' not in dic:
            dic['gender'] = 'Male'
        return super().__new__(cls, name, bases, dic)

With the above change your code outputs:

Male
like image 83
Christian Dean Avatar answered Sep 27 '22 19:09

Christian Dean


You can just add it to the dic if it is not present, as it holds the class's attribute:

def __new__(mcs, name, bases, dict):
    if 'gender' not in dict:
        dict['gender'] = 'Male'
    # or just `dict.setdefault('gender', 'Male')`
    return super().__new__(mcs, name, bases, dic)

    # Or you can create the class and set it

    cls = super().__new__(mcs, name, bases, dic)
    if not hasattr(cls, 'gender'):
        cls.gender = 'Male'
    return cls

Or you could have a class attribute:

class NameMeta(type):
    gender = 'Male'
    # `gender = 'Male'` will be inherited by all classes
    # but not instances of those classes.
like image 27
Artyer Avatar answered Sep 27 '22 20:09

Artyer