Python 进阶

第八章 元类编程

  • 元类编程用的不多,主要是了解元类编程方便阅读源码。元类是创建类的类,类默认由type创建,但是我们可以通过metaClass参数指定元类,但是元类必须继承type。

8.1 property动态属性

  • property一般搭配setter方法使用,类似于Java里面的setter和getter,一般用于访问属性时需要对属性做一些操作,或者间接访问对象的私有属性。
    下面展示一些 内联代码片
class User:
    def __init__(self, name, age):
        self.name = name
        self.__age = age # 定义为私有属性
    @property
    def age(self):
        return self.__age
    @age.setter
    def age(self, value):
        self.__age = value
if __name__ == "__main__":
    user = User("bobby", 18)
    user.age = 30
    print(user.age)    
    print(user._User__age) # 可以看到虽然无法利用user.__age取出私有属性,但可以通过user._User__age访问私有属性,所以Python中的私有属性也是不安全的。
    # 30
    # 30

8.2 getattr、__getattribute__魔法函数

  • 这里再次用到了Python中的备用机制,当__getattribute__魔法方法存在时,优先执行该方法,如果该方法不存在,则执行__getattr__方法,当__getattribute__存在时,即便对象中不存在这个属性也不会报错。
class User:
    def __init__(self,info={}):
        self.info = info
    def __getattr__(self, item): # 备用
        return self.info[item]
    def __getattribute__(self, item): # 优先执行
        return "bobby"
if __name__ == "__main__":
    user = User(info={"company_name":"imooc", "name":"bobby"})
    user.test
    # bobby

8.3 属性描述符和属性查找过程

  • 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
from datetime import date, datetime
import numbers
class 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 self.value
class User:
    age = IntField()
    # age = NonDataIntField()
class User:
    def __init__(self, name, email, birthday):
        self.name = name
        self.email = email
        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
if __name__ == "__main__":
    user = User("bobby", 1,date(year=1987, month=1, day=1))
    user.__dict__["age"] = "abc"
    print(user.__dict__)
    print(user.age)
    print(getattr(user, 'age'))
    user = User("bobby", 1,date(year=1987, month=1, day=1))
    user.age = 30
    print(user._age)
    print(user.age)
    # {'name': 'bobby', 'email': 1, 'birthday': datetime.date(1987, 1, 1), '_age': 0, 'age': 'abc'}
    # 34
    # 34
    # 30
    # 34

8.4 __new__和__init__的区别

  • __new__负责创建对象,__init__负责对象的实例化。
class User:
    def __new__(cls, *args, **kwargs):
        print (" in new ")
        return super().__new__(cls)
    def __init__(self, name):
        print (" in init")
        pass
a = int()
#new 是用来控制对象的生成过程, 在对象生成之前
#init是用来完善对象的
#如果new方法不返回对象, 则不会调用init函数
if __name__ == "__main__":
    user = User(name="bobby")

8.5 自定义元类

# type动态创建类
User = type("User", (), {})
class MetaClass(type):
    def __new__(cls, *args, **kwargs):
        print("in MetaClass") # 预编译就会创建类
        return super().__new__(cls, *args, **kwargs)
from collections.abc import *
#什么是元类, 元类是创建类的类 对象<-class(对象)<-type
class User(metaclass=MetaClass):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return "user"
#python中类的实例化过程,会首先寻找metaclass,通过metaclass去创建user类
#去创建类对象,实例

8.6 元类实现简单的orm

# 需求
import numbers
class Field: # 属性描述符的基类
    pass
class IntField(Field):
    # 数据描述符
    def __init__(self, db_column, min_value=None, max_value=None):
        self._value = None
        self.min_value = min_value
        self.max_value = max_value
        self.db_column = db_column
        if min_value is not None:
            if not isinstance(min_value, numbers.Integral):
                raise ValueError("min_value must be int")
            elif min_value < 0:
                raise ValueError("min_value must be positive int")
        if max_value is not None:
            if not isinstance(max_value, numbers.Integral):
                raise ValueError("max_value must be int")
            elif max_value < 0:
                raise ValueError("max_value must be positive int")
        if min_value is not None and max_value is not None:
            if min_value > max_value:
                raise ValueError("min_value must be smaller than max_value")
    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 < self.min_value or value > self.max_value:
            raise ValueError("value must between min_value and max_value")
        self._value = value
class CharField(Field):
    def __init__(self, db_column, max_length=None):
        self._value = None
        self.db_column = db_column
        if max_length is None:
            raise ValueError("you must spcify max_lenth for charfiled")
        self.max_length = max_length

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError("string value need")
        if len(value) > self.max_length:
            raise ValueError("value len excess len of max_length")
        self._value = value
class ModelMetaClass(type):
    def __new__(cls, name, bases, attrs, **kwargs):
        if name == "BaseModel":
            return super().__new__(cls, name, bases, attrs, **kwargs)
        fields = {}
        for key, value in attrs.items():
            if isinstance(value, Field):
                fields[key] = value
        attrs_meta = attrs.get("Meta", None)
        _meta = {}
        db_table = name.lower()
        if attrs_meta is not None:
            table = getattr(attrs_meta, "db_table", None)
            if table is not None:
                db_table = table
        _meta["db_table"] = db_table
        attrs["_meta"] = _meta
        attrs["fields"] = fields
        del attrs["Meta"]
        return super().__new__(cls, name, bases, attrs, **kwargs)
class BaseModel(metaclass=ModelMetaClass):
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        return super().__init__()
    def save(self):
        fields = []
        values = []
        for key, value in self.fields.items():
            db_column = value.db_column
            if db_column is None:
                db_column = key.lower()
            fields.append(db_column)
            value = getattr(self, key)
            values.append(str(value))
        sql = "insert {db_table}({fields}) value({values})".format(db_table=self._meta["db_table"],                                                                   fields=",".join(fields), values=",".join(values))
        pass
class User(BaseModel):
    name = CharField(db_column="name", max_length=10)
    age = IntField(db_column="age", min_value=1, max_value=100)

    class Meta:
        db_table = "user"
if __name__ == "__main__":
    user = User(name="bobby", age=28)
    user.name = "bobby"
    user.age = 28
    user.save()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值