python 元类编程

__getattr__与__getattribute__魔法函数

__getattr__方法可用来检查一个类中是否有一个属性,比如:

class User():
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        print("not find attr")


def main():
    user = User("dog")
    user.age


if __name__ == '__main__':
    main()

上面代码中User类中是没有age属性的,而我们调用user.age时也不会报错,而是运行了__getattr__里的内容,所以为了反正使用者用了一个类中没有的属性,我们就可以使用__getattr__。

__getattribute__方法也是类似的,不过他运行在__getattr__之前:

class User():
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        print("not find attr")

    def __getattribute__(self, item):
        print("not find attribute")


def main():
    user = User("dog")
    user.age


if __name__ == '__main__':
    main()

如上述代码运行后,打印的就不是not find attr,而是not find attribute了。

属性描述符

如果User类中有多个属性都需要判断,那么就需要写多个方法,这些方法怎么复用呢?这个时候就要用到属性描述符

class IntField(object):
    """
    数据描述符
    """
    def __get__(self, instance, owner):
        print("get")
        return self.values

    def __set__(self, instance, value):
        print("set")
        if not isinstance(value, int):
            raise ValueError('Value Error')
        self.values = value

    def __delete__(self, instance):
        pass


class User:
    age = IntField()
    # age = 19
 

user = User()
user.age = 30


print(user.age)

属性描述符,只要实现了__get__,__set__,__delete__任何一个方法,就被称为属性描述符,在上述代码运行后,会依次打印出set get 30,,而如果讲age = 19 的注释去除,就不会打印出set get ,只会打印出30.

属性查找顺序

user = User(), 那么user.age 顺序如下:

1 如果"age"是出现在User或其基类的__dict__中, 且age是data descriptor,那么调用其__get__方法, 否则

2 如果"age"出现在user的__dict__中, 那么直接返回 obj.dict[‘age’],否则

3 如果"age"出现在User或其基类的__dict__中

3.1 如果age是non-data descriptor,那么调用其__get__方法, 否则

3.2 返回 dict[‘age’]

4 如果User有__getattr__方法,调用__getattr__方法,否则

5 抛出AttributeError

动态创建类

def create_class(name):
    if name == "user":
        class User:
            def __str__(self):
                return "user"
        return User
    elif name == "student":
        class Student:
            def __str__(self):
                return "Student"
        return Student


if __name__ == "__main__":
    myclass = create_class('user')
    obj = myclass()
    print(obj)
    print(type(obj))

在上述代码中,定义了一个create_class方法,该方法实现根据参数来创建一个类的功能,myclass = create_class(‘user’)相当于是创建了一个名字叫myclass的类,而这个类与create_class方法中定义的User类是一样的。obj = myclass()相当于实例化一个叫obj的myclass类。最后的打印结果为

user
<class '__main__.create_class.<locals>.User'>

使用type创建类

type除了查看一个对象的类型,还能用来创建一个类,像这种由类创建类的就叫元类:
使用方法为。type(类名,由父类组成的元组,包含属性的字典)
第一个参数:name表示类名称,字符串类型
第二个参数:bases表示继承对象(父类),元组类型,单元素使用逗号
第三个参数:attr表示属性,这里可以填写类属性、类方式、静态方法,采用字典格式,key为属性名,value为属性值

metaclass属性

如果一个类中定义了metaclass = xxx,Python就会用元类的方式来创建类

def upper_attr(class_name, class_parents, class_attr):
    newattr = {}
    for name, value in class_attr.items():
        if not name.startswith("_"):
            newattr[name.upper()] = value

    return type(class_name, class_parents, newattr)


class Foo(object, metaclass=upper_attr):
    # __metaclass__ = upper_attr
    name = 'dog'


f = Foo()
print(hasattr(Foo, 'name'))
print(hasattr(Foo, 'NAME'))

其中 class_name 对应的是Foo,class_parents对应的是object,class_对应的是个字典,字典的键为Foo里的属性,值为属性对应的值。if not name.startswith("_"):用来过滤掉class_attr中的类似与__module__这种带_的。
最后打印出来的结果为 False True。
讲Foo里的name属性全大写为了NAME。
需注意的是,在python2里metaclass的用法为代码中的注释部分__metaclass__ = upper_attr,python3中的用法为代码中的用法class Foo(object, metaclass=upper_attr):。

发布了24 篇原创文章 · 获赞 2 · 访问量 419

猜你喜欢

转载自blog.csdn.net/weixin_45735361/article/details/103981475
今日推荐