博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python 元类编程
阅读量:3707 次
发布时间:2019-05-21

本文共 3480 字,大约阅读时间需要 11 分钟。

property动态属性

通过使用property可以将方法像属性一样获取值。使用setter对方法进行赋值操作

from datetime import datetime, dateclass Student:    def __init__(self, name, birthday):        self.name = name        self.birthday = birthday        self._age = 0        @property    def age(self):        return datetime.now().year - self.birthday.year        @age.setter    def age(self, value):        self._age = value    stu = Student("zhangsan", date(year=1995, month=3, day=7))stu.age = 4print(stu.age)output: 24

getattributegetattr

__getattr__在类中找不到属性时,调用该函数。

__getattribute__首先调用该函数,然后找属性.

__getattribute__中抛出AttributeError时,会调用__getattr__

调用顺序

__getattribute__ > __getattr__

属性描述符

在类中只要实现了__get____set____delete__方法中的一个就认为是描述符.

只实现了__get__的对象是非数据描述符. 只读
实现了__get____set__的对象是数据描述符. 可读可写.

class IntField:    def __get__(self):        pass            def __set__(self):        pass            def __delete__(self):        pass

属性描述符查找过程

属性描述符发生的过程在__getattribute__中.

如果age是属性描述符,则调用IntField中的__get__获得属性值,如果获取失败,则调用__dict__获取值。如果age不是属性描述符,则直接获取__dict__对应的值。

import numbersclass IntField:    # 数据描述符    def __get__(self, instance, owner):        return self.value            def __set__(self, instance, value):        if not isinstance(value, numbers.Integral):            raise ValueError("int value need")        if value < 0:            raise ValueError("positive value need")        self.value = value            def __delete__(self, instance):        pass    class NonDataIntField:    # 非数据属性描述符    def __get__(self, instance, owner):        return "NonDataIntField = {}".format(self.value)class User:    age = IntField()    #age = NonDataIntField()user = User()user.name = "zhangsan"    # name不是属性描述符,所以直接加入到`__dict__`中print(user.__dict__)user.age = 12               # age是属性描述符, 调用`IntField`中的`__set__`方法, 而不会加入到`__dict__`中print(user.__dict__)print(user.age)              # 调用`IntField`中的`__get__`方法

元类

元类是创建类的类.

type -> class -> 对象

所有的类都是通过type实例化得到的。

通过type创建class

使用type创建User类,该类继承Base类,并且有test方法和name属性

class Base:    def __init__(self, *args, **kwargs):        print("Base __init__")        super().__init__(*args, **kwargs)def test(self):    print("test = {}".format(self.name)) User = type("User", (Base,), {
"test": test, "name": "zhangsan"})user = User()user.test()

自定义元类

自定义元类需要通过继承type实现

如果父类有metaclass,则子类和父类的创建都需要通过该元类实例化得到。

class BaseMeta(type):    def __new__(cls, name, bases, args, **kwargs):        # print(name)        # print(bases)        # print(args)        print("BaseMeta __new__..")                if name == "Base":            return super().__new__(cls, name, bases, args, **kwargs)                    meta = args['Meta']        name = getattr(meta, "name")    # 获取到A中meta的name的值. django的orm也是这样实现的        print(name)                return super().__new__(cls, name, bases, args, **kwargs)class Base(metaclass=BaseMeta):    def __new__(cls, *args, **kwargs):        print("Base __new__..")        return super().__new__(cls, *args, **kwargs)            def __init__(self, *args, **kwargs):        print("Base init..")        super().__init__(*args, **kwargs)    class A(Base):    def __new__(cls, *args, **kwargs):        print("A __new__..")        return super().__new__(cls, *args, **kwargs)            def __init__(self, *args, **kwargs):        print("A init..")        super().__init__(*args, **kwargs)            class Meta:        name = "zhangsan"# A和Base都会通过BaseMeta创建,所以会调用两次__new__创建实例output:BaseMeta __new__..BaseMeta __new__..zhangsan

元类的经典例子是django ORM

转载地址:http://wakjn.baihongyu.com/

你可能感兴趣的文章
L3-002 特殊堆栈 (30分)
查看>>
L3-005 垃圾箱分布 (30分)
查看>>
L3-007 天梯地图 (30分)
查看>>
试题 E: 矩阵
查看>>
2020年10月蓝桥杯E题七段管
查看>>
洛谷P1220 关路灯
查看>>
黄金分割比和斐波那契数列
查看>>
状压dp
查看>>
智能家居协议设计
查看>>
数值分析实验教学系统开发
查看>>
HTML5自学笔记上
查看>>
HTML5自学笔记下
查看>>
CSS3各种类型的选择器总结
查看>>
HTML+CSS+JS雷霆战机
查看>>
IDEA 修改自动生成实体类的类型对应
查看>>
SpringMVC的web层正常启动,但无法找到了Map路径
查看>>
leecode刷题-20200528-easy-110.平衡二叉树
查看>>
uniapp开发:“this.$refs.xxxx“调用子组件无效的可能原因
查看>>
IEDA2019.1.4 鼠标放到方法上显示注解
查看>>
Springboot整合SpringSecurity之后出现跨域请求
查看>>