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?
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.
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.
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.
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With