PythonI/O进阶学习笔记_7.python动态属性,__new__和__init__和元类编程(下)

content:

上:

1.property动态属性

2.__getattr__和__setattr__的区别和在属性查找中的作用

3.属性描述符 和属性查找过程

4.__new__和__init__的区别

下:

5.什么是元类和自定义元类

6.用元类实现orm

=====================

五.什么是元类和自定义元类

1.什么是元类

在理解元类的时候,需要具备对python中类和实例的创建的概念有所了解,需要复习该系列的3中的类和对象那一章。了解类的创建、实例的创建、type和父类的继承顺序等。

在类的相关章节中也提到了,元类就是类的类。也就是元类就是负责创建类的一种东西。可以理解为,元类就是负责生成类的。而 type 就是内建的元类。也就是 Python 自带的元类。

 

2.如何自定义使用元类

a.模拟python中动态创建类

既然我们知道python中一切皆对象,类也是,所以类也是可以被创建的,那么我们自己是新一个传入类名,并且动态生成类的方法:

传入类名,在create_class中返回同名字的类名。

 

b.用type来更加灵活的创建类

但是实际上在a中创建类还是比较蠢的。要在方法中去一个个判断名字。

-  用type来动态创建类:

直接输入类名,就可以返回一个类。后面的参数要求:必须传递第一个参数,为类。并且传递进去的函数不加括号,为直接的函数对象。

这样的类就像是class User: pass 没有自定义的属性和方法。

 

-  创建传递类的属性和方法:

创建类的属性:
 
传递类的方法:先创建一个函数,函数必须传递一个参数,为类。并且传递进去的函数不加括号,为直接的函数对象。

 

- 继承父类

注意父类传递的那个参数类型为tuple。而且要加逗号,否则报错。

 

3.metaclass

a.使用元类
这个metaclass可以控制User2的形成。那么为什么可以控制类的形成呢?
 
b.元类的创建顺序
创建类的时候,会去找metaclass这个属性。最后都找不到metaclass的时候,才会调用type去创建类。
 
c.什么时候用元类
将创建user的new过程,托给元类。而不需要自己来做。

 

六. 实现简易django的orm

1.想要实现的效果

什么是orm?

即Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。

具体的orm和django这种web框架的orm的设计自行查找资料哦~

我们想要实现类似于django中建立表对应的类的方法:

所以需要实现的有:
CharField类  IntField类  并且能实现save来insert。能把User类中定义的属性进行对应,Meta里的属性能被特定处理。

 

2.整个实现步骤

a.首先 IntField需要进行初始化、实现对参数进行检查。让InterField称为int属性描述符。同理,Charfield也是。

class InterField:
    def __init__(self,value,min_length=None,max_length=None):
        self._value=value
        self._min_value=min_length
        self._max_value=max_length
        if self._min_value is not None:
            if not isinstance(self._min_value,numbers.Integral):
                raise ValueError("Value Must be Intergral")
            elif self._min_value < 0:
                raise ValueError("Value Must large than 0")


        if self._max_value is not None:
            if not isinstance(self._max_value,numbers.Integral):
                raise ValueError("Value must be Intergral")
            elif self._max_value <0 :
                raise  ValueError("Value must large than 0")
        if self.max_length < min_length:
            raise ValueError("Max must large than min ")

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

    def __set__(self, instance, value):
        if not isinstance(value,numbers.Integral):
            raise ValueError("Not intergral")
        self._value=value
class CharField:
    def __init__(self,db_column,value,max_length=None):
        self._value=value
        self.db_column=db_column
        self._max_length=max_length
        if not max_length:
            raise ValueError("You must special the charfield length")

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

    def __set__(self, instance, value):
        self._value=value
        if len(value) > self._max_length:
            raise ValueError("Value's length must less than max length")

 

b.定义元类来设置特定的属性和方法

重点是,创建一个元类,来指定创建User的时候需要设定的很多属性,例如meta和save。在User类中是没有说明这些属性如何去设定、限制和用法的。
而我们需要在创建User的时候,注册进很多自己的属性到User里来。
比如django中,生成一个django中的表的时候,meta默认有很多属性是被设定了的,才会有我们建立一个表的类,只要设定key,value,django就自动帮忙把对应的属性和功能创建好了。
django中的大致设定:
class ModelBase(type):

"""Metaclass for all models."""
 def __new__(cls, name, bases, attrs, **kwargs):
    super_new = super().__new__

    # Also ensure initialization is only performed for subclasses of Model
    # (excluding Model class itself).
    parents = [b for b in bases if isinstance(b, ModelBase)]
    if not parents:
        return super_new(cls, name, bases, attrs)

    # Create the class.
    module = attrs.pop('__module__')
    new_attrs = {'__module__': module}
    classcell = attrs.pop('__classcell__', None)
    if classcell is not None:
        new_attrs['__classcell__'] = classcell
        attr_meta = attrs.pop('Meta', None)
    # Pass all attrs without a (Django-specific) contribute_to_class()
    # method to type.__new__() so that they're properly initialized
    # (i.e. __set_name__()).
    contributable_attrs = {}
    for obj_name, obj in list(attrs.items()):
        if _has_contribute_to_class(obj):
            contributable_attrs[obj_name] = obj
        else:
            new_attrs[obj_name] = obj
    new_class = super_new(cls, name, bases, new_attrs, **kwargs)

    abstract = getattr(attr_meta, 'abstract', False)
    meta = attr_meta or getattr(new_class, 'Meta', None)
    base_meta = getattr(new_class, '_meta', None)

  app_label = None

# Look for an application configuration to attach the model to.
  app_config = apps.get_containing_app_config(module)

  if getattr(meta, 'app_label', None) is None:
   if app_config is None:
      if not abstract:
      raise RuntimeError(
        "Model class %s.%s doesn't declare an explicit "
        "app_label and isn't in an application in "
        "INSTALLED_APPS." % (module, name)
        )

    else:
      app_label = app_config.label
......
我们模拟User直接设置它的元类为 modelMetaClass。并且再元类中实现__new__来对传进去的参数做处理。
首先用元类的__new__来打印传递到__new__中的参数。
可以看见args的列传进去的基本就是name(类名)、bases、attrs(属性dict)。将参数用这三个变量代替。与django中设置的变量一致。
对传递进去的attrs进行操作,如果是在User内定义的charfield、interfield这种类型的字段,直接用__new__生成对应的key、value。
但是对User类中定义的meta这类属性,对里面的一些属性的定义就需要做对应的处理。
又因为我们想把InterField、CharField这种属性描述符作为key,value存储,meta属性里的一些字段做特定处理,所以我们让各种属性描述符类继承同一个Field来判断是否都是Field类来做统一key value处理。
在User继承了改动过的元类之后。再User类中定义init,就能再pycharm中看到User中的变量。
class Field:
    pass
class ModelMetaClass(type):
    def __new__(cls, name, bases, attrs, **kwargs):
        fields=dict()
        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
        attrs["db_table"]=db_table
        attrs["_meta"]=attrs_meta
        attrs["fields"]=fields
        del fields["Meta"]
        return super().__new__(cls,name, bases, attrs,**kwargs)

debug之后的效果:

 

c.定义User类的父类

但是在我们实际应用的时候,不可能每个User去定义__init__函数,save函数这样,所以我们就用一个modle父类,让User这些类来继承。
需要注意的是:
如果定义一个父类BaseModel,需要在BaseModel继承的元类中判断如果传进来的name是BaseModel,就直接__new__就可以了。因为在BaseModel中,是没有定义fields这些东西的。
并且在父类中,定义好save方法。
最后完整的代码为:
import numbers
class Field:
    pass
class InterField(Field):
    def __init__(self,db_column=None,min_value=None,max_value=None):
        self._min_value=min_value
        self._max_value=max_value
        self.db_column=db_column
        if self._min_value is not None:
            if not isinstance(self._min_value,numbers.Integral):
                raise ValueError("Value Must be Intergral")
            elif self._min_value < 0:
                raise ValueError("Value Must large than 0")


        if self._max_value is not None:
            if not isinstance(self._max_value,numbers.Integral):
                raise ValueError("Value must be Intergral")
            elif self._max_value <0 :
                raise  ValueError("Value must large than 0")
        if max_value < min_value:
            raise ValueError("Max must large than min ")

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

    def __set__(self, instance, value):
        if not isinstance(value,numbers.Integral):
            raise ValueError("Not intergral")
        self._value=value

class CharField(Field):
    def __init__(self,db_column=None,max_length=None):
        self.db_column=db_column
        self._max_length=max_length
        if not max_length:
            raise ValueError("You must special the charfield length")

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


    def __set__(self, instance, value):
        self._value=value
        if len(value) > self._max_length:
            raise ValueError("Value's length must less than max length")

class ModelMetaClass(type):
    def __new__(cls, name, bases, attrs, **kwargs):
        if name=="BaseModel":
            super().__new__(cls,name,bases,attrs,**kwargs)

        fields=dict()
        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
        attrs["db_table"]=db_table
        attrs["_meta"]=attrs_meta
        attrs["fields"]=fields
        if attrs_meta:
            del attrs["Meta"]
        return super().__new__(cls,name, bases, attrs,**kwargs)

class ModelBase(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(ModelBase):
    name=CharField(max_length=10)
    age=InterField(min_value=0,max_value=100)

    class Meta:
        db_table="user"

    def _test(self):
        print("_test:",self.age)


if __name__=="__main__":
    user=User()
    user.name="bobby"
    user.age=23
    user._test()
    user.save()

 

 

 

 

posted @ 2019-12-02 16:38  besttr1225  阅读(407)  评论(0编辑  收藏  举报