如何使用元类拦截类创建和添加属性?

时间:2017-08-31 20:43:42

标签: python

在以下代码中,我希望元类NameMeta将属性gender添加到MyName类,以防该类未声明该属性。

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)

这是我得到的输出:

<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'

我知道这个错误是有道理的,因为name是一个字符串。 我的问题是,如何在元类中访问MyName类作为对象,以便我可以添加属性?

2 个答案:

答案 0 :(得分:3)

你很亲密。您的问题是您尝试使用name(一个字符串)将您的属性添加到元类的名称。您需要将属性分配给您正在创建的类对象。这可以使用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)

通过上述更改,您的代码输出:

Male

答案 1 :(得分:2)

如果它不存在,您可以将它添加到dic,因为它包含类的属性:

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

或者你可以有一个类属性:

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