9

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?

2 Answers 2

7

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
Sign up to request clarification or add additional context in comments.

6 Comments

Worked. Thank you very much for the quick response. I have to say that I honestly thought that cls reffered to the class it is in in this case NameMeta.
@VincentJ.Michuki I've update my answer. As Artyer so kindly pointed out, you need to use: dic['gender'] = 'Male'.
You could use dic.setdefault('gender', 'Male') instead of the "if not present then insert". Probably not faster but "shorter". :)
Well you could, but you should you?. dict.setdefault returns a value, so it'd seem to me that you'd really only be using it for it's side-effects. But yeah, it'd be shorter ;-)
Never figured out if it's a side-effect or if it was the main purpose (look at the name) and just too convenient to let that function return. It's one of the odd methods that modify and return... But I know it's avoiding hashing the value twice and you don't need a try and except. :D
|
4

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.