How to intercept class creation and add attribute using metaclass?

In the following code, I want the metaclass NameMeta add a gender attribute to the MyName class if this class does not declare this 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 result I get:

 <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 the MyName class as an object in a metaclass to add an attribute?

+5
source share
2 answers

You were close. Your problem is that you tried to add your attribute to the name of the metaclass using name , which is a string. You need to assign an attribute to the class object you are 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, the code outputs:

 Male 
+3
source

You can simply add it to dic if it is not, as it contains a class 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 may have a class attribute:

 class NameMeta(type): gender = 'Male' # `gender = 'Male'` will be inherited by all classes # but not instances of those classes. 
+2
source

Source: https://habr.com/ru/post/1271402/


All Articles